Update variable in logged message without printing a new line?

I’m wondering if it’s possible to update a logged message without printing a new line. I’d like to log some information to the console on each iteration of a loop but, rather than printing a new @info statement on each iteration, I’d like to simply update the existing message to reflect the new value of the variable.

For example, instead of this:

function testlog()
    i = 1
    for n in 1:5
        @info "Processing file number $i"
        i += 1
    end
end

julia> testlog()
[ Info: Processing file number 1
[ Info: Processing file number 2
[ Info: Processing file number 3
[ Info: Processing file number 4
[ Info: Processing file number 5

I’d like to be able to do something like this:

function testlog()
    i = 1
    @info "Processing file number $i"
    for n in 1:5
        i += 1
    end
end

julia> testlog()
[ Info: Processing file number 1     # This updates on each iteration

Is this possible? If not, is it possible to have the “processing file number #” print as part of the same Info statement? What I mean is, instead of the word “Info” showing up, can I just append the message to an existing @info event? It would look something like this:

[ Info: Processing file number 1
|       Processing file number 2
|       Processing file number 3
....

Aware of ProgressMeter.jl? It supports updating the message in place with arbitrary number of variables, nested loops, asynchronously.

5 Likes

For a quick and dirty alternative you could use an ANSI escape code to move the cursor back 1000 positions after printing the string (we are at the start of the line again).

function testlog()
    i = 1
    for n in 1:5
        print("Processing file number $i\u001b[1000D")
        sleep(0.3)  # simulate some work
        i += 1
    end
end

testlog()
3 Likes

Aware of ProgressMeter.jl ?

I was not aware, thanks!

For a quick and dirty alternative you could use an ANSI escape code to move the cursor back 1000 positions after printing the string (we are at the start of the line again).

This is clever, thank you :wink:

In Genie I successfully made use of the REPL.Terminals API to provide a status for loading the different parts of an app.

The functionality is wrapped in a helper function which prints to screen:

function replprint(output::String, terminal;
                    newline::Int = 0, clearline::Int = 1, color::Symbol = :white, bold::Bool = false, sleep_time::Float64 = 0.2,
                    prefix::String = "", prefix_color::Symbol = :green, prefix_bold::Bool = true)
  if clearline > 0
    for i in 1:(clearline+1)
      REPL.Terminals.clear_line(terminal)
    end
  end

  isempty(prefix) || printstyled(prefix, color = prefix_color, bold = prefix_bold)
  printstyled(output, color = color, bold = bold)

  if newline > 0
    for i in 1:newline
      println()
    end
  end

  sleep(sleep_time)
end

The function is used like this:

function load(; context::Union{Module,Nothing} = nothing) :: Nothing
  t = Terminals.TTYTerminal("", stdin, stdout, stderr)

  replprint("initializers", t, clearline = 0, prefix = "Loading ")
  load_initializers(context = context)

  replprint("helpers", t, prefix = "Loading ")
  load_helpers()

  replprint("lib", t, prefix = "Loading ")
  load_libs()

  replprint("resources", t, prefix = "Loading ")
  load_resources()

  replprint("plugins", t, prefix = "Loading ")
  load_plugins(context = context)

  replprint("routes", t, prefix = "Loading ")
  load_routes_definitions(context = context)

  replprint("Ready! ", t, clearline = 2, color = :green, bold = :true)
  println()

  nothing
end
2 Likes

Thanks so much for sharing! I love Genie.jl, by the way :heart_eyes:

1 Like