I want to have a custom logger that will log certain local variables if available. I canβt seem to make the logger see the local variables.
Normally I could do:
@info gs_tester
to get an output like
Info: gs_tester="MYGSTESTERvalue"
I however, want to just log like this:
function some_worker(gs_tester)
@info "blahblah"
end
with_logger(logger_that_logs_gs_tester) do
some_worker(my_gs_tester)
end
And I would expect the logging output to be:
Info: [MYGSTESTERvalue] blahblah
Or, if gs_tester is undeclared:
Info: [?] blahblah
To this end I have written a logger:
logger_that_logs_gs_tester(logger) = TransformerLogger(logger) do log
try
merge(log, (; message = "[$gs_tester] $(log.message)"))
catch e
merge(log, (; message = "[?] $(log.message)"))
end
end
However, the gs_tester local variable context is not available. I only get gs_tester if I declare it before the βwith_loggerβ statement which makes this a lot less useful.
How could I go about having a custom logger that will automatically prefix my log message with elements of context if available?
I am making extensive use of LoggingExtras.jl but Iβll settle for a working example in any manner.
I donβt think this is possible at all in Julia.
Like you can use @locals
to get local variables.
But I donβt think you can get the local variables of any other scope.
And the dictionary you get from using @locals
is not updated when new ones are defined.
This is kinda by design.
It is one of the things that makes julia faster than python.
You could define your own logging macro which does this though.
Something like (untested)
macro smart_info(outer_msg)
quote
msg = $(esc(outer_msg))
if isdefined(gs_tester)
@info "[gs_tester=$gs_tester] $msg"
else
@info "$msg"
end
end
end
Which you would use as
@smart_info "hi"
1 Like
Seems like the gs_tester var is still not available using smart_info macro. Am I missing something obvious? I simplified the example and ran it:
julia> macro smart_info(outer_msg)
quote
@info "$gs_tester"
end
end
@smart_info (macro with 1 method)
julia> function testsmartinfo()
gs_tester=1
@smart_info "did this work?"
end
testsmartinfo (generic function with 1 method)
julia> testsmartinfo()
β Error: Exception while generating log record in module Main at REPL[22]:3
β exception =
β UndefVarError: gs_tester not defined
β Stacktrace:
β [1] macro expansion
β @ ./logging.jl:340 [inlined]
β [2] macro expansion
β @ ./REPL[22]:3 [inlined]
β [3] testsmartinfo()
β @ Main ./REPL[23]:3
β [4] top-level scope
β @ REPL[24]:1
β [5] eval
β @ ./boot.jl:360 [inlined]
β [6] eval_user_input(ast::Any, backend::REPL.REPLBackend)
β @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:139
β [7] repl_backend_loop(backend::REPL.REPLBackend)
β @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:200
β [8] start_repl_backend(backend::REPL.REPLBackend, consumer::Any)
β @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:185
β [9] run_repl(repl::REPL.AbstractREPL, consumer::Any; backend_on_current_task::Bool)
β @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:317
β [10] run_repl(repl::REPL.AbstractREPL, consumer::Any)
β @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:305
β [11] (::Base.var"#874#876"{Bool, Bool, Bool})(REPL::Module)
β @ Base ./client.jl:387
β [12] #invokelatest#2
β @ ./essentials.jl:708 [inlined]
β [13] invokelatest
β @ ./essentials.jl:706 [inlined]
β [14] run_main_repl(interactive::Bool, quiet::Bool, banner::Bool, history_file::Bool, color_set::Bool)
β @ Base ./client.jl:372
β [15] exec_options(opts::Base.JLOptions)
β @ Base ./client.jl:302
β [16] _start()
β @ Base ./client.jl:485
β @ Main REPL[22]:3
Got it to work, but unfortunately I cannot really explain why it works β¦
julia> macro smart_info(outer_msg)
quote
@info "$($(esc(:gs_tester)))"
end
end
@smart_info (macro with 1 method)
julia> function testsmartinfo()
gs_tester=1
@smart_info "did this work?"
end
testsmartinfo (generic function with 1 method)
julia> testsmartinfo()
[ Info: 1
In any case, the esc(:gs_tester)
is needed to paste the symbol gs_tester
into the code without the usual hygiene rules. What I donβt quite follow yet is the interaction with the interpolation in the call of the @info
macro, i.e., simply printing the variable within a function is easier and would work like:
macro smart_print(outer_msg)
quote
print($(esc(:gs_tester))
end
end
Hope you can make some sense out of this β¦
Ah yes, it needs to be interpolated twice.
Once into the quote, and once into the string.
I always find that fiddly