datadog 2.32.0 → 2.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -1
  3. data/ext/datadog_profiling_native_extension/clock_id.h +9 -1
  4. data/ext/datadog_profiling_native_extension/clock_id_from_mach.c +73 -0
  5. data/ext/datadog_profiling_native_extension/clock_id_from_pthread.c +1 -1
  6. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +20 -0
  7. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +5 -1
  8. data/ext/datadog_profiling_native_extension/extconf.rb +3 -0
  9. data/ext/datadog_profiling_native_extension/macos_sampler_thread.h +55 -0
  10. data/ext/datadog_profiling_native_extension/stack_recorder.c +3 -9
  11. data/ext/datadog_profiling_native_extension/time_helpers.h +1 -0
  12. data/ext/libdatadog_api/crashtracker.c +2 -0
  13. data/ext/libdatadog_extconf_helpers.rb +1 -1
  14. data/lib/datadog/ai_guard/autoload.rb +10 -0
  15. data/lib/datadog/ai_guard/component.rb +1 -1
  16. data/lib/datadog/ai_guard/contrib/auto_instrument.rb +24 -0
  17. data/lib/datadog/ai_guard/contrib/rack/integration.rb +42 -0
  18. data/lib/datadog/ai_guard/contrib/rack/patcher.rb +26 -0
  19. data/lib/datadog/ai_guard/contrib/rack/request_middleware.rb +83 -0
  20. data/lib/datadog/ai_guard/contrib/rails/integration.rb +41 -0
  21. data/lib/datadog/ai_guard/contrib/rails/patcher.rb +97 -0
  22. data/lib/datadog/ai_guard/evaluation.rb +1 -0
  23. data/lib/datadog/ai_guard/ext.rb +1 -0
  24. data/lib/datadog/ai_guard.rb +8 -0
  25. data/lib/datadog/appsec/component.rb +4 -1
  26. data/lib/datadog/appsec/compressed_json.rb +2 -2
  27. data/lib/datadog/appsec/contrib/aws_lambda/gateway/watcher.rb +75 -0
  28. data/lib/datadog/appsec/contrib/aws_lambda/integration.rb +39 -0
  29. data/lib/datadog/appsec/contrib/aws_lambda/patcher.rb +30 -0
  30. data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +111 -0
  31. data/lib/datadog/appsec/contrib/rack/ext.rb +1 -1
  32. data/lib/datadog/appsec.rb +1 -0
  33. data/lib/datadog/core/configuration/components.rb +8 -1
  34. data/lib/datadog/core/configuration/settings.rb +16 -1
  35. data/lib/datadog/core/configuration/supported_configurations.rb +12 -0
  36. data/lib/datadog/core/environment/ext.rb +5 -0
  37. data/lib/datadog/core/environment/identity.rb +15 -1
  38. data/lib/datadog/core/environment/process.rb +48 -27
  39. data/lib/datadog/core/environment/socket.rb +13 -0
  40. data/lib/datadog/core/remote/client/capabilities.rb +11 -2
  41. data/lib/datadog/core/remote/transport/http/config.rb +5 -5
  42. data/lib/datadog/core/telemetry/request.rb +0 -2
  43. data/lib/datadog/core/transport/response.rb +1 -1
  44. data/lib/datadog/core/utils/{base64.rb → base64_codec.rb} +3 -2
  45. data/lib/datadog/core/utils/hash.rb +0 -23
  46. data/lib/datadog/core/utils/spawn_monkey_patch.rb +46 -16
  47. data/lib/datadog/data_streams/pathway_context.rb +3 -3
  48. data/lib/datadog/di/code_tracker.rb +43 -22
  49. data/lib/datadog/di/contrib/active_record.rb +6 -2
  50. data/lib/datadog/di/instrumenter.rb +24 -4
  51. data/lib/datadog/di/probe_notification_builder.rb +1 -1
  52. data/lib/datadog/di/remote.rb +4 -4
  53. data/lib/datadog/di/serializer.rb +5 -5
  54. data/lib/datadog/di/utils.rb +42 -14
  55. data/lib/datadog/opentelemetry/configuration/settings.rb +65 -0
  56. data/lib/datadog/opentelemetry/ext.rb +9 -0
  57. data/lib/datadog/opentelemetry/logs.rb +98 -0
  58. data/lib/datadog/opentelemetry/metrics.rb +10 -37
  59. data/lib/datadog/opentelemetry/sdk/configurator.rb +40 -0
  60. data/lib/datadog/opentelemetry/sdk/id_generator.rb +16 -10
  61. data/lib/datadog/opentelemetry/sdk/logs_exporter.rb +37 -0
  62. data/lib/datadog/opentelemetry/signal_configuration.rb +53 -0
  63. data/lib/datadog/opentelemetry.rb +1 -0
  64. data/lib/datadog/profiling/component.rb +0 -1
  65. data/lib/datadog/profiling/stack_recorder.rb +0 -4
  66. data/lib/datadog/symbol_database/component.rb +409 -0
  67. data/lib/datadog/symbol_database/configuration.rb +2 -2
  68. data/lib/datadog/symbol_database/extractor.rb +45 -26
  69. data/lib/datadog/symbol_database/remote.rb +175 -0
  70. data/lib/datadog/symbol_database/scope.rb +16 -12
  71. data/lib/datadog/symbol_database/scope_batcher.rb +288 -0
  72. data/lib/datadog/symbol_database/service_version.rb +15 -6
  73. data/lib/datadog/symbol_database/symbol.rb +6 -3
  74. data/lib/datadog/symbol_database/uploader.rb +65 -8
  75. data/lib/datadog/tracing/contrib/action_pack/action_dispatch/instrumentation.rb +8 -0
  76. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +0 -4
  77. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +0 -4
  78. data/lib/datadog/tracing/contrib/active_support/cache/instrumentation.rb +0 -4
  79. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +0 -5
  80. data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +0 -5
  81. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +0 -5
  82. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +0 -5
  83. data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +0 -8
  84. data/lib/datadog/tracing/contrib/excon/middleware.rb +0 -5
  85. data/lib/datadog/tracing/contrib/ext.rb +2 -3
  86. data/lib/datadog/tracing/contrib/faraday/middleware.rb +0 -5
  87. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +0 -5
  88. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +0 -5
  89. data/lib/datadog/tracing/contrib/http/instrumentation.rb +0 -5
  90. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +0 -5
  91. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +0 -5
  92. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +0 -5
  93. data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +0 -5
  94. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +0 -5
  95. data/lib/datadog/tracing/contrib/pg/instrumentation.rb +0 -5
  96. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +0 -5
  97. data/lib/datadog/tracing/contrib/racecar/event.rb +0 -5
  98. data/lib/datadog/tracing/contrib/rack/configuration/settings.rb +6 -0
  99. data/lib/datadog/tracing/contrib/rack/ext.rb +27 -0
  100. data/lib/datadog/tracing/contrib/rack/trace_proxy_middleware.rb +117 -1
  101. data/lib/datadog/tracing/contrib/redis/tags.rb +0 -5
  102. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +0 -5
  103. data/lib/datadog/tracing/contrib/sequel/utils.rb +0 -5
  104. data/lib/datadog/tracing/contrib/trilogy/instrumentation.rb +0 -5
  105. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +0 -13
  106. data/lib/datadog/tracing/distributed/trace_context.rb +0 -28
  107. data/lib/datadog/tracing/metadata/ext.rb +3 -0
  108. data/lib/datadog/tracing/span_operation.rb +13 -0
  109. data/lib/datadog/tracing/trace_operation.rb +22 -0
  110. data/lib/datadog/tracing/tracer.rb +7 -3
  111. data/lib/datadog/version.rb +1 -1
  112. metadata +27 -8
  113. data/ext/datadog_profiling_native_extension/clock_id_noop.c +0 -21
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
+ require 'securerandom'
4
5
  require 'zlib'
5
6
  require 'stringio'
6
7
  require_relative '../core/environment/identity'
@@ -45,6 +46,12 @@ module Datadog
45
46
  agent_settings: agent_settings,
46
47
  logger: @logger,
47
48
  )
49
+
50
+ # Protects the lazy initialization of @upload_pid/@upload_id and the
51
+ # @batch_num increment in next_upload_metadata. ScopeBatcher calls
52
+ # upload_scopes outside its own mutex, so two threads (e.g. a size-
53
+ # triggered flush and the timer/shutdown path) can race here.
54
+ @metadata_mutex = Mutex.new
48
55
  end
49
56
 
50
57
  # Upload a batch of scopes to the agent.
@@ -56,9 +63,13 @@ module Datadog
56
63
  def upload_scopes(scopes)
57
64
  return if scopes.empty?
58
65
 
59
- json_data = build_symbol_payload(scopes)
66
+ upload_id, batch_num = next_upload_metadata
67
+ json_data = build_symbol_payload(scopes, upload_id: upload_id, batch_num: batch_num)
60
68
  compressed_data = Zlib.gzip(json_data)
61
69
 
70
+ # Emitted unconditionally so the rare oversized case is observable.
71
+ @telemetry&.distribution('tracers', 'symbol_database.payload_size', compressed_data.bytesize)
72
+
62
73
  # Symbols for very large applications (>50MB after gzip) are dropped:
63
74
  # the upload is skipped and the customer sees no autocomplete /
64
75
  # symbol probe results for those classes. Java handles the same case
@@ -69,7 +80,7 @@ module Datadog
69
80
  return
70
81
  end
71
82
 
72
- perform_http_upload(compressed_data, scopes.size)
83
+ perform_http_upload(compressed_data, scopes.size, upload_id: upload_id, batch_num: batch_num)
73
84
  rescue => e
74
85
  @logger.debug { "symdb: upload failed: #{e.class}: #{e.message}" }
75
86
  @telemetry&.report(e, description: 'symdb: upload failed')
@@ -79,31 +90,44 @@ module Datadog
79
90
 
80
91
  # Build JSON payload from scopes.
81
92
  # @param scopes [Array<Scope>] Scopes to serialize
93
+ # @param upload_id [String] UUID identifying the logical upload
94
+ # @param batch_num [Integer] 1-indexed batch number within the upload
82
95
  # @return [String] JSON string
83
- def build_symbol_payload(scopes)
96
+ def build_symbol_payload(scopes, upload_id:, batch_num:)
84
97
  ServiceVersion.new(
85
98
  service: @settings.service,
86
99
  env: @settings.env,
87
100
  version: @settings.version,
88
101
  scopes: scopes,
102
+ upload_id: upload_id,
103
+ batch_num: batch_num,
104
+ # Always false: the Ruby tracer continuously uploads new code
105
+ # as files are loaded; there is no defined end-of-upload point.
106
+ final: false,
89
107
  ).to_json
90
108
  end
91
109
 
92
110
  # Perform HTTP POST with multipart form-data via transport layer.
93
111
  # @param compressed_data [String] GZIP compressed JSON payload
94
112
  # @param scope_count [Integer] Number of scopes (for logging)
113
+ # @param upload_id [String] UUID identifying the logical upload
114
+ # @param batch_num [Integer] 1-indexed batch number within the upload
95
115
  # @return [void]
96
- def perform_http_upload(compressed_data, scope_count)
97
- form = build_multipart_form(compressed_data)
116
+ def perform_http_upload(compressed_data, scope_count, upload_id:, batch_num:)
117
+ form = build_multipart_form(compressed_data, upload_id: upload_id, batch_num: batch_num)
98
118
  response = @transport.send_symbols(form)
99
119
  handle_response(response, scope_count)
100
120
  end
101
121
 
102
122
  # Build multipart form-data with event metadata and compressed symbols.
103
123
  # @param compressed_data [String] GZIP compressed JSON payload
124
+ # @param upload_id [String] UUID identifying the logical upload
125
+ # @param batch_num [Integer] 1-indexed batch number within the upload
104
126
  # @return [Hash] Form data hash with UploadIO objects
105
- def build_multipart_form(compressed_data)
106
- event_io = StringIO.new(build_event_metadata)
127
+ def build_multipart_form(compressed_data, upload_id:, batch_num:)
128
+ event_io = StringIO.new(
129
+ build_event_metadata(compressed_data.bytesize, upload_id: upload_id, batch_num: batch_num),
130
+ )
107
131
  file_io = StringIO.new(compressed_data)
108
132
 
109
133
  event_upload = Datadog::Core::Vendor::Multipart::Post::UploadIO.new(
@@ -125,17 +149,50 @@ module Datadog
125
149
  end
126
150
 
127
151
  # Build event.json metadata part.
152
+ # @param attachment_size [Integer] Size of the compressed attachment in bytes
153
+ # @param upload_id [String] UUID identifying the logical upload
154
+ # @param batch_num [Integer] 1-indexed batch number within the upload
128
155
  # @return [String] JSON string for event metadata
129
- def build_event_metadata
156
+ def build_event_metadata(attachment_size, upload_id:, batch_num:)
130
157
  JSON.generate(
131
158
  ddsource: 'ruby',
132
159
  service: @settings.service,
160
+ version: @settings.version,
161
+ language: 'ruby',
133
162
  runtimeId: Datadog::Core::Environment::Identity.id,
134
163
  parentId: nil, # Fork tracking deferred for MVP
135
164
  type: 'symdb',
165
+ uploadId: upload_id,
166
+ batchNum: batch_num,
167
+ # Always false: the Ruby tracer continuously uploads new code
168
+ # as files are loaded; there is no defined end-of-upload point.
169
+ final: false,
170
+ attachmentSize: attachment_size,
136
171
  )
137
172
  end
138
173
 
174
+ # Return [upload_id, batch_num] for the current process. upload_id is
175
+ # generated lazily on first use and shared by all batches uploaded from
176
+ # the process. A forked child observes a different Process.pid and
177
+ # therefore gets a fresh upload_id and batch counter.
178
+ # Synchronized: ScopeBatcher releases its mutex before calling
179
+ # upload_scopes, so this can be entered concurrently.
180
+ # @return [Array<(String, Integer)>]
181
+ def next_upload_metadata
182
+ @metadata_mutex.synchronize do
183
+ if @upload_pid != Process.pid
184
+ @upload_pid = Process.pid
185
+ @upload_id = SecureRandom.uuid.freeze
186
+ @batch_num = 0
187
+ end
188
+ @batch_num += 1 # steep:ignore NoMethod
189
+ # @upload_id and @batch_num are non-nil after the branch above; refine for Steep.
190
+ upload_id = @upload_id or raise('unreachable: @upload_id should be set')
191
+ batch_num = @batch_num or raise('unreachable: @batch_num should be set')
192
+ [upload_id, batch_num]
193
+ end
194
+ end
195
+
139
196
  # Handle HTTP response and track metrics.
140
197
  # @param response [Core::Transport::Response] HTTP response from agent
141
198
  # @param scope_count [Integer] Number of scopes uploaded
@@ -11,6 +11,8 @@ module Datadog
11
11
  module Instrumentation
12
12
  SCRIPT_NAME_KEY = 'SCRIPT_NAME'
13
13
  FORMAT_SUFFIX = '(.:format)'
14
+ # NOTE: Rails 8.1.1+ natively provides the same object via 'action_dispatch.route'.
15
+ DATADOG_RAILS_ROUTE_ENV_KEY = 'datadog.action_dispatch.route'
14
16
 
15
17
  module_function
16
18
 
@@ -48,6 +50,8 @@ module Datadog
48
50
  result.each do |_, _, route|
49
51
  next unless Instrumentation.dispatcher_route?(route)
50
52
 
53
+ req.env[DATADOG_RAILS_ROUTE_ENV_KEY] = route
54
+
51
55
  http_route = route.path.spec.to_s
52
56
  http_route.delete_suffix!(FORMAT_SUFFIX)
53
57
 
@@ -66,6 +70,8 @@ module Datadog
66
70
  def find_routes(req)
67
71
  super do |match, parameters, route|
68
72
  if Instrumentation.dispatcher_route?(route)
73
+ req.env[DATADOG_RAILS_ROUTE_ENV_KEY] = route
74
+
69
75
  http_route = route.path.spec.to_s
70
76
  http_route.delete_suffix!(FORMAT_SUFFIX)
71
77
 
@@ -86,6 +92,8 @@ module Datadog
86
92
 
87
93
  super do |route, parameters|
88
94
  if Instrumentation.dispatcher_route?(route)
95
+ req.env[DATADOG_RAILS_ROUTE_ENV_KEY] = route
96
+
89
97
  http_route = route.path.spec.to_s
90
98
  http_route = http_route.delete_suffix(FORMAT_SUFFIX)
91
99
 
@@ -48,10 +48,6 @@ module Datadog
48
48
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
49
49
  span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_SQL)
50
50
 
51
- if service_name != Datadog.configuration.service
52
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
53
- end
54
-
55
51
  # Set analytics sample rate
56
52
  if Contrib::Analytics.enabled?(configuration[:analytics_enabled])
57
53
  Contrib::Analytics.set_sample_rate(span, configuration[:analytics_sample_rate])
@@ -82,10 +82,6 @@ module Datadog
82
82
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
83
83
  span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_CACHE)
84
84
 
85
- if span.service != Datadog.configuration.service
86
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
87
- end
88
-
89
85
  span.set_tag(Ext::TAG_CACHE_BACKEND, cache_backend(store))
90
86
 
91
87
  span.set_tag('EVENT', event)
@@ -54,10 +54,6 @@ module Datadog
54
54
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
55
55
  span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_CACHE)
56
56
 
57
- if span.service != Datadog.configuration.service
58
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
59
- end
60
-
61
57
  span.set_tag(Ext::TAG_CACHE_BACKEND, store) if store
62
58
 
63
59
  set_cache_key(span, key, multi_key) if Datadog.configuration.tracing[:active_support][:cache_key].enabled
@@ -61,11 +61,6 @@ module Datadog
61
61
  )
62
62
  end
63
63
 
64
- # Tag original global service name if not used
65
- if span.service != Datadog.configuration.service
66
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
67
- end
68
-
69
64
  span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_CLIENT)
70
65
 
71
66
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
@@ -31,11 +31,6 @@ module Datadog
31
31
  )
32
32
  end
33
33
 
34
- # Tag original global service name if not used
35
- if span.service != Datadog.configuration.service
36
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
37
- end
38
-
39
34
  span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_CLIENT)
40
35
 
41
36
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
@@ -66,11 +66,6 @@ module Datadog
66
66
  )
67
67
  end
68
68
 
69
- # Tag original global service name if not used
70
- if span.service != Datadog.configuration.service
71
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
72
- end
73
-
74
69
  span.type = Datadog::Tracing::Contrib::Elasticsearch::Ext::SPAN_TYPE_QUERY
75
70
 
76
71
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
@@ -144,11 +144,6 @@ module Datadog
144
144
  )
145
145
  end
146
146
 
147
- # Tag original global service name if not used
148
- if span.service != Datadog.configuration.service
149
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
150
- end
151
-
152
147
  # Set analytics sample rate
153
148
  Contrib::Analytics.set_sample_rate(span, analytics_sample_rate) if analytics_enabled?
154
149
 
@@ -69,14 +69,6 @@ module Datadog
69
69
 
70
70
  @datadog_multi_span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_CLIENT)
71
71
 
72
- # Tag original global service name if not used
73
- if @datadog_multi_span.service != Datadog.configuration.service
74
- @datadog_multi_span.set_tag(
75
- Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE,
76
- Datadog.configuration.service
77
- )
78
- end
79
-
80
72
  # Set analytics sample rate
81
73
  Contrib::Analytics.set_sample_rate(@datadog_multi_span, analytics_sample_rate) if analytics_enabled?
82
74
 
@@ -128,11 +128,6 @@ module Datadog
128
128
  )
129
129
  end
130
130
 
131
- # Tag original global service name if not used
132
- if span.service != Datadog.configuration.service
133
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
134
- end
135
-
136
131
  span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_CLIENT)
137
132
 
138
133
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../metadata/ext'
4
+
3
5
  module Datadog
4
6
  module Tracing
5
7
  module Contrib
@@ -60,9 +62,6 @@ module Datadog
60
62
 
61
63
  # Value of tag from which peer.service value was remapped from
62
64
  TAG_PEER_SERVICE_REMAP = '_dd.peer.service.remapped_from'
63
-
64
- # Set equal to the global service when contrib span.service is overriden
65
- TAG_BASE_SERVICE = '_dd.base_service'
66
65
  end
67
66
  end
68
67
  end
@@ -56,11 +56,6 @@ module Datadog
56
56
  )
57
57
  end
58
58
 
59
- # Tag original global service name if not used
60
- if span.service != Datadog.configuration.service
61
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
62
- end
63
-
64
59
  span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_CLIENT)
65
60
 
66
61
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
@@ -59,11 +59,6 @@ module Datadog
59
59
  )
60
60
  end
61
61
 
62
- # Tag original global service name if not used
63
- if span.service != Datadog.configuration.service
64
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
65
- end
66
-
67
62
  span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_CLIENT)
68
63
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
69
64
  span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_CLIENT)
@@ -66,11 +66,6 @@ module Datadog
66
66
  span.set_tag(header, value)
67
67
  end
68
68
 
69
- # Tag original global service name if not used
70
- if span.service != Datadog.configuration.service
71
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
72
- end
73
-
74
69
  span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_SERVER)
75
70
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
76
71
  span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_SERVICE)
@@ -63,11 +63,6 @@ module Datadog
63
63
  )
64
64
  end
65
65
 
66
- # Tag original global service name if not used
67
- if span.service != Datadog.configuration.service
68
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
69
- end
70
-
71
66
  span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_CLIENT)
72
67
 
73
68
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
@@ -68,11 +68,6 @@ module Datadog
68
68
  )
69
69
  end
70
70
 
71
- # Tag original global service name if not used
72
- if span.service != Datadog.configuration.service
73
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
74
- end
75
-
76
71
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
77
72
  span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_REQUEST)
78
73
 
@@ -66,11 +66,6 @@ module Datadog
66
66
  )
67
67
  end
68
68
 
69
- # Tag original global service name if not used
70
- if span.service != Datadog.configuration.service
71
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
72
- end
73
-
74
69
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
75
70
  span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_REQUEST)
76
71
 
@@ -41,11 +41,6 @@ module Datadog
41
41
  )
42
42
  end
43
43
 
44
- # Tag original global service name if not used
45
- if span.service != Datadog.configuration.service
46
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
47
- end
48
-
49
44
  span.set_tag(Contrib::Ext::DB::TAG_SYSTEM, Ext::TAG_SYSTEM)
50
45
 
51
46
  span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_CLIENT)
@@ -34,11 +34,6 @@ module Datadog
34
34
  )
35
35
  end
36
36
 
37
- # Tag original global service name if not used
38
- if span.service != Datadog.configuration.service
39
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
40
- end
41
-
42
37
  span.set_tag(Contrib::Ext::DB::TAG_SYSTEM, Ext::TAG_SYSTEM)
43
38
  span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_CLIENT)
44
39
 
@@ -62,11 +62,6 @@ module Datadog
62
62
  )
63
63
  end
64
64
 
65
- # Tag original global service name if not used
66
- if span.service != Datadog.configuration.service
67
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
68
- end
69
-
70
65
  # Set url tags
71
66
  span.set_tag(OpenSearch::Ext::TAG_URL, url)
72
67
  span.set_tag(OpenSearch::Ext::TAG_HOST, host)
@@ -160,11 +160,6 @@ module Datadog
160
160
  )
161
161
  end
162
162
 
163
- # Tag original global service name if not used
164
- if span.service != Datadog.configuration.service
165
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
166
- end
167
-
168
163
  span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
169
164
  span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_QUERY)
170
165
  span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_CLIENT)
@@ -92,11 +92,6 @@ module Datadog
92
92
  )
93
93
  end
94
94
 
95
- # Tag original global service name if not used
96
- if span.service != Datadog.configuration.service
97
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
98
- end
99
-
100
95
  if (host_port = @options[:server])
101
96
  host, port = Core::Utils.extract_host_port(host_port)
102
97
  if host && port
@@ -48,11 +48,6 @@ module Datadog
48
48
  # Measure service stats
49
49
  Contrib::Analytics.set_measured(span)
50
50
 
51
- # Tag original global service name if not used
52
- if span.service != Datadog.configuration.service
53
- span.set_tag(Tracing::Contrib::Ext::Metadata::TAG_BASE_SERVICE, Datadog.configuration.service)
54
- end
55
-
56
51
  span.set_tag(Ext::TAG_TOPIC, payload[:topic])
57
52
  span.set_tag(Ext::TAG_CONSUMER, payload[:consumer_class])
58
53
  span.set_tag(Ext::TAG_PARTITION, payload[:partition])
@@ -54,6 +54,12 @@ module Datadog
54
54
  o.type :string, nilable: true
55
55
  end
56
56
 
57
+ option :inferred_proxy_enabled do |o|
58
+ o.type :bool
59
+ o.env Ext::ENV_INFERRED_PROXY_ENABLED
60
+ o.default false
61
+ end
62
+
57
63
  option :web_service_name, default: Ext::DEFAULT_PEER_WEBSERVER_SERVICE_NAME, type: :string
58
64
  end
59
65
  end
@@ -9,6 +9,7 @@ module Datadog
9
9
  module Ext
10
10
  ENV_ENABLED = 'DD_TRACE_RACK_ENABLED'
11
11
  ENV_DISTRIBUTED_TRACING = 'DD_TRACE_RACK_DISTRIBUTED_TRACING'
12
+ ENV_INFERRED_PROXY_ENABLED = 'DD_TRACE_INFERRED_PROXY_SERVICES_ENABLED'
12
13
  # @!visibility private
13
14
  ENV_ANALYTICS_ENABLED = 'DD_TRACE_RACK_ANALYTICS_ENABLED'
14
15
  ENV_ANALYTICS_SAMPLE_RATE = 'DD_TRACE_RACK_ANALYTICS_SAMPLE_RATE'
@@ -24,6 +25,32 @@ module Datadog
24
25
  TAG_OPERATION_HTTP_SERVER_QUEUE = 'queue'
25
26
  WEBSERVER_APP = 'webserver'
26
27
  DEFAULT_PEER_WEBSERVER_SERVICE_NAME = 'web-server'
28
+
29
+ # @!visibility private
30
+ HEADER_X_DD_PROXY = 'HTTP_X_DD_PROXY'
31
+ HEADER_X_DD_PROXY_REQUEST_TIME_MS = 'HTTP_X_DD_PROXY_REQUEST_TIME_MS'
32
+ HEADER_X_DD_PROXY_PATH = 'HTTP_X_DD_PROXY_PATH'
33
+ HEADER_X_DD_PROXY_RESOURCE_PATH = 'HTTP_X_DD_PROXY_RESOURCE_PATH'
34
+ HEADER_X_DD_PROXY_HTTPMETHOD = 'HTTP_X_DD_PROXY_HTTPMETHOD'
35
+ HEADER_X_DD_PROXY_DOMAIN_NAME = 'HTTP_X_DD_PROXY_DOMAIN_NAME'
36
+ HEADER_X_DD_PROXY_STAGE = 'HTTP_X_DD_PROXY_STAGE'
37
+ HEADER_X_DD_PROXY_ACCOUNT_ID = 'HTTP_X_DD_PROXY_ACCOUNT_ID'
38
+ HEADER_X_DD_PROXY_API_ID = 'HTTP_X_DD_PROXY_API_ID'
39
+ HEADER_X_DD_PROXY_REGION = 'HTTP_X_DD_PROXY_REGION'
40
+ HEADER_X_DD_PROXY_USER = 'HTTP_X_DD_PROXY_USER'
41
+
42
+ PROXY_AWS_APIGATEWAY = 'aws-apigateway'
43
+ PROXY_AWS_HTTPAPI = 'aws-httpapi'
44
+
45
+ SPAN_AWS_APIGATEWAY = 'aws.apigateway'
46
+ SPAN_AWS_HTTPAPI = 'aws.httpapi'
47
+
48
+ PROXY_SPAN_NAMES = {
49
+ PROXY_AWS_APIGATEWAY => SPAN_AWS_APIGATEWAY,
50
+ PROXY_AWS_HTTPAPI => SPAN_AWS_HTTPAPI,
51
+ }.freeze
52
+
53
+ TAG_INFERRED_SPAN = '_dd.inferred_span'
27
54
  end
28
55
  end
29
56
  end
@@ -13,7 +13,11 @@ module Datadog
13
13
  module TraceProxyMiddleware
14
14
  module_function
15
15
 
16
- def call(env, configuration)
16
+ def call(env, configuration, &block)
17
+ if configuration[:inferred_proxy_enabled] &&
18
+ (proxy_type = env[Ext::HEADER_X_DD_PROXY]) && !proxy_type.empty?
19
+ return call_with_inferred_proxy(env, proxy_type, &block)
20
+ end
17
21
  return yield unless configuration[:request_queuing]
18
22
 
19
23
  # parse the request queue time
@@ -51,6 +55,118 @@ module Datadog
51
55
  queue_span&.finish
52
56
  request_span&.finish
53
57
  end
58
+
59
+ # Creates a virtual parent span representing the upstream proxy
60
+ # that wraps the actual request processing.
61
+ #
62
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
63
+ def call_with_inferred_proxy(env, proxy_type)
64
+ span_name = Ext::PROXY_SPAN_NAMES[proxy_type]
65
+ return yield unless span_name
66
+
67
+ request_time_ms = env[Ext::HEADER_X_DD_PROXY_REQUEST_TIME_MS].to_f
68
+ return yield unless request_time_ms.positive?
69
+
70
+ path = env[Ext::HEADER_X_DD_PROXY_PATH]
71
+ stage = env[Ext::HEADER_X_DD_PROXY_STAGE]
72
+ domain = env[Ext::HEADER_X_DD_PROXY_DOMAIN_NAME]
73
+ http_method = env[Ext::HEADER_X_DD_PROXY_HTTPMETHOD]
74
+ resource_path = env[Ext::HEADER_X_DD_PROXY_RESOURCE_PATH]
75
+
76
+ # NOTE: resource_path is the parameterized route (e.g. /users/{id}) vs literal path
77
+ resource = "#{http_method} #{resource_path || path}" if http_method
78
+
79
+ inferred_span = Tracing.trace(
80
+ span_name,
81
+ service: domain,
82
+ type: Tracing::Metadata::Ext::AppTypes::TYPE_WEB,
83
+ start_time: Time.at(request_time_ms / 1_000),
84
+ )
85
+ inferred_span.resource = resource if resource
86
+ inferred_span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, proxy_type)
87
+ inferred_span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_SERVER)
88
+ inferred_span.set_tag('stage', stage) if stage
89
+ inferred_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_METHOD, http_method) if http_method
90
+ inferred_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_URL, "https://#{domain}#{path}") if domain && path
91
+ inferred_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ROUTE, resource_path) if resource_path
92
+ inferred_span.set_metric(Ext::TAG_INFERRED_SPAN, 1)
93
+
94
+ set_optional_tags(inferred_span, env: env, proxy_type: proxy_type)
95
+
96
+ yield
97
+ # NOTE: The underlying {Rack::TraceMiddleware} rescues {Exception}
98
+ # to tag the request span with error details.
99
+ # We must propagate errors to the inferred proxy parent span.
100
+ rescue Exception => e # rubocop:disable Lint/RescueException
101
+ inferred_span&.set_error(e)
102
+ raise
103
+ ensure
104
+ if inferred_span
105
+ propagate_request_span_tags(inferred_span, env: env)
106
+
107
+ if (trace = Tracing.active_trace) && resource
108
+ trace.resource = resource
109
+ end
110
+
111
+ inferred_span.finish
112
+ end
113
+ end
114
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
115
+
116
+ # Sets cloud provider metadata and constructs the resource ARN.
117
+ #
118
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
119
+ def set_optional_tags(span, env:, proxy_type:)
120
+ api_id = env[Ext::HEADER_X_DD_PROXY_API_ID]
121
+ region = env[Ext::HEADER_X_DD_PROXY_REGION]
122
+
123
+ # API Gateway v1 sends region as a single-quoted string
124
+ region = region.delete("'") if region
125
+
126
+ span.set_tag('apiid', api_id) if api_id
127
+ span.set_tag('region', region) if region
128
+
129
+ if (account_id = env[Ext::HEADER_X_DD_PROXY_ACCOUNT_ID])
130
+ span.set_tag('account_id', account_id)
131
+ end
132
+
133
+ if (user = env[Ext::HEADER_X_DD_PROXY_USER])
134
+ span.set_tag('aws_user', user)
135
+ end
136
+
137
+ if api_id && region
138
+ # NOTE: Update this when adding non-AWS proxy types.
139
+ restapi_prefix = (proxy_type == Ext::PROXY_AWS_APIGATEWAY) ? 'restapis' : 'apis'
140
+ span.set_tag('dd_resource_key', "arn:aws:apigateway:#{region}::/#{restapi_prefix}/#{api_id}")
141
+ end
142
+ end
143
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
144
+
145
+ # Propagates response-level and security tags from the request span to
146
+ # the inferred parent.
147
+ def propagate_request_span_tags(span, env:)
148
+ rack_span = env[Ext::RACK_ENV_REQUEST_SPAN]
149
+ return unless rack_span
150
+
151
+ if (status_code = rack_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE))
152
+ span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE, status_code)
153
+ span.status = rack_span.status
154
+ end
155
+
156
+ if (user_agent = rack_span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_USER_AGENT))
157
+ span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_USER_AGENT, user_agent)
158
+ end
159
+
160
+ # NOTE: Tracing shouldn't know about AppSec tags.
161
+ if (appsec_enabled = rack_span.get_metric('_dd.appsec.enabled'))
162
+ span.set_metric('_dd.appsec.enabled', appsec_enabled)
163
+ end
164
+
165
+ # NOTE: Tracing shouldn't know about AppSec tags.
166
+ if (appsec_json = rack_span.get_tag('_dd.appsec.json'))
167
+ span.set_tag('_dd.appsec.json', appsec_json)
168
+ end
169
+ end
54
170
  end
55
171
  end
56
172
  end