I am writing a basic log function so that I can print debug output to a file (and perhaps more-so, to practice writing a function in vim). Here is where I am at right now:

let g:log_level_map = {'DEBUG': 10, 'INFO': 20, 'WARNING': 30, 'ERROR': 40}
let g:log_level     = "DEBUG"

func LogOutput(msg, level='DEBUG')

    redir >> log.vim

    " Make sure its above the log_level
    if g:log_level_map[a:level] < g:log_level_map[g:log_level]

    " _LogFormat: [TIME] LEVEL - FILE - FUNC - LINE : MSG
    let time = strftime('%c')
    let level = a:level
    let file = expand('%:p:t')
    let func = '' " how to get caller function?
    let line = '' " how to get caller line no?
    let msg = a:msg

    " Print the output
    echom(printf("[%s] %s - %s - %s- L%s : %s",
               \ time, level, file, func, line, msg))

    redir END


It works great, but I'm having difficult figuring out how to get the line and function (if it exists) of the line of code that called the LogOutput function. Is it possible to access something like a call stack here, or how can this be done?


That's quite complex to do. At this moment, vim offers no native way to do this correctly.

I provide an API as good/correct as vim let me wrote, that I document here.

There are several steps and issues to be aware of.

  1. First to know the current line number, we have to throw a dummy exception, and catch it to decode its v:throwpoint. And this will incur an important slow down of the execution if called often.

  2. Then there are several problems

    • v:throwpoint is localized -- its exact value will differ from locale to locale (English, French, German...): there is a little localized ", line %d" (IIRC) at the end.
    • While on *nix we can use gettext through bash to parse a localised message, Windows required other workarounds (like temporarily forcing the locale to C)
    • script local functions have mangled names
    • anonymous functions can be found thanks to :verbose, but again, the message will be localized (and knows small differences between Vim versions). Anonymous functions may also be discarded if the dictionary they belong to is garbage-collected. This is the most annoying issue with these function. Hence: my one of my recommendation regarding how to do some OO programming in vimscript language.
    • autoloaded functions contain # which may not belong to 'isk' which may cause regex mismatch -- at least I had a few issues with this because I try to avoid parsing the result of :verbose function ..... if I can.

In the end, I use my API for 4 things at this date:

  • provide a logging framework
  • provide a unit testing framework for vimscript language
  • provide a DbC framework
  • decode error messages

(See the link to the API for more regarding my use cases)

| improve this answer | |
  • wow, that's really neat. Could you show a very basic example of manually throwing an error and then capturing it (without parsing the result, maybe just 2-4 lines of code I suppose)? – David542 Jun 13 at 23:34
  • Without the locale issue, and throwing the dummy exception, the decoding is close to 70lines; and this is the really important part. Throwing the dummy exception, is just that (it's in the documentation of the API), and just a few lines. – Luc Hermitte Jun 13 at 23:45
  • I see. So I've been able to capture v:throwpoint, but it's just telling me the function of the try/catch -- function LogOutput, line 3 -- how do I grab the parent function/caller? – David542 Jun 14 at 0:05
  • The simple answer: exe "verbose function ".funcname". For a little bit more resilient answer, see previous link. Note: your use case is with a call depth of 1 -- only one function. – Luc Hermitte Jun 14 at 0:09
  • is verbose function in the above "LogOutput" or its a wrapper function that calls it? – David542 Jun 14 at 0:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.