Compose loggers and logger ensembles declaratively using configuration files.
Author tanmaykm
0 Stars
Updated Last
2 Years Ago
Started In
April 2020


Build Status Build Status codecov.io

Provides a way to specify hierarchical logging configuration in a file.

Configuration file is in the form of a TOML file. Configuration sections are named, with each section specifying a logger type and parameters needed for its construction. Sections inherit parameter values from preceeding sections and can override them as well. Loggers can be constructed by providing the name of a section.

Here is what a configuration that allows logging to several types of loggers may look like.

Plugging in a Logger

Support for a logger can be added by providing an implementation of LogCompose.logcompose for the target logger type. The implementation needs to be of the following form:

function LogCompose.logcompose(::Type{MyLoggerType},
        config::Dict{String,Any},           # config: the entire logging configuration file
        logger_config::Dict{String,Any})    # logger_config: configuration relevant for the
                                            #      section specified to `LogCompose.logger`
                                            #      with the hierarchy flattened out
    # provides support for MyLoggerType in LogCompose

For complete examples, refer to any of the existing implementations listed below.

Loggers Supported

LogCompose has in-built support for the loggers provided in the stdlib logging package. They are listed below with example configuration sections illustrating parameters they accept.

  • Logging.SimpleLogger
    type = "Logging.SimpleLogger"
    # min_level = "Debug"             # Debug, Info (default) or Error
    stream = "simple.log"             # file to log to
  • Logging.ConsoleLogger
    type = "Logging.ConsoleLogger"
    # min_level = "Debug"             # Debug, Info (default) or Error
    stream = "stdout"                 # stdout (default), stderr or a filepath
  • Logging.NullLogger
    type = "Logging.NullLogger"

There are external packages that provide support for a few other types of loggers as well:

For loggers supplied by external packages, LogCompose looks for the logger implementation type (the one mentioned in type configuration attribute) in the Main module by default. But if your code imports the external loggers within your module instead of the Main module, then the module name where the logger type can be found must be specified in the (otherwise optional) topmodule configuration parameter. E.g.:

type = "LogRoller.RollingFileLogger"
topmodule = "MyModule"


Here is an example configuration using multiple logger types, from different logging packages.

type = "LogRoller.RollingLogger"
min_level = "Info"
nfiles = 5

type = "SyslogLogging.SyslogLogger"
facility = "user"

filename = "/tmp/testapp1.log"

filename = "/tmp/testapp2.log"
min_level = "Debug"     # overrides min_level to Debug for testapp2
nfiles = 10             # overrides nfiles to 10 for testapp2

identity = "testapp1"
facility = "daemon"     # facility set to daemon instead of default user

identity = "testapp2"

type = "LoggingExtras.TeeLogger"
destinations = ["file.testapp1", "syslog.testapp1"]

type = "LoggingExtras.TeeLogger"
destinations = ["file.testapp2", "syslog.testapp2"]

And below is a snippet of Julia code that make use of this configuration:

julia> using LogCompose, Logging

julia> using LogRoller, LogRollerCompose

julia> using SyslogLogging, SyslogLoggingCompose

julia> using LoggingExtras, LoggingExtrasCompose

julia> logger1 = LogCompose.logger("testconfig.toml", "testapp1");

julia> typeof(logger1)

julia> logger2 = LogCompose.logger("testconfig.toml", "testapp2");

julia> typeof(logger2)

julia> first(logger1.loggers).stream.filename

julia> first(logger2.loggers).stream.filename

julia> first(logger2.loggers).stream.nfiles

julia> with_logger(logger1) do
           @info("hello from app1")

shell> cat /tmp/testapp1.log
┌ Info: 2020-04-02T12:03:03.588: hello from app1
└ @ Main REPL[13]:2

julia> with_logger(logger2) do
           @info("hello from app2")

shell> cat /tmp/testapp2.log
┌ Info: 2020-04-02T12:04:13.156: hello from app2
└ @ Main REPL[15]:2