mimi-logger

A pre-configured logger for microservice applications.

[in development]

Installation

Add this line to your application's Gemfile:

gem 'mimi-logger'

And then execute:

$ bundle

Or install it yourself as:

$ gem install mimi-logger

Usage

require 'mimi/logger'

logger = Mimi::Logger.new format: :string

logger.info 'I am a banana!' # outputs "I, I am a banana!" to STDOUT

Logging format

Since v0.2.0 the default format for logging is JSON:

require 'mimi/logger'

logger = Mimi::Logger.new

logger.info 'I am a banana' # => '{"s":"I","m":"I am a banana","c":"60eecc2e764fe2f6"}'

The following properties of a serialized JSON object are reserved:

name description
s severity, one of D,I,W,E,F
m message
c context ID, 8 bytes hex encoded

Additional properties may be provided by the caller and they will be included in the logged JSON:

require 'mimi/logger'

logger = Mimi::Logger.new

t_start = Time.now
... # work, work
logger.info 'Jobs done', t: Time.now - t_start
# => '{"s":"I","m":"Jobs done","c":"60eecc2e764fe2f6", "t": 13.794034499}'

How to log structured data

There are multiple ways to log an event. The first and the simplest one is to log just a text message:

require 'mimi/logger'

logger = Mimi::Logger.new

logger.info 'I am a banana'
# or using a block variant
logger.debug { 'Debug this banana' }

Alternatively you can log a structured object, by passing a Hash in addition to the message:

require 'mimi/logger'

logger = Mimi::Logger.new

logger.info 'I am a banana', banana: { id: 123, weight: 456 }
# => {"s":"I","m":"I am a banana","c":"d8b6f859bf9d0190","banana":{"id":123,"weight":456}}

Or specify the object/Hash explicitly:

...
logger.info m: 'I am a banana', banana: { id: 123, weight: 456 }

Or with a block variant:

...
logger.debug do
  { banana: { id: 123, weight: 456 } }
end

# a block can also return an Array of one (Hash) or two (String,Hash) elements:
logger.debug { [m: 'Debug this banana', banana: { id: 123, weight: 456 }] }
logger.debug { ['Debug this banana', banana: { id: 123, weight: 456 }] }

Logging context

Logging context refers to a set of instructions that are somehow logically grouped. For example, the whole logic processing an incoming web request can be seen as operating within a single context of this particular web request. In a distributed or a multithreaded application it would be beneficial to identify logged messages produced within same context, otherwise the messages related to different web requests will be interleaved and understanding the flow, the cause and effect would be very difficult.

To solve this problem, Mimi::Logger allows for setting an arbitrary (or random) context ID, which is local to the current thread, and which is included in every logged message.

A new context may be initiated by .new_context!:

require 'mimi/logger'

logger = Mimi::Logger.new

logger.info 'I am a banana!'
logger.new_context!
logger.info 'I am a banana!' # this is not the same banana, it's from a different context

Or it can be set to an explicit value:

require 'mimi/logger'

logger = Mimi::Logger.new

logger.info 'I am a banana!'
logger.context_id = 'different-context!'
logger.info 'I am a banana!' # this is not the same banana, it's from a 'different-context!'

Context ID in a multithreaded application

The context ID is local to a current thread, so it's safe to start or assign a different context ID in other threads of the application.

Context ID in a distributed application

If you have a distributed multicomponent application (e.g. microservice architecture), context ID may help to track requests between multiple parties. In order to achieve it, you need to generate a new context ID in the beginning of your processing and pass it along with all the requests/messages to other components of the system. Upon receiving such a request, another application sets its local context ID to the received value and continues.

Preserving context

If necessary, it is possible to run a block of code in another logging context and restore the original context after completion:

require 'mimi/logger'

logger = Mimi::Logger.new

logger.context_id = 'original-context'
logger.with_preserved_context do
  logger.context_id = 'another-context'
  ...
end
logger.context_id # => "original-context"

Or run a block of code with a new temporary context and restore the original one after completion:

require 'mimi/logger'

logger = Mimi::Logger.new

logger.context_id = 'original-context'
logger.with_new_context do
  logger.context_id # => "5d11f7c483dcfb2a"
  ...
end
logger.context_id # => "original-context"

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/kukushkin/mimi-logger. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

License

The gem is available as open source under the terms of the MIT License.