Tracing functions, inverse of Meta.parse?

I’ve not found a function tracing facility for Julia so I’m trying to implement a simple one. My idea is to have a macro,@trace, that prefixes a method definition and alters that definition to start with a log message that prints the function and arguments and ends with a log message that prints the return values. The macro also takes a global variable name as argument. Logging will only occur if that variable is true at execution time.

using Pkg
Pkg.add(;url="https://github.com/MarkNahabedian/NahaJuliaLib.jl")
using NahaJuliaLib

@trace(trace_hanoi, function hanoi(from, to, other, count)
    if count == 1
        println("move 1 from $from to $to")
        return
    else
        hanoi(from, other, to, count - 1)
        println("move 1 from $from to $to")
        hanoi(other, to, from, count - 1)
        return (from, to)   # arbitrary result to show
    end
end)

trace_hanoi = false

With tracing turned off we just get

hanoi(:a, :b, :c, 2)
move 1 from a to c
move 1 from a to b
move 1 from c to b
(:a, :b)

but with tracing turned on, we get

trace_hanoi = true
true
hanoi(:a, :b, :c, 2)
┌ Info: Trace Enter
└   Expr(:call, hanoi, Expr(:parameters, Expr(:kw), from, to, other, count)) = :((hanoi)(; $(Expr(:kw)), a, b, c, 2))
┌ Info: Trace Enter
└   Expr(:call, hanoi, Expr(:parameters, Expr(:kw), from, to, other, count)) = :((hanoi)(; $(Expr(:kw)), a, c, b, 1))
move 1 from a to c
┌ Info: Trace Exit
└   result = nothing
move 1 from a to b
┌ Info: Trace Enter
└   Expr(:call, hanoi, Expr(:parameters, Expr(:kw), from, to, other, count)) = :((hanoi)(; $(Expr(:kw)), c, b, a, 1))
move 1 from c to b
┌ Info: Trace Exit
└   result = nothing
┌ Info: Trace Exit
└   result = nothing
(:a, :b)

I would like for the function call expression in the @info output to look like Julia surface syntax. I don’t see Meta.unparse or Meta.serialize. Is there a function that will do the inverse of Meta.parse – going from an expression to a string of source code?

Thanks.

string does this, but the default logging backend should already format expressions this way if you use structured logging:

julia> sig = :(hanoi(from, to, other, count))
:(hanoi(from, to, other, count))

julia> @info "Entering function" signature=sig
┌ Info: Entering function
└   signature = :(hanoi(from, to, other, count))
1 Like