Skip to content

Commit

Permalink
Use instrument for coach.handler.finish event
Browse files Browse the repository at this point in the history
This change uses ActiveSupport::Notifications.instrument to time and
publish the event for coach.handler.finish. This makes Coach consistent
with how it handles handler and middleware events. Such a change should
not break any integrations, and can therefore be included in a patch.

The upshot of this is that integrating Coach with third-party
instrumentation tools then becomes a small bit easier. This is because
using #instrument over #publish will trigger start and end events under
the same key, if your subscriber is listening appropriately.
  • Loading branch information
lawrencejones committed Aug 5, 2017
1 parent d1b22ae commit 227947c
Showing 1 changed file with 21 additions and 20 deletions.
41 changes: 21 additions & 20 deletions lib/coach/handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def initialize(middleware, config = {})

# Run validation on the root of the middleware chain
delegate :validate!, to: :@root_item
delegate :publish, :instrument, to: ActiveSupport::Notifications

# The Rack interface to handler - builds a middleware chain based on
# the current request, and invokes it.
Expand All @@ -23,20 +24,25 @@ def call(env)
sequence = build_sequence(@root_item, context)
chain = build_request_chain(sequence, context)

start_event = start_event(context)
start = Time.now
event = build_event(context)

publish('coach.handler.start', start_event.dup)

begin
response = chain.instrument.call
ensure
status = response.try(:first) || STATUS_CODE_FOR_EXCEPTIONS
publish('coach.handler.finish', start, Time.now, nil,
start_event.merge(
response: { status: status },
metadata: context.fetch(:_metadata, {})
))
publish('coach.handler.start', event.dup)
instrument('coach.handler.finish', event) do
begin
response = chain.instrument.call
ensure
# We want to populate the response and metadata fields after the middleware
# chain has completed so that the end of the instrumentation can see them. The
# simplest way to do this is pass the event by reference to ActiveSupport, then
# modify the hash to contain this detail before the instrumentation completes.
#
# This way, the last coach.handler.finish event will have all the details.
status = response.try(:first) || STATUS_CODE_FOR_EXCEPTIONS
event.merge!(
response: { status: status },
metadata: context.fetch(:_metadata, {})
)
end
end
end

Expand Down Expand Up @@ -65,19 +71,14 @@ def inspect

private

# Trigger ActiveSupport::Notification
def publish(name, *args)
ActiveSupport::Notifications.publish(name, *args)
end

# Remove middleware that have been included multiple times with the same
# config, leaving only the first instance
def dedup_sequence(sequence)
sequence.uniq { |item| [item.class, item.middleware, item.config] }
end

# Event to send for start of handler
def start_event(context)
# Event to send with notifications
def build_event(context)
{
middleware: @root_item.middleware.name,
request: context[:request]
Expand Down

0 comments on commit 227947c

Please sign in to comment.