Lumberjack.jl

Create logs that would make Paul Bunyan blush
Popularity
35 Stars
Updated Last
4 Years Ago
Started In
September 2013

Lumberjack.jl

Build Status

Quick Start

Pkg.add("Lumberjack")

Create logs

julia> using Lumberjack

julia> debug("something innocuous happened!")
2013-12-02T19:39:16.123 UTC - debug:"something innocuous happened!"

julia> info("using more memory!", {:mem_allocated => 9001, :mem_left => 22})
2013-12-02T19:39:21.345 UTC - info:"using more memory!" mem_allocated:9001 mem_left:22

julia> warn("running really low on memory...", {:mem_left => "22 k"})
2013-12-02T19:39:44.456 UTC - warn:"running really low on memory..." mem_left:"22 k"

julia> try
         error("OUT OF MEMORY - IT'S ALL OVER - ARRGGGHHHH")
       catch err
         # Acts like Base.error, throws an `ErrorException`
       end
2013-12-02T19:39:48.678 UTC - error:"OUT OF MEMORY - IT'S ALL OVER - ARRGGGHHHH"

julia> log("info", "use `log` for user-defined modes, or to be verbose.")
2013-12-12T23:58:56.890 UTC - info:"use `log` for user-defined modes, or to be verbose."

Add and remove TimberTrucks

Logs are brought to different output streams by TimberTrucks. To create a truck that will dump logs into a file, simply:

julia> add_truck(LumberjackTruck("mylogfile.log"), "my-file-logger")

Now there is a truck named "my-file-logger", and it will write all of your logs to mylogfile.log. Your logs will still show up in the console, however, because -by default- there is a truck named "console" already hard at work. Remove it by calling:

julia> remove_truck("console")

Manage logging modes/levels

Defining Modes

Timber trucks and saws can both be configured to work only at/above certain log levels/modes.

There are 4 built-in modes: ["debug", "info", "warn", "error"]. If you'd like to use logging levels/modes beyond the four default modes, you'll want to tell your lumber mill (Lumberjack's log controller) what order the modes are in. You can do this with a call to configure:

julia> configure(; modes=["debug", "info", "notice", "warn", "error", "crit", "alert", "emerg"])

This way, if you only want a truck to log something at warning-level or above and it sees a "notice" message, the truck will know not to log it.

Timber Truck Modes

Each timber truck is configured to log messages at or above a certain level/mode, and by default they will log everything. To create a timber truck that will only record warnings and errors, you can:

julia> add_truck(LumberjackTruck(STDOUT, "warn"), "dangerous-logger")

Or to configure an existing truck, you can call configure and specify the truck in question:

julia> configure(timber_truck; mode = "warn")

For additional examples, see the Log Level Example below.

Saw Modes

When saws are added, they can also be configured such that they are only included for logs at or above a certain level. For example, you may want to include a stack trace for each log that's a warning or above:

julia> add_saw(Saw(Lumberjack.stacktrace_saw, "warn"))

For additional examples, see the Log Level Example below.

Logging Options

Lumberjack.add_truck provides an optional third Dict argument. Possible keys are:

is_colorized

Colors can be added enabled using the following:

add_truck(LumberjackTruck(STDOUT, nothing, {:is_colorized => true}), "console")

colors

By default the following colors are used:

{"debug" => :cyan, "info" => :blue, "warn" => :yellow, "error" => :red}

colors

Custom colors/log levels can also be specified:

add_truck(LumberjackTruck(STDOUT, nothing, :colors => {"debug" => :black, "info" => :blue, "warn" => :yellow, "error" => :red, "crazy" => :green}), "console")

uppercase

Log levels can be made uppercase (INFO vs info, etc.) with the following option:

add_truck(LumberjackTruck(STDOUT, nothing, {:uppercase => true}), "console")

Architecture

There are three main components of Lumberjack.jl that you can manipulate:

LumberMill

A lumber mill holds information needed to manage the whole process of creating and storing logs. There is a global _lumber_mill inside the Lumberjack module and, in all likelyhood, you wont need to create another. All exported api methods that accept a LumberMill will be overloaded to use _lumber_mill by default.

Saw functions

A saw function simply takes in a dict of parameters, adds or removes things, and then returns the dict back. By default, the date_saw is applied to logs that come in and appends date => DateTime.now().

TimberTruck

Timber trucks are used to send logs to their final destinations (files, the console, etc). A timber truck inherits from the abstract type TimberTruck and overloads the log(t::TimberTruck, args::Dict) function. By default, the framework will create a LumberjackLog truck that will print args as a string of key:value pairs to STDOUT.

API

Logging

log(lm::LumberMill, mode::String, msg::String, args::Dict)
  • mode is a string like "debug", "info", "warn", "error", etc
  • msg is an explanative message about what happened
  • args is an optional dictionary of data to be recorded alongside msg
debug(lm::LumberMill, msg::String, args::Dict)
info(lm::LumberMill, msg::String, args::Dict)
warn(lm::LumberMill, msg::String, args::Dict)
error(lm::LumberMill, msg::String, args::Dict)
  • each call log with mode filled in appropriately

Saws

add_saw(lm::LumberMill, saw::Saw, index::Integer)
add_saw(lm::LumberMill, saw_fn::Function, index::Integer)
  • index is optional and will default to the end of the saw list

Typically when adding a saw you will simply specify the saw function itself, but if you'd like to limit your saw to certain logging levels/modes then you'll want to use the Lumberjack.Saw type. See the Log Level Example below.

remove_saw(lm::LumberMill, index)
  • index is optional, by default the last saw in the list will be removed
remove_saws(lm::LumberMill)  # removes ALL saws currently in use

Trucks

add_truck(lm::LumberMill, truck::TimberTruck, name)
  • name is optional and will default to a unique id
remove_truck(lm::LumberMill, name)
  • name is the id associated with the truck to be removed
remove_trucks(lm::LumberMill)  # removes ALL trucks currently in use

Configuration

configure(lm::LumberMill; modes = ["debug", "info", "warn", "error"])
  • modes is an ordered array of logging levels

Recipes and Examples

Including Additional Fields

Additional parameters may be specified in calls to log (and debug, info, warn, and error) by passing a Dict as the final positional argument. This is useful if you'd like to specify values for fields other than mode and msg that are not provided by saws.

These additional parameters can also be specified with keyword arguments:

julia> using Lumberjack

# The easy way.
julia> Lumberjack.warn("Something happened."; id="LoggingTest", impact="None, really.", resolve="Next steps.", cause="Needed an example.")
2015-11-16T15:23:54 - warn: Something happened. resolve: "Next steps." id: "LoggingTest" cause: "Needed an example." impact: "None, really."

# The hard(er) way.
julia> Lumberjack.warn("Something happened.", Dict{Any, Any}(:id=>"LoggingTest", :impact=>"None, really.", :resolve=>"Next steps.", :cause=>"Needed an example."))
2015-11-16T15:24:54 - warn: Something happened. resolve: "Next steps." id: "LoggingTest" cause: "Needed an example." impact: "None, really."

Log Level Example

julia> using Lumberjack

# We already have console output of all modes/log levels via the default console truck.

# Define some additional log levels.
julia> configure(; modes=["debug", "info", "warn", "error", "crit"])

# Let's add a truck that will ignore debug/info messages (only outputting info-level and up).
julia> add_truck(LumberjackTruck(STDOUT, "info"), "new-logger")
Lumberjack.LumberjackTruck(Base.TTY(open, 0 bytes waiting),"info",Dict{Any,Any}(:is_colorized=>false,:uppercase=>false))

# Let's add another that will only output logs at warning-level and above.
julia> add_truck(JsonTruck(STDOUT, "warn"), "json-logger")
Lumberjack.JsonTruck(Base.TTY(open, 0 bytes waiting),"warn")

# Add the function call saw to each log entry that is error-level or above.
add_saw(Saw(Lumberjack.fn_call_saw, "error"))
2-element Array{Lumberjack.Saw,1}:
 Lumberjack.Saw(Lumberjack.msec_date_saw,nothing)
 Lumberjack.Saw(Lumberjack.fn_call_saw,"error")

# Crticial messages will show up for all three trucks: json-logger, new-logger, console (default).
julia> log("crit", "Message")
{"date":"2015-11-19T11:24:31","lookup":{"name":"eval_user_input","file":"REPL.jl","line":62},"msg":"Message","mode":"crit"}
2015-11-19T11:24:31 - eval_user_input@REPL.jl:62 - crit: Message
2015-11-19T11:24:31 - eval_user_input@REPL.jl:62 - crit: Message

# Warning messages also show up for all trucks (but without function call information).
julia> log("warn", "Message")
{"date":"2015-11-19T11:24:49","msg":"Message","mode":"warn"}
2015-11-19T11:24:49 - warn: Message
2015-11-19T11:24:49 - warn: Message

# Info is less important than a warning, so won't show up for new-logger.
julia> log("info", "Something")
2015-11-19T11:25:52 - info: Message
2015-11-19T11:25:52 - info: Message

# Debug level isn't important enough to log for either json-logger or new-logger.
julia> log("debug", "Not very important")
2015-11-19T11:26:16 - debug: Not very important

Syslog and Stack Trace Example

Please note that syslog output is only available on systems that have logger utility installed. (This should include both Linux and OS X, but typically excludes Windows.)

julia> using Lumberjack

# Configure Lumberjack to recognize all Syslog log levels.
julia> configure(; modes=["debug", "info", "notice", "warn", "error", "crit", "alert", "emerg"])

# Output to syslog on facility "local0", with tag "julia", and include Julia's process ID.
julia> syslog_io = Syslog(:local0, "julia", true)
Lumberjack.Syslog(:local0,"julia",63474)

# Send logs in JSON format to syslog_io, but only if they're warnings or above.
julia> add_truck(JsonTruck(syslog_io, "warn"), "syslog-json")
Lumberjack.JsonTruck(Lumberjack.Syslog(:local0,"julia",63474),"warn")

# Add a stacktrace to each log entry that is error-level or above.
add_saw(Saw(Lumberjack.stacktrace_saw, "error"))
2-element Array{Lumberjack.Saw,1}:
 Lumberjack.Saw(Lumberjack.msec_date_saw,nothing)
 Lumberjack.Saw(Lumberjack.stacktrace_saw,"error")

julia> log("crit", "Error message!")		# Includes stack trace.
julia> log("warn", "Warning message!")		# No stack trace.
julia> log("notice", "Notice message!")		# Not logged.

Run tail /var/log/system.log (modifying as needed, depeding on where your system stores its logs) and you should see something like this:

Nov 17 15:00:03 localhost julia[63474]: {"stacktrace":[{"name":"eval_user_input","file":"REPL.jl","line":62},{"name":"anonymous","file":"REPL.jl","line":92}],"date":"2015-11-17T15:00:03","msg":"Error message!","mode":"error"}
Nov 17 15:00:45 localhost julia[63474]: {"date":"2015-11-17T15:00:45","msg":"Warning message!","mode":"warn"}

The info message is missing because we set our truck to only output logs at warning level and above.

Note that BSD's logger (used on OS X) will append a second process ID, which is the PID of the logger tool itself.