Project

marlowe

0.01
Low commit activity in last 3 years
A long-lived project that still receives updates
{Marlowe}[https://github.com/KineticCafe/marlowe] is a Rack middleware that extracts or creates a request ID using a pre-defined header, permitting request correlation across multiple services. When using Rails, Marlowe automatically adds itself to the middleware before <tt>Rails::Rack::Logger</tt>.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
 Dependencies

Development

Runtime

>= 0.9, < 3
 Project Readme

Marlowe, Your Request Sleuth¶ ↑

code

github.com/KineticCafe/marlowe/

issues

github.com/KineticCafe/marlowe/issues

docs

www.rubydoc.info/github/KineticCafe/marlowe/master

continuous integration

Build Status

Description¶ ↑

Marlowe is a Rack middleware that extracts or creates a request ID using a pre-defined header, permitting request correlation across multiple services.

When using Rails, Marlowe automatically adds itself to the middleware before Rails::Rack::Logger.

Upgrading from Marlowe 1.x¶ ↑

In Marlowe 1.0, the correlation header was called Correlation-Id; since then, Rails 5 and other tools and frameworks (such as Phoenix) have standardized on the header X-Request-Id. Marlowe 2.0 changes to this header by default.

To keep complete compatibility with Marlowe 1.0, the following should be used:

# Rails
config.marlowe_header = 'Correlation-Id'
config.marlowe_handler = :simple
config.marlowe_return = false

# Rack
use Marlowe::Middleware, header: 'Correlation-Id', handler: :simple, return: false

Configuration¶ ↑

Marlowe has three main configuration options: the request ID header, the request ID handler, and the request ID return. The options may be provided to the Rack use command as a keyword option, or set in a corresponding marlowe_option configuration variable in Rails.

Request ID Header¶ ↑

Specifies the header to be used for the request correlation ID. Defaults to X-Request-Id.

# Rails
config.marlowe_header = 'Correlation-Id'
# OR: config.marlowe_correlation_header = 'Correlation-Id'

# Rack
use Marlowe::Middleware, header: 'Correlation-Id'
# OR: use Marlowe::Middleware, correlation_header: 'Correlation-Id'

Marlowe will convert this to an appropriate HTTP header (in the Rack env parameter, the above header would be represented as env['HTTP_CORRELATION_ID']).

Request ID Handler¶ ↑

Specifies the method for sanitizing or generating the request correlation ID. Values can be :clean (the default, which limits incoming correlation IDs to 255 alphanumeric-or-dash characters), :simple (does not limit incoming correlation IDs), or a proc to transform or generate a correlation ID.

In all cases, if a correlation request ID is not handled, a UUID will be generated.

# Rails
config.marlowe_handler = :simple
config.marlowe_handler = ->(req_id) {
  req_id.try(:reverse) || SecureRandom.uuid
}

# Rack
use Marlowe::Middleware, handler: :simple
use Marlowe::Middleware, handler: ->(req_id) {
  req_id.try(:reverse) || SecureRandom.uuid
}

Request ID Return¶ ↑

If true (the default), the request correlation ID will be returned to the client in the same header that it was provided in.

# Rails
config.marlowe_return = false

# Rack
use Marlowe::Middleware, return: false

Using Marlowe with Rails 5¶ ↑

Rails 5 includes the ActionDispatch::RequestId middleware, reducing the need for Marlowe. Marlowe is more configurable than the Rails 5 default, so set marlowe_replace_action_dispatch_request_id to true to have Marlowe::Middleware will replace ActionDispatch::RequestId:

# Rails only
config.marlowe_replace_action_dispatch_request_id = true

Accessing the Correlation ID¶ ↑

The correlation id can be accessed throughout the application by accessing the RequestStore storage.

RequestStore[:correlation_id]

Logging¶ ↑

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

Lograge¶ ↑

As lograge supplies its 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

Clients¶ ↑

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 request 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['X-Request-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['X-Request-Id'] = ::RequestStore.store[:correlation_id]
end

or

class Correlator
  def name
    :correlator
  end

  def call(request)
    request.header['X-Request-Id'] = ::RequestStore.store[:correlation_id]
  end
end

client.before_call(Correlator.new)

Install¶ ↑

Add Marlowe to your Gemfile:

gem 'marlowe', '~> 2.0'

Or manually install:

$ gem install marlowe

Marlowe Semantic Versioning¶ ↑

Marlowe uses a Semantic Versioning scheme with one significant change:

  • When PATCH is zero (0), it will be omitted from version references.

Additionally, the major version will generally be reserved for plug-in infrastructure changes.

Community and Contributing¶ ↑

Marlowe welcomes your contributions as described in Contributing.md. This project, like all Kinetic Cafe open source projects, is under the Kinetic Cafe Open Source Code of Conduct.