require 'grape/datadog/version' require 'grape' require 'statsd' require 'socket' require 'active_support/notifications' module Grape class Datadog include Singleton # Configure and install datadog instrumentation. Example: # # Grape::Datadog.install! do |c| # c.hostname = "my-host" # end # # Settings: # * 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 # * statsd - custom statsd instance def self.install!(&block) block.call instance if block instance.send(:subscribe!) end attr_accessor :hostname, :metric_name, :statsd_host, :statsd_port, :tags, :statsd def initialize @hostname = ENV['INSTRUMENTATION_HOSTNAME'] || Socket.gethostname @metric_name = "grape.request" @statsd_host = ENV['STATSD_HOST'] || "localhost" @statsd_port = (ENV['STATSD_PORT'] || 8125).to_i @tags = [] end private def subscribe! if frozen? warn "#{self.class.name} is already installed (called from #{caller[1]})" return end if tags.none? {|t| t =~ /^host\:/ } tags.push("host:#{hostname}") end if ENV['RACK_ENV'] && tags.none? {|t| t =~ /^env\:/ } tags.push("env:#{ENV['RACK_ENV']}") end ActiveSupport::Notifications.subscribe 'endpoint_run.grape' do |_, start, finish, _, payload| record payload[:endpoint], ((finish-start)*1000).round end @statsd ||= ::Statsd.new(statsd_host, statsd_port) freeze end def record(endpoint, ms) route = endpoint.route version = route.route_version method = route.route_method path = route.route_path path.sub!("(.:format)", "") path.sub!(":version/", "") if version path.gsub!(/\(?:(\w+)\)?/) {|m| "_#{m[1..-1]}_" } tags = self.tags.map do |tag| case tag when String then tag when Proc then tag.call(endpoint) end end tags.push "method:#{method}" tags.push "path:#{path}" tags.push "version:#{version}" if version tags.push "status:#{endpoint.status}" tags.compact! statsd.increment metric_name, :tags => tags statsd.timing "#{metric_name}.time", ms, :tags => tags end end end