How to log to a file
It is a good idea to follow along by copy-pasting the code snippets into a Julia REPL!
Instead of logging to the terminal it is quite common to send log messages to a log file. Both the ConsoleLogger
and the SimpleLogger
from the Logging
standard library accept an IO stream as input, so it is pretty easy to plug an open filestream in those loggers and log to a file. Here is an example:
using Logging
io = open("logfile.txt", "w")
logger = ConsoleLogger(io)
with_logger(logger) do
@info "Message to the logfile"
end
close(io)
When reading the file and printing the result we can verify that the file contains the expected log output:
print(read("logfile.txt", String))
[ Info: Message to the logfile
With this approach you might notice that, due to IO buffering the messages will be written to the file with a delay, or possibly not until the program ends and the file is closed. In addition it is somewhat annoying to manage the file IO stream yourself like in the example above. Because of these reasons it is often better to use a logger that are implemented specifically for file-writing.
The LoggingExtras.jl package provide the FileLogger
, and, as the name suggest, this is implemented specifically for writing messages to a file. The FileLogger
opens the file and automatically flushes the stream after handling each messages. Here is an example of using the FileLogger
:
using Logging, LoggingExtras
logger = FileLogger("logfile.txt")
with_logger(logger) do
@info "First message to the FileLogger!"
end
Reading and printing the content:
print(read("logfile.txt", String))
┌ Info: First message to the FileLogger!
└ @ Main log-to-file.md:51
By default, when the FileLogger
opens the file stream it uses "write" mode (see documentation for open
), which means that if the file exist and have some content this will be overwritten. There is an option to use "append" mode by passing append = true
to the constructor. This will preserve content in the file and only append at the end. Here is an example of that where we open the same file from above, but use append = true
:
logger = FileLogger("logfile.txt"; append = true)
with_logger(logger) do
@info "Second message to the FileLogger?"
end
print(read("logfile.txt", String))
┌ Info: First message to the FileLogger!
└ @ Main log-to-file.md:51
┌ Info: Second message to the FileLogger?
└ @ Main log-to-file.md:73
As you can see, the first message is still in the file, and we only appended a second one.
The FileLogger
uses a SimpleLogger
internally, so the output formatting is the same as the SimpleLogger
. If you want to control the output formatting you can use a FormatLogger
(also from LoggingExtras.jl) instead. The FormatLogger
accepts a formatting function as the first argument. The formatting function takes two arguments: (i) an IO stream to write the formatted message to and (ii) the logging arguments (see LoggingExtras.handle_message_args
). Here is an example:
using Logging, LoggingExtras
logger = FormatLogger(open("logfile.txt", "w")) do io, args
# Write the module, level and message only
println(io, args._module, " | ", "[", args.level, "] ", args.message)
end
with_logger(logger) do
@info "Info message to the FormatLogger!"
@warn "Warning message to the FormatLogger!"
end
print(read("logfile.txt", String))
Main | [Info] Info message to the FormatLogger!
Main | [Warn] Warning message to the FormatLogger!
With the FormatLogger
we need to open the file stream manually with the preferred option ("w"
, "a"
, etc) but, just like the FileLogger
, it flushes the stream after every message.
See also:
- Send messages to multiple locations for how to send log messages to multiple loggers, for example to a file and to the terminal.
- How to rotate log files for more file logging options.