Class: Datadog::Contrib::Rack::TraceMiddleware

Inherits:
Object
  • Object
show all
Defined in:
lib/ddtrace/contrib/rack/middlewares.rb

Overview

TraceMiddleware ensures that the Rack Request is properly traced from the beginning to the end. The middleware adds the request span in the Rack environment so that it can be retrieved by the underlying application. If request tags are not set by the app, they will be set using information available at the Rack level.

Constant Summary collapse

DEFAULT_CONFIG =
{
  tracer: Datadog.tracer,
  default_service: 'rack'
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(app, options = {}) ⇒ TraceMiddleware

Returns a new instance of TraceMiddleware.



20
21
22
23
24
25
26
27
# File 'lib/ddtrace/contrib/rack/middlewares.rb', line 20

def initialize(app, options = {})
  # update options with our configuration, unless it's already available
  options[:tracer] ||= DEFAULT_CONFIG[:tracer]
  options[:default_service] ||= DEFAULT_CONFIG[:default_service]

  @app = app
  @options = options
end

Instance Method Details

#call(env) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/ddtrace/contrib/rack/middlewares.rb', line 45

def call(env)
  # configure the Rack middleware once
  configure()

  # start a new request span and attach it to the current Rack environment;
  # we must ensure that the span `resource` is set later
  request_span = @tracer.trace(
    'rack.request',
    service: @service,
    resource: nil,
    span_type: Datadog::Ext::HTTP::TYPE
  )
  env[:datadog_rack_request_span] = request_span

  # call the rest of the stack
  status, headers, response = @app.call(env)
rescue StandardError => e
  # catch exceptions that may be raised in the middleware chain
  # Note: if a middleware catches an Exception without re raising,
  # the Exception cannot be recorded here
  request_span.set_error(e)
  raise e
ensure
  # the source of truth in Rack is the PATH_INFO key that holds the
  # URL for the current request; some framework may override that
  # value, especially during exception handling and because of that
  # we prefer using the `REQUEST_URI` if this is available.
  # NOTE: `REQUEST_URI` is Rails specific and may not apply for other frameworks
  url = env['REQUEST_URI'] || env['PATH_INFO']

  # Rack is a really low level interface and it doesn't provide any
  # advanced functionality like routers. Because of that, we assume that
  # the underlying framework or application has more knowledge about
  # the result for this request; `resource` and `tags` are expected to
  # be set in another level but if they're missing, reasonable defaults
  # are used.
  request_span.resource = "#{env['REQUEST_METHOD']} #{status}".strip unless request_span.resource
  request_span.set_tag('http.method', env['REQUEST_METHOD']) if request_span.get_tag('http.method').nil?
  request_span.set_tag('http.url', url) if request_span.get_tag('http.url').nil?
  request_span.set_tag('http.status_code', status) if request_span.get_tag('http.status_code').nil? && status

  # detect if the status code is a 5xx and flag the request span as an error
  # unless it has been already set by the underlying framework
  if status.to_s.start_with?('5') && request_span.status.zero?
    request_span.status = 1
    # in any case we don't touch the stacktrace if it has been set
    if request_span.get_tag(Datadog::Ext::Errors::STACK).nil?
      request_span.set_tag(Datadog::Ext::Errors::STACK, caller().join("\n"))
    end
  end

  request_span.finish()

  [status, headers, response]
end

#configureObject



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/ddtrace/contrib/rack/middlewares.rb', line 29

def configure
  # ensure that the configuration is executed only once
  return if @tracer && @service

  # retrieve the current tracer and service
  @tracer = @options.fetch(:tracer)
  @service = @options.fetch(:default_service)

  # configure the Rack service
  @tracer.set_service_info(
    @service,
    'rack',
    Datadog::Ext::AppTypes::WEB
  )
end