A way to set logging level in Pluto notebooks

Is there a way to set the logging level of a cell in Pluto? By default it will show everything, including debug. Or even just a way to set it globally?

Pluto notebooks are a great way to debug newly developed code, and sometimes I have a need for debug prints when going over complex loops. Other times these same prints would flood the output, causing lag and taking out space.

I find myself using this pattern a lot

my_log_level >= -1000 && @debug "my message"

but it seems redundant to re-invent the wheel.

Alternatively, I can create my own logger and send it to stdout, which will show up under the cell.

logger1 = ConsoleLogger(stdout, Logging.Info)

with_logger(logger1) do
	foo()
	current_logger()
end

However, this converts the logs to pure text, losing some of the advanced display functionality Pluto has. Also, the font color for part of the text is not suitable for the background.

1 Like

You can set a created logger as the global logger!

I’m not at my computer at the moment, but I think if you’re skip passing in the ‘stderr’ stream, a SimpleLogger should act like the default Logger.

1 Like

The SimpleLogger makes all the text the same color, which fixes that problem. Thanks!

with_logger(SimpleLogger(stdout, Logging.Info)) do
    foo()
    current_logger()
end

However, it seems global_logger is ignored by Pluto.

begin
    global_logger(SimpleLogger(stdout, Logging.Info))
    foo()
end

Maybe, before launching Pluto, you could try setting the global logger in the REPL? It’s not great since you can’t switch once you’re in the notebook.

Update: I tried and it didn’t seem to work.

Maybe @fonsp can help?

I think also @Pangoraw can shed some light here, not sure if currently possible in Pluto

1 Like

Okay, I read the source code, and it sure does look impossible given the current structure. Each cell is assigned its own logger, and it checks Logger.shouldlog (method to dispatch on PlutoCellLogger is here). Right now, it is hard coded with:

level = convert(Logging.LogLevel, level)
    (_module isa Module && is_pluto_workspace(_module)) ||
        level >= Logging.Info ||
        level == Logging.LogLevel(-1) ||
        level == stdout_log_level

So it passes through info and above for non-pluto modules, and everything from within the notebook. Given that it’s all contained inside the Pluto logic, I think the only solution would be a toggle in the GUI.

I guess you could potentially have the constructor for PlutoCellLogger check the level of the global logger and match it. But then you could only change with with the hacky workaround: launch Pluto with Threads.@spawn and then use the REPL to change logger level. Doesn’t really work with Pluto’s design principles.

2 Likes

I was also looking at the source code.
It seems that you can filter log level globally by overloading the Logging.min_enabled_level which is defined here:

You can see that this work on this simple notebook:

2 Likes

Setting global logging in this way is also very useful. However, it seems that I can’t have both at once.

For some reason, if I set the global logging level as @disberd describes, alternative loggers will stop working:

Code for above scrot
begin
    using Logging
    Logging.min_enabled_level(::PlutoRunner.PlutoCellLogger) = Logging.Info
end

foo()

debug_logger = SimpleLogger(stdout, Logging.Info)

with_logger(debug_logger) do
    foo()
end

But if I comment out the line that sets global logging, it starts behaving again.

I think for now I will stick to custom logging logic in my code, or alternatively dump it all to the terminal instead.

1 Like

@AsgerHB, that is because the standard loglevel of the captured stdout from Pluto is -555 (it’s stored inside the variable PlutoRunner.stdout_log_level.

If you change the global logging min value like so:

Logging.min_enabled_level(::PlutoRunner.PlutoCellLogger) = PlutoRunner.stdout_log_level

you should also be able to see the captured stdout while stil filtering out the @debug messages since they have a level of -1000

Edit: With the help of the very nice LogginExtras package, you can also achieve this (and even more complex stuff) easily on a cell-by-cell basis:

1 Like

That clears it up!

And thanks for the tip on LoggingExtras. This lets me achieve the workflow I want by suppressing debug prints normally, and then using the LevelOverrideLogger to enable it in specific cells as needed.

Code snippet for the above scrot
# ╔═╡ Cell 1
begin
    using Logging
    using LoggingExtras
    Logging.min_enabled_level(::PlutoRunner.PlutoCellLogger) = 
            PlutoRunner.stdout_log_level
end

# ╔═╡ Cell 2
foo()

# ╔═╡ Cell 3
with_logger(LevelOverrideLogger(Logging.Debug, current_logger())) do
    foo()
end
1 Like