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
-
#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.
30 31 32 |
# File 'lib/datadog/tracing/contrib/rack/middlewares.rb', line 30 def initialize(app) @app = app end |
Instance Method Details
#call(env) ⇒ Object
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 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 100 101 102 |
# File 'lib/datadog/tracing/contrib/rack/middlewares.rb', line 34 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 = Contrib::HTTP.extract(env) Tracing.continue_trace!(trace_digest) end TraceProxyMiddleware.call(env, configuration) do = { 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] # 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 # rubocop:disable Lint/RescueException # 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 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
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 143 144 145 146 147 148 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 |
# File 'lib/datadog/tracing/contrib/rack/middlewares.rb', line 108 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) if status != 404 && (last_route = trace.get_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE)) last_script_name = trace.get_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH) || '' # If the last_script_name is empty but the env['SCRIPT_NAME'] is NOT empty # then the current rack request was not routed and must be accounted for # which only happens in pure nested rack requests i.e /rack/rack/hello/world # # To account for the unaccounted nested rack requests of /rack/hello/world, # we use 'PATH_INFO knowing that rack cannot have named parameters if last_script_name == '' && env['SCRIPT_NAME'] && env['SCRIPT_NAME'] != '' last_script_name = last_route last_route = env['PATH_INFO'] end # Clear the route and route path tags from the request trace to avoid possibility of misplacement trace.clear_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE) trace.clear_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH) # Ensure tags are placed in rack.request span as desired request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE, last_script_name + last_route) request_span.clear_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE_PATH) end # 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 if request_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE).nil? && status != 404 request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE, env['PATH_INFO']) 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 |