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.
Constant Summary collapse
- CLOUDWISE_JS_ENABLED =
✅ 优化:缓存 ENV 配置为常量,避免每次请求读取操作系统环境变量 rubocop:disable CustomCops/EnvUsageCop
ENV.fetch('CLOUDWISE_JS_CONFIG', 'false') == 'true'
Instance Method Summary collapse
- #call(env) ⇒ Object
-
#extract_request_id(cloudwise_info) ⇒ Object
从 CLOUDWISEREQUESTINFO 中提取 request_id.
-
#initialize(app) ⇒ TraceMiddleware
constructor
rubocop:enable CustomCops/EnvUsageCop.
-
#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
rubocop:enable CustomCops/EnvUsageCop
36 37 38 |
# File 'lib/datadog/tracing/contrib/rack/middlewares.rb', line 36 def initialize(app) @app = app end |
Instance Method Details
#call(env) ⇒ Object
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 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 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 |
# File 'lib/datadog/tracing/contrib/rack/middlewares.rb', line 40 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) if trace_digest end # 需要在创建 span 之前提取信息 request_headers = Header::RequestHeaderCollection.new(env) # 处理 CLOUDWISEREQUESTINFO(前端调用) cloudwise_info = request_headers.get('CLOUDWISEREQUESTINFO') request_id = nil if cloudwise_info # 提取 request_id request_id = extract_request_id(cloudwise_info) if request_id Datadog.logger.debug do "Extracted request_id from CLOUDWISEREQUESTINFO: #{request_id}" end end end # 处理 CLOUDWISE(外部服务调用,如 Java/PHP) cloudwise_header = request_headers.get('CLOUDWISE') 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 # 如果存在 request_id,将其作为 span 的 cwsa_trace 字段 if request_id request_span.set_tag('cwsa_trace', request_id) Datadog.logger.debug do "Set cwsa_trace tag on span: #{request_id}" end end # 如果存在 CLOUDWISE 头(外部服务调用),提取并设置字段到 span if cloudwise_header require_relative '../cloudwise/propagation' Cloudwise::Propagation.extract_and_tag_from_header!(request_span, cloudwise_header) end # 提取 CLOUDWISE-OTHER 头并添加到根 span require_relative '../cloudwise/propagation' Cloudwise::Propagation.extract_other_from_request!(request_span, request_headers) # 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) # 如果存在 cloudwise_info,在响应头中添加 CLOUDWISETRACE if cloudwise_info headers ||= {} headers['CLOUDWISETRACE'] = 'true' Datadog.logger.debug do "Added CLOUDWISETRACE response header" end end # 如果启用了 CLOUDWISE_JS_CONFIG,在响应头中添加 CLOUDWISE(用于 RUM 追踪) # 默认关闭,可通过环境变量 CLOUDWISE_JS_CONFIG=true 开启 # 使用常量替代每次 ENV 读取 if CLOUDWISE_JS_ENABLED headers ||= {} require_relative '../cloudwise/propagation' # 构建 CLOUDWISE 响应头值 # Only add CLOUDWISE header if Cloudwise is active (not suspended) service_name = Datadog.configuration.service if service_name && request_span cloudwise_value = Cloudwise::Propagation.build_cloudwise_value( span: request_span, trace: request_trace, service_name: service_name, target_url: nil # 响应头不需要 target_url ) headers['CLOUDWISE'] = cloudwise_value Datadog.logger.debug do "Added CLOUDWISE response header for RUM: #{cloudwise_value}" end end end [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) 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 |
#extract_request_id(cloudwise_info) ⇒ Object
从 CLOUDWISEREQUESTINFO 中提取 request_id
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
# File 'lib/datadog/tracing/contrib/rack/middlewares.rb', line 184 def extract_request_id(cloudwise_info) return nil unless cloudwise_info.is_a?(String) # 使用正则表达式提取:第一个 _ 之后,@SDK 之前的内容(不区分大小写) match = cloudwise_info.match(/_([^_@]+)@SDK/i) if match && match[1] request_id = match[1] return request_id else Datadog.logger.debug do "Could not extract request_id from CLOUDWISEREQUESTINFO: #{cloudwise_info}" end end nil rescue => e Datadog.logger.error do "Error extracting request_id from CLOUDWISEREQUESTINFO: #{e.message}" end nil 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
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 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
# File 'lib/datadog/tracing/contrib/rack/middlewares.rb', line 211 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) (trace: trace, request_span: request_span, status: status, env: env) # 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 if request_span.status.zero? && Datadog.configuration.tracing.http_error_statuses.server.include?(status) request_span.status = Tracing::Metadata::Ext::Errors::STATUS end end |