require 'grape'
require 'statsd'
require 'socket'
module Grape
module Datadog
class Middleware < ::Grape::Middleware::Base
# Create a new +Datadog+ middleware instance.
#
# ==== Options
# * :hostname - the hostname used for instrumentation, defaults to system hostname, respects +INSTRUMENTATION_HOSTNAME+ env variable
# * :metric_name - the metric name (prefix) to use, defaults to "grape.request"
# * :tags - array of custom tags, these can be plain strings or lambda blocks accepting a rack env instance
# * :statsd_host - the statsD host, defaults to "localhost", respects +STATSD_HOST+ env variable
# * :statsd_port - the statsD port, defaults to 8125, respects +STATSD_PORT+ env variable
# * :prefer_global - if set, tries to find global `$statsd` instance, otherwise connects to +statsd_host+:+statsd_port. Default: true
def initialize(app, opts = {})
hostname = opts[:hostname] || ENV['INSTRUMENTATION_HOSTNAME'] || Socket.gethostname
statsd_host = opts[:statsd_host] || ENV['STATSD_HOST'] || "localhost"
statsd_port = (opts[:statsd_port] || ENV['STATSD_PORT'] || 8125).to_i
@app = app
@metric = opts[:metric_name] || "grape.request"
@statsd = opts[:prefer_global] == false || !defined?($statsd) ? ::Statsd.new(statsd_host, statsd_port) : $statsd
@tags = opts[:tags] || []
@tags.push "host:#{hostname}"
end
def call(env)
tags = prepare_tags(env)
@statsd.time "#{@metric}.time", :tags => tags do
resp = @app.call(env)
tags.push "status:#{resp.status}"
@statsd.increment @metric, :tags => tags
resp
end
end
private
def prepare_tags(env)
path = env['api.endpoint'].routes.first.route_path[1..-1].gsub("/", ".").sub(/\(\.:format\)\z/, "").gsub(/\.:(\w+)/, '.{\1}')
@tags.map do |tag|
case tag
when String
tag
when Proc
tag.call(env)
end
end.compact + ["method:#{env[Rack::REQUEST_METHOD]}", "path:#{path}"]
end
end
end
end