class Telegraf::Rack

Telegraf::Rack

This rack middleware collects request metrics and sends them to the telegraf agent. A `Point` data structure is added to the Rack environment to assign custom tags and values. This point can be accessed using the environment key defined in `::Telegraf::Rack::FIELD_NAME`.

Example:

if (point = request.env[::Telegraf::Rack::FIELD_NAME])
  point.tags[:tag] = 'tag'
  point.values[:value] = 10
end

Tags:

Values:

Constants

FIELD_NAME
HEADER_REGEX
Point

Warning: `:values` member overrides `Struct#values` and it may be unexpected, but nothing we can change here as this is an import public API right now.

rubocop:disable Lint/StructNewOverride

Public Class Methods

new(app, agent:, series: 'rack', tags: {}, logger: nil) click to toggle source

rubocop:enable Lint/StructNewOverride

# File lib/telegraf/rack.rb, line 55
def initialize(app, agent:, series: 'rack', tags: {}, logger: nil)
  @app = app
  @tags = tags.freeze
  @agent = agent
  @series = series.to_s.freeze
  @logger = logger
end

Public Instance Methods

call(env) click to toggle source
# File lib/telegraf/rack.rb, line 63
def call(env)
  if (request_start = extract_request_start(env))
    queue_ms = (::Time.now.utc - request_start) * 1000 # milliseconds
  end

  rack_start = ::Rack::Utils.clock_time
  point = env[FIELD_NAME] = Point.new(@tags.dup, {})
  point.values[:queue_ms] = queue_ms if queue_ms

  begin
    begin
      status, headers, body = @app.call(env)
    ensure
      point.tags[:status] ||= status || -1
      point.values[:app_ms] = \
        (::Rack::Utils.clock_time - rack_start) * 1000 # milliseconds
    end

    send_start = ::Rack::Utils.clock_time
    proxy = ::Rack::BodyProxy.new(body) do
      point.values[:send_ms] = \
        (::Rack::Utils.clock_time - send_start) * 1000 # milliseconds

      finish(env, point, rack_start)
    end

    [status, headers, proxy]
  ensure
    finish(env, point, rack_start) unless proxy
  end
end

Private Instance Methods

extract_request_start(env) click to toggle source
# File lib/telegraf/rack.rb, line 106
def extract_request_start(env)
  return unless env.key?('HTTP_X_REQUEST_START')

  if (m = HEADER_REGEX.match(env['HTTP_X_REQUEST_START']))
    ::Time.at(m[1].to_f).utc
  end
rescue FloatDomainError
  # Ignore obscure floats in Time.at (e.g. infinity)
  false
end
finish(env, point, rack_start) click to toggle source
# File lib/telegraf/rack.rb, line 97
def finish(env, point, rack_start)
  point.values[:request_ms] = \
    (::Rack::Utils.clock_time - rack_start) * 1000 # milliseconds

  @agent.write(@series, tags: point.tags, values: point.values)
rescue StandardError => e
  (@logger || env[::Rack::RACK_LOGGER])&.error(e)
end