Skip to content

Instantly share code, notes, and snippets.

@javierhonduco
Last active April 4, 2017 13:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save javierhonduco/716b1418042781fd4870f5587709224a to your computer and use it in GitHub Desktop.
Save javierhonduco/716b1418042781fd4870f5587709224a to your computer and use it in GitHub Desktop.
Small rack server timing middleware
# This simple Rack middleware subscribes to all AS::Notifications
# and adds the appropriate `Server-Timing` header as described in
# the spec [1] with the notifications grouped by name and with the
# elapsed time added up.
#
# [1] Server Timing spec: https://w3c.github.io/server-timing/
module Rack
class ServerTimingMiddleware
def initialize(app)
@app = app
end
def call(env)
events = []
subs = ActiveSupport::Notifications.subscribe(//) do |*args|
events << ActiveSupport::Notifications::Event.new(*args)
end
status, headers, body = @app.call(env)
# As the doc states, this harms the internal AS:Notifications
# caches, but I'd say it's necessary so we don't leak memory
ActiveSupport::Notifications.unsubscribe(subs)
mapped_events = events.group_by { |el|
el.name
}.map{ |event_name, event_data|
agg_time = event_data.map{ |ev|
ev.duration
}.inject(0){ |curr, accum| curr += accum}
# We need the string formatter as the scientific notation
# a.k.a <number>e[+-]<exponent> is not allowed
# Time divided by 1000 as it's in milliseconds
[event_name, '%.10f' % (agg_time/1000)]
}
# Example output:
# 'cpu=0.009; "CPU", mysql=0.005; "MySQL", filesystem=0.006; "Filesystem"'
headers['Server-Timing'] = mapped_events.map do |name, elapsed_time|
"#{name}=#{elapsed_time}; \"#{name}\""
end.join(', ')
[status, headers, body]
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment