marlowe provides a correlation id header for Rails and Rack applications to correlate logs for a single request across multiple services.
You can install it as a gem:
$ gem install marlowe
or add it into a Gemfile:
gem 'marlowe'
To configure the header where Marlowe looks for and puts the correlation id:
-
In Rails, specify the header in your environment configuration:
config.marlowe_correlation_header = “My-Correlation-Id”
-
If you are adding the middleware to a Rack application directly, pass it as an option:
use Marlowe::Middleware, correlation_header: “My-Correlation-Id”
(Note that HTTP headers are case insensitive. In the Rack ‘env`, this header name is actually represented as `env`.)
The correlation id can be accessed throughout the application by accessing the RequestStore storage.
RequestStore[:correlation_id]
For a rails application, you simply need to change the log formatter to one of the provided ones. Correlated versions of both the SimpleFormatter and Formatter are included.
# config/environments/development.rb Rails.application.configure do config.log_formatter = Marlowe::SimpleFormatter.new end
To create your own formatter, you’ll need to access the RequestStore storage. You can use this pattern if you’ve rolled your own logger/formatter:
# lib/correlated_formatter.rb require 'request_store' class CorrelatedSimpleFormatter < ActiveSupport::Logger::SimpleFormatter def call(severity, timestamp, progname, msg) "[#{RequestStore.store[:correlation_id]}] #{super}" end end
As lograge supplies it’s own formatter, you will need to do something a little different:
# config/application.rb class Application < Rails::Application config.before_initialize do ... # use lograge for all request logs config.lograge.enabled = true config.lograge.custom_options = lambda do |event| { correlation_id: RequestStore[:correlation_id] } end end end
Catching and creating the correlation ID is a great all on its own, but to really take advantage of the correlation in a service based architecture you’ll need to pass the id to the next service in the change.
Here’s an example of a Hurley client:
# lib/correlated_client.rb require 'hurley' require 'request_store' class Hurley::CorrelatedClient < Hurley::Client def initialize(*args, &block) super header['Correlation-Id'] = ::RequestStore.store[:correlation_id] end end
If you have long-lived Hurley clients, it is also possible to use the Hurley callback machanism to add the outgoing headers:
client.before_call do |request| request.header['Correlation-Id'] = ::RequestStore.store[:correlation_id] end
or
class Correlator def name :correlator end def call(request) request.header['Correlation-Id'] = ::RequestStore.store[:correlation_id] end end client.before_call(Correlator.new)