Class: Datadog::Tracing::Contrib::Rack::TraceMiddleware
- Inherits:
-
Object
- Object
- Datadog::Tracing::Contrib::Rack::TraceMiddleware
- Defined in:
- lib/datadog/tracing/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.
Instance Method Summary collapse
- #call(env) ⇒ Object
- #compute_queue_time(env) ⇒ Object
-
#initialize(app) ⇒ TraceMiddleware
constructor
A new instance of TraceMiddleware.
-
#set_request_tags!(trace, request_span, env, status, headers, response, original_env) ⇒ Object
rubocop:disable Metrics/AbcSize rubocop:disable Metrics/CyclomaticComplexity rubocop:disable Metrics/PerceivedComplexity rubocop:disable Metrics/MethodLength.
Constructor Details
#initialize(app) ⇒ TraceMiddleware
Returns a new instance of TraceMiddleware.
28 29 30 |
# File 'lib/datadog/tracing/contrib/rack/middlewares.rb', line 28 def initialize(app) @app = app end |
Instance Method Details
#call(env) ⇒ Object
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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/datadog/tracing/contrib/rack/middlewares.rb', line 69 def call(env) # Find out if this is rack within rack previous_request_span = env[Ext::RACK_ENV_REQUEST_SPAN] return @app.call(env) if previous_request_span boot = Datadog::Core::Remote::Tie.boot # Extract distributed tracing context before creating any spans, # so that all spans will be added to the distributed trace. if configuration[:distributed_tracing] trace_digest = Tracing::Propagation::HTTP.extract(env) Tracing.continue_trace!(trace_digest) end # Create a root Span to keep track of frontend web servers # (i.e. Apache, nginx) if the header is properly set frontend_span = compute_queue_time(env) = { span_type: Tracing::Metadata::Ext::HTTP::TYPE_INBOUND } [:service] = configuration[:service_name] if configuration[:service_name] # 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 = Tracing.trace(Ext::SPAN_REQUEST, **) request_span.resource = nil # When tracing and distributed tracing are both disabled, `.active_trace` will be `nil`, # Return a null object to continue operation request_trace = Tracing.active_trace || TraceOperation.new env[Ext::RACK_ENV_REQUEST_SPAN] = request_span Datadog::Core::Remote::Tie::Tracing.tag(boot, request_span) # Copy the original env, before the rest of the stack executes. # Values may change; we want values before that happens. original_env = env.dup # call the rest of the stack status, headers, response = @app.call(env) [status, headers, response] # rubocop:disable Lint/RescueException # Here we really want to catch *any* exception, not only StandardError, # as we really have no clue of what is in the block, # and it is user code which should be executed no matter what. # It's not a problem since we re-raise it afterwards so for example a # SignalException::Interrupt would still bubble up. rescue Exception => 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) unless request_span.nil? raise e ensure env[Ext::RACK_ENV_REQUEST_SPAN] = previous_request_span if previous_request_span if request_span # 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_trace, request_span, env, status, headers, response, original_env || env) # ensure the request_span is finished and the context reset; # this assumes that the Rack middleware creates a root span request_span.finish end frontend_span.finish if frontend_span end |
#compute_queue_time(env) ⇒ Object
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/datadog/tracing/contrib/rack/middlewares.rb', line 32 def compute_queue_time(env) return unless configuration[:request_queuing] # parse the request queue time request_start = Contrib::Rack::QueueTime.get_request_start(env) return if request_start.nil? case configuration[:request_queuing] when true, :include_request # DEV: Switch `true` to `:exclude_request` in v2.0 queue_span = trace_http_server(Ext::SPAN_HTTP_SERVER_QUEUE, start_time: request_start) # Tag this span as belonging to Rack queue_span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT) queue_span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_HTTP_SERVER_QUEUE) queue_span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_SERVER) queue_span when :exclude_request request_span = trace_http_server(Ext::SPAN_HTTP_PROXY_REQUEST, start_time: request_start) request_span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT_HTTP_PROXY) request_span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_HTTP_PROXY_REQUEST) request_span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_PROXY) queue_span = trace_http_server(Ext::SPAN_HTTP_PROXY_QUEUE, start_time: request_start) # Measure service stats Contrib::Analytics.set_measured(queue_span) queue_span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT_HTTP_PROXY) queue_span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_HTTP_PROXY_QUEUE) queue_span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_PROXY) # finish the `queue` span now to record only the time spent *in queue*, # excluding the time spent processing the request itself queue_span.finish request_span end end |
#set_request_tags!(trace, request_span, env, status, headers, response, original_env) ⇒ Object
rubocop:disable Metrics/AbcSize rubocop:disable Metrics/CyclomaticComplexity rubocop:disable Metrics/PerceivedComplexity rubocop:disable Metrics/MethodLength
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/datadog/tracing/contrib/rack/middlewares.rb', line 149 def (trace, request_span, env, status, headers, response, original_env) request_header_collection = Header::RequestHeaderCollection.new(env) # Since it could be mutated, it would be more accurate to fetch from the original env, # e.g. ActionDispatch::ShowExceptions middleware with Rails exceptions_app configuration original_request_method = original_env['REQUEST_METHOD'] # request_headers is subject to filtering and configuration so we # get the user agent separately user_agent = parse_user_agent_header(request_header_collection) # The priority # 1. User overrides span.resource # 2. Configuration # 3. Nested App override trace.resource # 4. Fallback with verb + status, eq `GET 200` request_span.resource ||= if configuration[:middleware_names] && env['RESPONSE_MIDDLEWARE'] "#{env['RESPONSE_MIDDLEWARE']}##{original_request_method}" elsif trace.resource_override? trace.resource else "#{original_request_method} #{status}".strip end # Overrides the trace resource if it never been set # Otherwise, the getter method would delegate to its root span trace.resource = request_span.resource unless trace.resource_override? request_span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT) request_span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_REQUEST) request_span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_SERVER) # Set analytics sample rate if Contrib::Analytics.enabled?(configuration[:analytics_enabled]) Contrib::Analytics.set_sample_rate(request_span, configuration[:analytics_sample_rate]) end # Measure service stats Contrib::Analytics.set_measured(request_span) if request_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_METHOD).nil? request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_METHOD, original_request_method) end url = parse_url(env, original_env) if request_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_URL).nil? = configuration[:quantize] || {} # Quantization::HTTP.url base defaults to :show, but we are transitioning [:base] ||= :exclude request_span.set_tag( Tracing::Metadata::Ext::HTTP::TAG_URL, Contrib::Utils::Quantization::HTTP.url(url, ) ) end if request_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_BASE_URL).nil? = configuration[:quantize] unless [:base] == :show base_url = Contrib::Utils::Quantization::HTTP.base_url(url) unless base_url.empty? request_span.set_tag( Tracing::Metadata::Ext::HTTP::TAG_BASE_URL, base_url ) end end end if request_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_CLIENT_IP).nil? Tracing::ClientIp.set_client_ip_tag( request_span, headers: request_header_collection, remote_ip: env['REMOTE_ADDR'] ) end if request_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE).nil? && status request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE, status) end if request_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_USER_AGENT).nil? && user_agent request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_USER_AGENT, user_agent) end HeaderTagging.tag_request_headers(request_span, request_header_collection, configuration) HeaderTagging.tag_response_headers(request_span, headers, configuration) if headers # 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 request_span.status = 1 if status.to_s.start_with?('5') && request_span.status.zero? end |