Chalk::Log
Chalk::Log adds a logger object to any class, which can be used for
unstructured or semi-structured logging. Use it as follows:
class A
include Chalk::Log
end
A.log.info('hello', key: 'value')
#=> [2013-06-18 22:18:28.314756] [64682] hello: key="value"
The output is both human-digestable and easily parsed by log indexing systems such as Splunk or Logstash.
It can also pretty-print exceptions for you:
module A
include Chalk::Log
end
begin
raise "hi"
rescue => e
end
A.log.error('Something went wrong', e)
#=> Something went wrong: hi (RuntimeError)
# (irb):8:in `irb_binding'
# /Users/gdb/.rbenv/versions/1.9.3-p362/lib/ruby/1.9.1/irb/workspace.rb:80:in `eval# /Users/gdb/.rbenv/versions/1.9.3-p362/lib/ruby/1.9.1/irb/workspace.rb:80:in `evaluate'
# /Users/gdb/.rbenv/versions/1.9.3-p362/lib/ruby/1.9.1/irb/context.rb:254:in `evaluate'
# /Users/gdb/.rbenv/versions/1.9.3-p362/lib/ruby/1.9.1/irb.rb:159:in `block (2 levels) in eval_input'
# [...]
The log methods accept a message and/or an exception and/or an info hash (if multiple are passed, they must be provided in that order). The log methods will never throw an exception, but will instead print an log message indicating they had a fault.
Overview
Including Chalk::Log creates a log method as both a class an
instance method, which returns a class-specific logger.
By default, it tags loglines with auxiliary information: a microsecond-granularity timestamp, the PID, and an action_id (which should tie together all lines for a single logical action in your system, such as a web request).
You can turn off tagging, or just turn off timestamping, through appropriate configatron settings (see config.yaml).
There are also two LSpace dynamic settings available:
LSpace[:action_id]: Set the action_id dynamically for this action. (This is used automatically by things likeChalk::Webwhich have a well-defined action.)LSpace[:'chalk.log.disabled']: Disable all logging.
You can use LSpace settings as follows:
class A; include Chalk::Log; end
foo = A.new
LSpace.with(action_id: 'request-123') do
foo.log.info('Test')
#=> [2014-05-26 01:12:28.485822] [47325|request-123] Test
end
Log methods
Chalk::Log provides five log levels:
debug, info, warn, error, fatal
Inheritance
Chalk::Log makes a heroic effort to ensure that inclusion chaining
works, so you can do things like:
module A
include Chalk::Log
end
module B
include A
end
class C
include B
end
and still have C.log and C.new.log work. (Normally you'd expect
for the class-method version to be left behind.)
Best practices
- You should never use string interpolation in your log message. Instead, always use the structured logging keys. So for example:
# Bad
log.info("Just printed #{lines.length} lines")
# Good
log.info("Printed", lines: lines.length)
Don't end messages with a punctuation --
Chalk::Logwill automatically add a colon if an info hash is provided; if not, it's fine to just end without trailing punctutaion. Case in pointIn most projects, you'll find most of your classes start including
Chalk::Log-- it's pretty cheap to add it, and it's quite lightweight to use. (In contrast, there's no good way to autoinclude it, since that would likely break many classes which aren't expecting a magicallogmethod to appear.)
Limitations
Chalk::Log is not very configurable. Our usage at Stripe tends to be
fairly opinionated, so there hasn't been much demand for increased
configurability. We would be open to making it less rigid,
however. (In any case, under the hood Chalk::Log is just using the
logging gem, so if the need arises it wouldn't be hard to acquire
the full flexibility of logging.)
Contributors
- Greg Brockman
- Andreas Fuchs
- Andy Brody
- Anurag Goel
- Evan Broder
- Nelson Elhage
- Brian Krausz
- Christian Anderson
- Jeff Balogh
- Jeremy Hoon
- Julia Evans
- Russell Davis
- Steven Noble