I was wondering how to properly implement logging for a function in a julia package.
My problem looks something like this: I have an algorithm with iter iterations and I want to provide the user with information about the current state, so naturally I’d use the @info macro like this:
function algorithm(iter = 1000)
@info "starting algorithm..."
for i in 1:iter
# do something
@info "current iteration: $i"
end
@info "finished algorithm..."
end
This works fine, but the algorithm is quite fast so the calls to @info take up a lot of time compared to the “raw” runtime.
In the real example the algorithm runs in about 60 microseconds without logging and takes 4 milliseconds with logging.
For this reason I’d like to make logging optional.
For now I settled on an approach using a verbose keyword argument like this:
function algorithm(iter = 1000; verbose = false)
verbose && @info "starting algorithm..."
for i in 1:iter
# do something
verbose && @info "current iteration: $i"
end
verbose && @info "finished algorithm..."
end
However I don’t particularly like the fact that this kind of disables logging independently of the minimum global log level. As a user I would expect to get @info statements if my log level is set accordingly.
Additionally this leads to code where users always have to specify keyword arguments if they like verbose output:
Yes I did originally, but the official documentation states: Logging.Debug (log level -1000) is information intended for the developer of the program. These events are disabled by default.
Since the information is not intended for the developer but the end user I figured @debug is not a very good fit in this case.
The @logmsg level "..." syntax provided by @goerz looks quite nice. Will this have any performance impact over verbose && @info "..."?
To avoid having to specify verbose for every function call I also looked at what the Turing.jl folk did with their progress logging. They have an option to set the progress logging at a global and function level using this pattern:
const verbosity = Ref(false) # default value
setverbosity!(x::Bool) = verbosity[] = x
function algorithm(args...; verbose = verbosity[])
# do something
level = verbose ? Logging.Info : Logging.Debug
@logmsg level "..."
end
This seems like quite a nice pattern to me since it allows the user to conveniently enable verbosity globally while still logging at the “correct” level.