Class: StatsdMiddleware

Inherits:
Object
  • Object
show all
Defined in:
lib/statsd_middleware.rb

Constant Summary collapse

STATUS_KEY =
'api.rack.request'
DURATION_KEY =
'api.rack.request.duration'

Instance Method Summary collapse

Constructor Details

#initialize(app) ⇒ StatsdMiddleware

Returns a new instance of StatsdMiddleware.



7
8
9
# File 'lib/statsd_middleware.rb', line 7

def initialize(app)
  @app = app
end

Instance Method Details

#call(env) ⇒ Object



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/statsd_middleware.rb', line 11

def call(env)
  start_time = Time.current
  status, headers, response = @app.call(env)
  duration = (Time.current - start_time) * 1000.0

  path_parameters = env['action_dispatch.request.path_parameters']

  # When ActionDispatch middleware is not processed, as is the case when middleware
  # such as Rack::Attack halts the call chain while applying a rate limit, path
  # parameters are not parsed. In this case, we don't have a controller or action
  # for the request.
  #
  # We should never use a dynamic path to apply the tag for the instrumentation,
  # since this will permit a rogue actor to increase the number of time series
  # exported from the process and causes instability in the metrics system. Effort
  # should be taken to track known conditions carefully in alternate metrics. For
  # the case of Rack::Attack rate limits, we can track the number of 429s responses
  # based on component at the reverse proxy layer, or with instrumentation provided
  # by the Rack::Attack middleware (which performs some rudimentary path matching)

  if path_parameters
    controller = path_parameters[:controller]
    action = path_parameters[:action]

    instrument_statsd(status, duration, controller, action, env['SOURCE_APP'])
  end

  [status, headers, response]
end

#instrument_statsd(status, duration, controller, action, source_app) ⇒ Object (private)



43
44
45
46
47
48
49
50
51
# File 'lib/statsd_middleware.rb', line 43

def instrument_statsd(status, duration, controller, action, source_app)
  duration_tags = ["controller:#{controller}", "action:#{action}", "source_app:#{source_app}"]
  status_tags = duration_tags + ["status:#{status}"]

  # rubocop:disable Style/RescueModifier
  StatsD.increment(STATUS_KEY, tags: status_tags) rescue nil
  StatsD.measure(DURATION_KEY, duration, tags: duration_tags) rescue nil
  # rubocop:enable Style/RescueModifier
end