Skip to content

Instantly share code, notes, and snippets.

@gshutler
Last active December 10, 2015 11:48
Show Gist options
  • Save gshutler/4429613 to your computer and use it in GitHub Desktop.
Save gshutler/4429613 to your computer and use it in GitHub Desktop.
Exploration of Nested Diagnostics Context (NDC) for Hatchet

Adding NDC to Hatchet

I like the idea of adding NDCs to Hatchet.

Most of the hard work could be contained within some middleware for Rack-based applications. Non-Rack applications would be responsible for calling context.clear before/after doing all the work for a context so that it was clear before new work commenced.

I'm thinking the API of the logger (accessed via the log and logger mixin methods) would be extended to include:

# Public: Returns a handle to the current logging context which
# spans all loggers in all classes.
#
# contexts - Zero or more Strings to add to the NDC stack, will be
#            ignored if no block is provided.
# block    - An optional block which can be used to push context
#            on to the NDC stack for the duration of the block.
#            The block will be invoked instantly.
#
# Returns a reference to the Hatchet::Context module/instance of
# that class, to be determined based upon implementation.
def context(*contexts, &block = nil)

The API here is a bit odd but there are essentially two forms as demonstrated in the example. One that takes a block and adds the context for the duration of that block, and one that does not modify the context but returns the context so that it can be manipulated.

In turn the Hatchet::Context would have an API like:

module Hatchet::Context

  # Public: Adds one or more levels of context to the NDC stack.
  #
  # contexts - One or more Strings to add to the NDC stack.
  #
  # Returns nothing.
  def push(*contexts)
  
  # Public: Removes one level of context from the NDC stack.
  #
  # Returns the removed context.
  def pop
  
  # Public: Clears the context of the NDC stack.
  #
  # Returns the removed contexts.
  def clear
  
end

The example below should demonstrate some usage of this which may help clarify.

Exposure to formatters

The current state of the NDC will be duplicated to the Hatchet::Message that formatters receive as message#ndc. They can then utilise the NDC as they see fit, for some this may mean they ignore it entirely. This also means that the change will be non-breaking.


Please add feedback in the comments, including thoughts as to the format the core formatters could use. Thanks!

module Authentication
def authenticate!
# Do something to check for the user.
# Push the current user's name as an additional context.
log.context.push(current_user.name)
end
end
class PostsController
include Hatchet
include Authentication
before_filter :authenticate!
def index
# In reality this could be done as part of the Rails
# pipeline but it makes for a simple example.
log.context.push("posts#index")
log.info { "Hello, World" }
# => INFO - PostsController - Bob, posts#index - Hello, World
log.context.pop
log.info { "Hello, again" }
# => INFO - PostsController - Bob - Hello, again
end
def show
log.context("posts#show.#{params[:id]}") do
log.info { "Hello, World" }
# => INFO - PostsController - Bob, posts#show.123 - Hello, World
end
log.info { "Hello, again" }
# => INFO - PostsController - Bob - Hello, again
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment