datadog 2.31.0 → 2.32.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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +17 -7
  3. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +11 -4
  4. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +6 -0
  5. data/ext/datadog_profiling_native_extension/extconf.rb +5 -4
  6. data/ext/datadog_profiling_native_extension/http_transport.c +10 -5
  7. data/ext/libdatadog_api/di.c +48 -0
  8. data/ext/libdatadog_api/extconf.rb +7 -4
  9. data/ext/libdatadog_extconf_helpers.rb +37 -0
  10. data/lib/datadog/ai_guard/configuration.rb +105 -2
  11. data/lib/datadog/ai_guard/evaluation.rb +1 -0
  12. data/lib/datadog/ai_guard/ext.rb +1 -0
  13. data/lib/datadog/appsec/autoload.rb +1 -1
  14. data/lib/datadog/appsec/component.rb +1 -1
  15. data/lib/datadog/appsec/configuration.rb +414 -1
  16. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +2 -1
  17. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +1 -1
  18. data/lib/datadog/appsec/contrib/rails/patcher.rb +2 -2
  19. data/lib/datadog/appsec/metrics/telemetry.rb +13 -1
  20. data/lib/datadog/appsec/security_engine/runner.rb +1 -1
  21. data/lib/datadog/appsec/trace_keeper.rb +18 -6
  22. data/lib/datadog/appsec/utils/http/url_encoded.rb +2 -2
  23. data/lib/datadog/core/configuration/components.rb +1 -1
  24. data/lib/datadog/core/configuration/settings.rb +3 -0
  25. data/lib/datadog/core/configuration/supported_configurations.rb +2 -0
  26. data/lib/datadog/core/configuration.rb +1 -1
  27. data/lib/datadog/core/contrib/rails/utils.rb +1 -1
  28. data/lib/datadog/core/crashtracking/component.rb +3 -3
  29. data/lib/datadog/core/diagnostics/environment_logger.rb +3 -1
  30. data/lib/datadog/core/environment/container.rb +2 -2
  31. data/lib/datadog/core/feature_flags.rb +1 -1
  32. data/lib/datadog/core/metrics/client.rb +5 -5
  33. data/lib/datadog/core/remote/client.rb +1 -1
  34. data/lib/datadog/core/remote/component.rb +2 -2
  35. data/lib/datadog/core/runtime/metrics.rb +1 -1
  36. data/lib/datadog/core/telemetry/emitter.rb +1 -1
  37. data/lib/datadog/core/telemetry/event/app_started.rb +2 -2
  38. data/lib/datadog/core/transport/http.rb +2 -0
  39. data/lib/datadog/core/utils.rb +1 -1
  40. data/lib/datadog/core/workers/async.rb +1 -1
  41. data/lib/datadog/core.rb +1 -1
  42. data/lib/datadog/data_streams/configuration.rb +40 -1
  43. data/lib/datadog/data_streams/pathway_context.rb +1 -1
  44. data/lib/datadog/data_streams/processor.rb +1 -1
  45. data/lib/datadog/data_streams.rb +1 -1
  46. data/lib/datadog/di/base.rb +8 -5
  47. data/lib/datadog/di/code_tracker.rb +179 -1
  48. data/lib/datadog/di/component.rb +1 -1
  49. data/lib/datadog/di/configuration.rb +235 -2
  50. data/lib/datadog/di/instrumenter.rb +46 -26
  51. data/lib/datadog/di/probe_builder.rb +1 -1
  52. data/lib/datadog/di/probe_file_loader.rb +2 -2
  53. data/lib/datadog/di/probe_manager.rb +6 -6
  54. data/lib/datadog/di/probe_notification_builder.rb +1 -1
  55. data/lib/datadog/di/probe_notifier_worker.rb +2 -2
  56. data/lib/datadog/di/remote.rb +6 -6
  57. data/lib/datadog/di/serializer.rb +1 -1
  58. data/lib/datadog/di/transport/input.rb +3 -3
  59. data/lib/datadog/error_tracking/configuration.rb +55 -2
  60. data/lib/datadog/kit/enable_core_dumps.rb +1 -1
  61. data/lib/datadog/open_feature/component.rb +18 -1
  62. data/lib/datadog/open_feature/evaluation_engine.rb +3 -3
  63. data/lib/datadog/open_feature/exposures/reporter.rb +1 -1
  64. data/lib/datadog/open_feature/exposures/worker.rb +1 -1
  65. data/lib/datadog/open_feature/hooks/flag_eval_hook.rb +49 -0
  66. data/lib/datadog/open_feature/metrics/flag_eval_metrics.rb +149 -0
  67. data/lib/datadog/open_feature/provider.rb +19 -1
  68. data/lib/datadog/open_feature/remote.rb +1 -1
  69. data/lib/datadog/open_feature/transport.rb +1 -1
  70. data/lib/datadog/opentelemetry/metrics.rb +3 -3
  71. data/lib/datadog/opentelemetry/sdk/configurator.rb +1 -1
  72. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +1 -1
  73. data/lib/datadog/profiling/collectors/code_provenance.rb +35 -9
  74. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +31 -2
  75. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +8 -2
  76. data/lib/datadog/profiling/collectors/info.rb +16 -3
  77. data/lib/datadog/profiling/component.rb +3 -5
  78. data/lib/datadog/profiling/exporter.rb +37 -12
  79. data/lib/datadog/profiling/ext.rb +0 -2
  80. data/lib/datadog/profiling/flush.rb +21 -12
  81. data/lib/datadog/profiling/http_transport.rb +12 -1
  82. data/lib/datadog/profiling/load_native_extension.rb +1 -1
  83. data/lib/datadog/profiling/profiler.rb +13 -1
  84. data/lib/datadog/profiling/scheduler.rb +2 -2
  85. data/lib/datadog/profiling/tasks/exec.rb +8 -3
  86. data/lib/datadog/profiling/tasks/help.rb +1 -0
  87. data/lib/datadog/profiling/tasks/setup.rb +2 -2
  88. data/lib/datadog/single_step_instrument.rb +1 -1
  89. data/lib/datadog/symbol_database/configuration.rb +65 -0
  90. data/lib/datadog/symbol_database/extractor.rb +915 -0
  91. data/lib/datadog/symbol_database/file_hash.rb +46 -0
  92. data/lib/datadog/symbol_database/logger.rb +43 -0
  93. data/lib/datadog/symbol_database/scope.rb +98 -0
  94. data/lib/datadog/symbol_database/service_version.rb +57 -0
  95. data/lib/datadog/symbol_database/symbol.rb +66 -0
  96. data/lib/datadog/symbol_database/transport/http/endpoint.rb +28 -0
  97. data/lib/datadog/symbol_database/transport/http.rb +45 -0
  98. data/lib/datadog/symbol_database/transport.rb +54 -0
  99. data/lib/datadog/symbol_database/uploader.rb +166 -0
  100. data/lib/datadog/symbol_database.rb +49 -0
  101. data/lib/datadog/tracing/buffer.rb +3 -3
  102. data/lib/datadog/tracing/configuration/settings.rb +1 -1
  103. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +5 -3
  104. data/lib/datadog/tracing/contrib/action_view/events/render_template.rb +1 -1
  105. data/lib/datadog/tracing/contrib/active_job/events/discard.rb +1 -1
  106. data/lib/datadog/tracing/contrib/active_job/events/enqueue.rb +1 -1
  107. data/lib/datadog/tracing/contrib/active_job/events/enqueue_at.rb +1 -1
  108. data/lib/datadog/tracing/contrib/active_job/events/enqueue_retry.rb +1 -1
  109. data/lib/datadog/tracing/contrib/active_job/events/perform.rb +1 -1
  110. data/lib/datadog/tracing/contrib/active_job/events/retry_stopped.rb +1 -1
  111. data/lib/datadog/tracing/contrib/active_model_serializers/events/render.rb +1 -1
  112. data/lib/datadog/tracing/contrib/active_model_serializers/events/serialize.rb +1 -1
  113. data/lib/datadog/tracing/contrib/active_record/configuration/resolver.rb +2 -2
  114. data/lib/datadog/tracing/contrib/active_record/events/instantiation.rb +1 -1
  115. data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -1
  116. data/lib/datadog/tracing/contrib/active_record/utils.rb +1 -1
  117. data/lib/datadog/tracing/contrib/active_support/cache/events/cache.rb +1 -1
  118. data/lib/datadog/tracing/contrib/active_support/notifications/subscription.rb +2 -2
  119. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +1 -1
  120. data/lib/datadog/tracing/contrib/component.rb +1 -1
  121. data/lib/datadog/tracing/contrib/configuration/resolver.rb +7 -4
  122. data/lib/datadog/tracing/contrib/dalli/quantize.rb +1 -1
  123. data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +1 -1
  124. data/lib/datadog/tracing/contrib/excon/middleware.rb +2 -2
  125. data/lib/datadog/tracing/contrib/extensions.rb +9 -0
  126. data/lib/datadog/tracing/contrib/faraday/middleware.rb +2 -2
  127. data/lib/datadog/tracing/contrib/grape/endpoint.rb +5 -5
  128. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +2 -2
  129. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +2 -2
  130. data/lib/datadog/tracing/contrib/http/instrumentation.rb +2 -2
  131. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +6 -2
  132. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +2 -2
  133. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +2 -2
  134. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +2 -2
  135. data/lib/datadog/tracing/contrib/karafka/patcher.rb +1 -1
  136. data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +3 -3
  137. data/lib/datadog/tracing/contrib/opensearch/patcher.rb +1 -1
  138. data/lib/datadog/tracing/contrib/presto/instrumentation.rb +3 -3
  139. data/lib/datadog/tracing/contrib/rack/patcher.rb +1 -1
  140. data/lib/datadog/tracing/contrib/rack/request_queue.rb +1 -1
  141. data/lib/datadog/tracing/contrib/rails/log_injection.rb +1 -1
  142. data/lib/datadog/tracing/contrib/rails/runner.rb +1 -1
  143. data/lib/datadog/tracing/contrib/rake/instrumentation.rb +2 -2
  144. data/lib/datadog/tracing/contrib/redis/quantize.rb +1 -1
  145. data/lib/datadog/tracing/contrib/redis/tags.rb +1 -1
  146. data/lib/datadog/tracing/contrib/sidekiq/utils.rb +1 -1
  147. data/lib/datadog/tracing/contrib/stripe/request.rb +1 -1
  148. data/lib/datadog/tracing/contrib.rb +8 -0
  149. data/lib/datadog/tracing/diagnostics/environment_logger.rb +3 -1
  150. data/lib/datadog/tracing/distributed/baggage.rb +59 -5
  151. data/lib/datadog/tracing/distributed/datadog.rb +11 -11
  152. data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +1 -1
  153. data/lib/datadog/tracing/distributed/propagation.rb +2 -2
  154. data/lib/datadog/tracing/distributed/trace_context.rb +74 -32
  155. data/lib/datadog/tracing/event.rb +1 -1
  156. data/lib/datadog/tracing/metadata/tagging.rb +2 -2
  157. data/lib/datadog/tracing/pipeline.rb +1 -1
  158. data/lib/datadog/tracing/remote.rb +1 -1
  159. data/lib/datadog/tracing/sampling/rule.rb +1 -1
  160. data/lib/datadog/tracing/sampling/rule_sampler.rb +2 -2
  161. data/lib/datadog/tracing/sampling/span/rule_parser.rb +2 -2
  162. data/lib/datadog/tracing/span_operation.rb +3 -3
  163. data/lib/datadog/tracing/trace_operation.rb +4 -4
  164. data/lib/datadog/tracing/tracer.rb +5 -5
  165. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  166. data/lib/datadog/tracing/workers.rb +2 -1
  167. data/lib/datadog/version.rb +1 -1
  168. metadata +18 -9
  169. data/lib/datadog/ai_guard/configuration/settings.rb +0 -113
  170. data/lib/datadog/appsec/configuration/settings.rb +0 -423
  171. data/lib/datadog/data_streams/configuration/settings.rb +0 -49
  172. data/lib/datadog/di/configuration/settings.rb +0 -243
  173. data/lib/datadog/error_tracking/configuration/settings.rb +0 -63
@@ -66,7 +66,7 @@ module Datadog
66
66
  span.set_tag(Ext::TAG_TASK_ARG_NAMES, arg_names)
67
67
  span.set_tag(Ext::TAG_INVOKE_ARGS, quantize_args(args)) unless args.nil?
68
68
  rescue => e
69
- Datadog.logger.debug { "Error while tracing Rake invoke: #{e.class}: #{e}" }
69
+ Datadog.logger.debug { "Error while tracing Rake invoke: #{e.class}: #{e.message}" }
70
70
  end
71
71
 
72
72
  def annotate_execute!(span, args)
@@ -75,7 +75,7 @@ module Datadog
75
75
  span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_EXECUTE)
76
76
  span.set_tag(Ext::TAG_EXECUTE_ARGS, quantize_args(args.to_hash)) unless args.nil?
77
77
  rescue => e
78
- Datadog.logger.debug { "Error while tracing Rake execute: #{e.class}: #{e}" }
78
+ Datadog.logger.debug { "Error while tracing Rake execute: #{e.class}: #{e.message}" }
79
79
  end
80
80
 
81
81
  def quantize_args(args)
@@ -34,7 +34,7 @@ module Datadog
34
34
  str = Core::Utils.utf8_encode(arg, binary: true, placeholder: PLACEHOLDER)
35
35
  Core::Utils.truncate(str, VALUE_MAX_LEN, TOO_LONG_MARK)
36
36
  rescue => e
37
- Datadog.logger.debug("non formattable Redis arg #{str}: #{e}")
37
+ Datadog.logger.debug("non formattable Redis arg #{str}: #{e.class}: #{e.message}")
38
38
  PLACEHOLDER
39
39
  end
40
40
 
@@ -47,7 +47,7 @@ module Datadog
47
47
 
48
48
  Contrib::SpanAttributeSchema.set_peer_service!(span, Ext::PEER_SERVICE_SOURCES)
49
49
  rescue => e
50
- Datadog.logger.error("#{e.class}: #{e}")
50
+ Datadog.logger.error("#{e.class}: #{e.message}")
51
51
  Datadog::Core::Telemetry::Logger.report(e)
52
52
  end
53
53
 
@@ -25,7 +25,7 @@ module Datadog
25
25
  job['class'].to_s
26
26
  end
27
27
  rescue => e
28
- Datadog.logger.debug { "Error retrieving Sidekiq job class name (jid:#{job["jid"]}): #{e}" }
28
+ Datadog.logger.debug { "Error retrieving Sidekiq job class name (jid:#{job["jid"]}): #{e.class}: #{e.message}" }
29
29
 
30
30
  job['class'].to_s
31
31
  end
@@ -55,7 +55,7 @@ module Datadog
55
55
  span.set_tag(Ext::TAG_REQUEST_PATH, event.path)
56
56
  span.set_tag(Ext::TAG_REQUEST_NUM_RETRIES, event.num_retries.to_s)
57
57
  rescue => e
58
- Datadog.logger.debug { "#{e.class}: #{e}" }
58
+ Datadog.logger.debug { "#{e.class}: #{e.message}" }
59
59
  end
60
60
 
61
61
  def configuration
@@ -81,3 +81,11 @@ require_relative 'contrib/sneakers/integration'
81
81
  require_relative 'contrib/stripe/integration'
82
82
  require_relative 'contrib/sucker_punch/integration'
83
83
  require_relative 'contrib/trilogy/integration'
84
+
85
+ # This list is used to determine if an integration is a built-in integration,
86
+ # and prevent sending telemetry for custom integrations.
87
+ # To do this, we get the list of registered integrations at this point and freeze it.
88
+ # Later, when custom integrations are registered, they will not be included in this list.
89
+ # .uniq to handle aliases, as we can register the same integration multiple times with different names.
90
+ Datadog::Tracing::Contrib::BUILT_IN_INTEGRATIONS =
91
+ Datadog::Tracing::Contrib::REGISTRY.map { |entry| entry.klass }.uniq.freeze
@@ -24,7 +24,9 @@ module Datadog
24
24
  end
25
25
  end
26
26
  rescue => e
27
- logger.warn("Failed to collect tracing environment information: #{e} Location: #{Array(e.backtrace).first}")
27
+ logger.warn(
28
+ "Failed to collect tracing environment information: #{e.class}: #{e.message} Location: #{Array(e.backtrace).first}"
29
+ )
28
30
  end
29
31
  end
30
32
 
@@ -77,7 +77,7 @@ module Datadog
77
77
  # Record telemetry for successful injection
78
78
  record_telemetry_metric('context_header_style.injected', 1, {'header_style' => 'baggage'})
79
79
  rescue => e
80
- ::Datadog.logger.warn("Failed to encode and inject baggage header: #{e.class}: #{e}")
80
+ ::Datadog.logger.warn("Failed to encode and inject baggage header: #{e.class}: #{e.message}")
81
81
  end
82
82
  end
83
83
 
@@ -133,12 +133,56 @@ module Datadog
133
133
  # - Keys and values are URL-encoded
134
134
  # - Returns an empty hash if the baggage header is malformed
135
135
  #
136
+ # For entries with duplicate keys, only last one is preserved:
137
+ # https://www.w3.org/TR/2024/CR-baggage-20240530/#mutating-baggage
138
+ #
136
139
  # @param baggage_header [String] The W3C Baggage header string to parse
137
140
  # @return [Hash<String, String>] A hash of decoded baggage items
138
141
  def parse_baggage_header(baggage_header)
142
+ # Reject inputs that would cause String operations below to raise.
143
+ # `valid_encoding?` is true for ASCII-8BIT regardless of byte values, so
144
+ # the typical Rack path is unaffected; only UTF-8-tagged-but-actually-invalid
145
+ # input (e.g. headers re-encoded upstream) is short-circuited here.
146
+ unless baggage_header.valid_encoding?
147
+ record_telemetry_metric('context_header_style.malformed', 1, {'header_style' => 'baggage'})
148
+ return {}
149
+ end
150
+
151
+ if baggage_header.bytesize > DD_TRACE_BAGGAGE_MAX_BYTES
152
+ record_telemetry_metric('context_header.truncated', 1, {'header_style' => 'baggage', 'truncation_reason' => 'baggage_byte_count_exceeded'})
153
+
154
+ # We MUST NOT propagate partial entries, but we SHOULD try
155
+ # to parse as much of the baggage as possible:
156
+ # https://www.w3.org/TR/2024/CR-baggage-20240530/#limits
157
+
158
+ # We parse 1 byte over the limit to detect if the last entry
159
+ # is a partial entry (toss) or ends exactly at the limit (keep).
160
+ remove_last_entry = baggage_header.byteslice(DD_TRACE_BAGGAGE_MAX_BYTES, 1) != ','
161
+
162
+ # To ensure we don't have a trailing partial UTF-8 codepoint, we keep one extra byte
163
+ # and safely remove it with `#chop`.
164
+ # `#chops` walks back the string until it finds a valid character boundary and deletes
165
+ # from there.
166
+ baggage_header = baggage_header.byteslice(0, DD_TRACE_BAGGAGE_MAX_BYTES + 1) #: String
167
+ baggage_header = baggage_header.chop
168
+ end
169
+
139
170
  baggage = {}
140
- baggages = baggage_header.split(',')
141
- baggages.each do |key_value|
171
+ # DEV: To avoid unnecessary eager string allocation, replace with
172
+ # DEV: `split(',') { |s| ... }` when Ruby 2.5 is no longer supported.
173
+ baggages = baggage_header.split(',', DD_TRACE_BAGGAGE_MAX_ITEMS + 1) # Stop splitting if we've reached max size
174
+ baggages.each_with_index do |key_value, index|
175
+ if index >= DD_TRACE_BAGGAGE_MAX_ITEMS
176
+ record_telemetry_metric('context_header.truncated', 1, {'header_style' => 'baggage', 'truncation_reason' => 'baggage_item_count_exceeded'})
177
+ break
178
+ end
179
+
180
+ # On truncation, remove incomplete trailing partial entry
181
+ next if remove_last_entry && index == baggages.size - 1
182
+
183
+ # Empty items are skipped
184
+ next if key_value.strip.empty?
185
+
142
186
  key, value = key_value.split('=', 2)
143
187
  # If baggage is malformed, return an empty hash
144
188
  if key.nil? || value.nil?
@@ -147,8 +191,17 @@ module Datadog
147
191
  return {}
148
192
  end
149
193
 
150
- key = URI.decode_www_form_component(key.strip)
151
- value = URI.decode_www_form_component(value.strip)
194
+ begin
195
+ key = URI.decode_www_form_component(key.strip)
196
+ value = URI.decode_www_form_component(value.strip)
197
+ rescue ArgumentError
198
+ # `URI.decode_www_form_component` raises on malformed percent encoding
199
+ # (e.g. `%XX`, lone `%`). Treat as malformed rather than letting it
200
+ # propagate to `Propagation#extract`'s caller.
201
+ record_telemetry_metric('context_header_style.malformed', 1, {'header_style' => 'baggage'})
202
+ return {}
203
+ end
204
+
152
205
  if key.empty? || value.empty?
153
206
  # Record telemetry for malformed header
154
207
  record_telemetry_metric('context_header_style.malformed', 1, {'header_style' => 'baggage'})
@@ -157,6 +210,7 @@ module Datadog
157
210
 
158
211
  baggage[key] = value
159
212
  end
213
+
160
214
  baggage
161
215
  end
162
216
 
@@ -131,16 +131,13 @@ module Datadog
131
131
 
132
132
  encoded_tags = DatadogTagsCodec.encode(tags)
133
133
 
134
- return set_tags_propagation_error(reason: 'inject_max_size') if tags_too_large?(
135
- encoded_tags.size,
136
- scenario: 'inject'
137
- )
134
+ return set_tags_propagation_error(reason: 'inject_max_size') if tags_too_large?(encoded_tags, scenario: 'inject')
138
135
 
139
136
  data[@tags_key] = encoded_tags
140
137
  rescue => e
141
138
  set_tags_propagation_error(reason: 'encoding_error')
142
139
  ::Datadog.logger.warn(
143
- "Failed to inject x-datadog-tags: #{e.class}: #{e} at #{Array(e.backtrace).first}"
140
+ "Failed to inject x-datadog-tags: #{e.class}: #{e.message} at #{Array(e.backtrace).first}"
144
141
  )
145
142
  nil
146
143
  end
@@ -156,7 +153,7 @@ module Datadog
156
153
 
157
154
  return if !tags || tags.empty?
158
155
  return set_tags_propagation_error(reason: 'disabled') if tags_disabled?
159
- return set_tags_propagation_error(reason: 'extract_max_size') if tags_too_large?(tags.size, scenario: 'extract')
156
+ return set_tags_propagation_error(reason: 'extract_max_size') if tags_too_large?(tags, scenario: 'extract')
160
157
 
161
158
  tags_hash = DatadogTagsCodec.decode(tags)
162
159
  # Only extract keys with the expected Datadog prefix
@@ -167,7 +164,7 @@ module Datadog
167
164
  rescue => e
168
165
  set_tags_propagation_error(reason: 'decoding_error')
169
166
  ::Datadog.logger.warn(
170
- "Failed to extract x-datadog-tags: #{e.class}: #{e} at #{Array(e.backtrace).first}"
167
+ "Failed to extract x-datadog-tags: #{e.class}: #{e.message} at #{Array(e.backtrace).first}"
171
168
  )
172
169
  nil
173
170
  end
@@ -182,12 +179,15 @@ module Datadog
182
179
  ::Datadog.configuration.tracing.x_datadog_tags_max_length <= 0
183
180
  end
184
181
 
185
- def tags_too_large?(size, scenario:)
186
- return false if size <= ::Datadog.configuration.tracing.x_datadog_tags_max_length
182
+ def tags_too_large?(tags, scenario:)
183
+ size = tags.bytesize
184
+ max_length = ::Datadog.configuration.tracing.x_datadog_tags_max_length
185
+
186
+ return false if size <= max_length
187
187
 
188
188
  ::Datadog.logger.warn(
189
- "Failed to #{scenario} x-datadog-tags: tags are too large for configured limit (size:#{size} >= " \
190
- "limit:#{::Datadog.configuration.tracing.x_datadog_tags_max_length}). This limit can be configured " \
189
+ "Failed to #{scenario} x-datadog-tags: tags are too large for configured limit (bytesize:#{size} >= " \
190
+ "limit:#{max_length}). This limit can be configured " \
191
191
  'through the DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH environment variable.'
192
192
  )
193
193
 
@@ -41,7 +41,7 @@ module Datadog
41
41
  "#{key}=#{value.strip}"
42
42
  end.join(',')
43
43
  rescue => e
44
- raise EncodingError, "Error encoding tags `#{tags}`: `#{e}`"
44
+ raise EncodingError, "Error encoding tags `#{tags}`: `#{e.class}: #{e.message}`"
45
45
  end
46
46
 
47
47
  # Deserializes a `x-datadog-tags`-formatted String into a {Hash<String,String>}.
@@ -76,7 +76,7 @@ module Datadog
76
76
  rescue => e
77
77
  result = nil
78
78
  ::Datadog.logger.error(
79
- "Error injecting distributed trace data. Cause: #{e} Location: #{Array(e.backtrace).first}"
79
+ "Error injecting distributed trace data. Cause: #{e.class}: #{e.message} Location: #{Array(e.backtrace).first}"
80
80
  )
81
81
  ::Datadog::Core::Telemetry::Logger.report(
82
82
  e,
@@ -139,7 +139,7 @@ module Datadog
139
139
  rescue => e
140
140
  # TODO: Not to report Telemetry logs for now
141
141
  ::Datadog.logger.error(
142
- "Error extracting distributed trace data. Cause: #{e} Location: #{Array(e.backtrace).first}"
142
+ "Error extracting distributed trace data. Cause: #{e.class}: #{e.message} Location: #{Array(e.backtrace).first}"
143
143
  )
144
144
  end
145
145
  # Handle baggage after all other styles if present
@@ -149,9 +149,9 @@ module Datadog
149
149
  # @see https://www.w3.org/TR/trace-context/#tracestate-header
150
150
  def build_tracestate(digest)
151
151
  tracestate = +'dd='
152
- tracestate << last_dd_parent_id(digest)
153
- tracestate << "s:#{digest.trace_sampling_priority};" if digest.trace_sampling_priority
154
- tracestate << "o:#{serialize_origin(digest.trace_origin)};" if digest.trace_origin
152
+ append_dd(tracestate, last_dd_parent_id(digest))
153
+ append_dd(tracestate, "s:#{digest.trace_sampling_priority};") if digest.trace_sampling_priority
154
+ append_dd(tracestate, "o:#{serialize_origin(digest.trace_origin)};") if digest.trace_origin
155
155
 
156
156
  # Replacing this by safe navigation seems to have a different behaviour on Rubies <= 3.0.
157
157
  # It cause a LocalJumpError in the CI.
@@ -159,45 +159,50 @@ module Datadog
159
159
  digest.trace_distributed_tags.each do |name, value|
160
160
  tag = "t.#{serialize_tag_key(name)}:#{serialize_tag_value(value)};"
161
161
 
162
- # If tracestate size limit is exceed, drop the remaining data.
163
- # String#bytesize is used because only ASCII characters are allowed.
164
- #
165
- # We add 1 to the limit because of the trailing comma, which will be removed before returning.
166
- break if tracestate.bytesize + tag.bytesize > (TRACESTATE_VALUE_SIZE_LIMIT + 1)
167
-
168
- tracestate << tag
162
+ # If tracestate size limit is exceeded, drop the remaining data.
163
+ break unless append_dd(tracestate, tag)
169
164
  end
170
165
  end
171
166
 
172
- tracestate << digest.trace_state_unknown_fields if digest.trace_state_unknown_fields
167
+ append_dd(tracestate, digest.trace_state_unknown_fields) if digest.trace_state_unknown_fields
168
+ vendors = split_tracestate(digest.trace_state)
173
169
 
174
170
  # Is there any Datadog-specific information to propagate.
175
171
  # Check for > 3 size because the empty prefix `dd=` has 3 characters.
176
- if tracestate.size > 3
172
+ if tracestate.bytesize > 3
177
173
  # Propagate upstream tracestate with `dd=...` appended to the list
178
174
  tracestate.chop! # Removes trailing `;` from Datadog trace state string.
179
175
 
180
- if digest.trace_state
181
- trace_state = digest.trace_state.strip
182
-
176
+ if vendors && !vendors.empty?
183
177
  # Delete existing `dd=` tracestate fields, if present.
184
- vendors = split_tracestate(trace_state)
185
178
  vendors.reject! { |v| v.start_with?('dd=') }
186
- end
187
179
 
188
- if vendors && !vendors.empty?
189
180
  # Ensure the list has at most 31 elements, as we need to prepend Datadog's
190
181
  # entry and the limit is 32 elements total.
191
- vendors = vendors[0..30]
192
- "#{tracestate},#{vendors.join(",")}"
193
- else
194
- tracestate.to_s
182
+ vendors.first(TRACESTATE_MAX_LIST_MEMBERS - 1).each do |vendor|
183
+ break if tracestate.bytesize + vendor.bytesize + 1 > TRACESTATE_MAX_SIZE_LIMIT
184
+
185
+ tracestate << ',' << vendor
186
+ end
195
187
  end
196
- else
197
- digest.trace_state # Propagate upstream tracestate with no Datadog changes
188
+
189
+ tracestate.to_s
190
+ elsif vendors && !vendors.empty?
191
+ vendors.join(',')
198
192
  end
199
193
  end
200
194
 
195
+ # Appends a Datadog tracestate field when it fits.
196
+ def append_dd(tracestate, field)
197
+ return true if field.empty?
198
+
199
+ # We add 1 to the limit because of the trailing semicolon, which will be removed before returning.
200
+ return false if tracestate.bytesize + field.bytesize > (TRACESTATE_VALUE_SIZE_LIMIT + 1)
201
+
202
+ tracestate << field
203
+ true
204
+ end
205
+
201
206
  def last_dd_parent_id(digest)
202
207
  if !digest.span_remote
203
208
  span_id = digest.span_id || 0 # Fall back to zero (invalid) if not present
@@ -281,19 +286,21 @@ module Datadog
281
286
 
282
287
  def parse_traceparent_string(traceparent)
283
288
  return unless traceparent
289
+ return if traceparent.bytesize > TRACEPARENT_MAX_SIZE_LIMIT
284
290
 
285
- version, trace_id, parent_id, trace_flags, extra = traceparent.strip.split('-')
291
+ traceparent = traceparent.strip
286
292
 
287
- return if version.size != 2 || version[0] < '0' || version[0] > 'f' || version[1] < '0' || version[1] > 'f'
293
+ version, trace_id, parent_id, trace_flags, extra = traceparent.split('-', 5)
294
+
295
+ return unless version && trace_id && parent_id && trace_flags
296
+ return if version.size != 2 || trace_id.size != 32 || parent_id.size != 16 || trace_flags.size != 2
297
+ return if version[0] < '0' || version[0] > 'f' || version[1] < '0' || version[1] > 'f'
288
298
 
289
299
  return if version == INVALID_VERSION
290
300
 
291
301
  # Extra fields are not allowed in version 00, but we have to be lenient for future versions.
292
302
  return if version == SPEC_VERSION && extra
293
303
 
294
- # Invalid field sizes
295
- return if trace_id.size != 32 || parent_id.size != 16 || trace_flags.size != 2
296
-
297
304
  [Integer(trace_id, 16), Integer(parent_id, 16), Integer(trace_flags, 16)]
298
305
  rescue ArgumentError # Conversion to integer failed
299
306
  nil
@@ -306,9 +313,10 @@ module Datadog
306
313
  # @return [Array<String,Integer,String,String,Hash>] returns 4 values:
307
314
  # tracestate, sampling_priority, ts_parent_id, origin, tags.
308
315
  def extract_tracestate(tracestate)
309
- return unless tracestate
310
-
311
316
  vendors = split_tracestate(tracestate)
317
+ return unless vendors && !vendors.empty?
318
+
319
+ tracestate = vendors.join(',')
312
320
 
313
321
  # Find Datadog's `dd=` tracestate field.
314
322
  idx = vendors.index { |v| v.start_with?('dd=') }
@@ -396,10 +404,44 @@ module Datadog
396
404
  end
397
405
  end
398
406
 
407
+ # We MUST NOT propagate partial members, but we SHOULD try
408
+ # to parse as much of the tracestate as possible.
399
409
  def split_tracestate(tracestate)
400
- tracestate.split(/[ \t]*+,[ \t]*+/)[0..31]
410
+ return unless tracestate
411
+
412
+ remove_last_member = false
413
+ if tracestate.bytesize > TRACESTATE_MAX_SIZE_LIMIT
414
+ # We parse 1 byte over the limit to detect if the last member
415
+ # is a partial member (toss) or ends exactly at the limit (keep).
416
+ remove_last_member = tracestate.byteslice(TRACESTATE_MAX_SIZE_LIMIT, 1) != ','
417
+
418
+ # To ensure we don't have a trailing partial UTF-8 codepoint, we keep one extra byte
419
+ # and safely remove it with `#chop`.
420
+ # `#chop` walks back the string until it finds a valid character boundary and deletes
421
+ # from there.
422
+ tracestate = tracestate.byteslice(0, TRACESTATE_MAX_SIZE_LIMIT + 1) #: String
423
+ tracestate = tracestate.chop
424
+ end
425
+
426
+ vendors = tracestate.split(',', TRACESTATE_MAX_LIST_MEMBERS + 1)
427
+ if vendors.length > TRACESTATE_MAX_LIST_MEMBERS || remove_last_member
428
+ vendors.pop
429
+ end
430
+
431
+ vendors.each(&:strip!)
432
+ vendors.pop while vendors.last == ''
433
+ vendors
401
434
  end
402
435
 
436
+ TRACEPARENT_MAX_SIZE_LIMIT = 512
437
+ private_constant :TRACEPARENT_MAX_SIZE_LIMIT
438
+
439
+ TRACESTATE_MAX_SIZE_LIMIT = 512
440
+ private_constant :TRACESTATE_MAX_SIZE_LIMIT
441
+
442
+ TRACESTATE_MAX_LIST_MEMBERS = 32
443
+ private_constant :TRACESTATE_MAX_LIST_MEMBERS
444
+
403
445
  # Version 0xFF is invalid as per spec
404
446
  # @see https://www.w3.org/TR/trace-context/#version
405
447
  INVALID_VERSION = 'ff'
@@ -62,7 +62,7 @@ module Datadog
62
62
  block.call(*args)
63
63
  rescue => e
64
64
  Datadog.logger.debug do
65
- "Error while handling '#{name}' event with '#{block}': #{e.class}: #{e} " \
65
+ "Error while handling '#{name}' event with '#{block}': #{e.class}: #{e.message} " \
66
66
  "at #{Array(e.backtrace).first}"
67
67
  end
68
68
  end
@@ -52,7 +52,7 @@ module Datadog
52
52
  meta[Core::Utils.utf8_encode(key)] = Core::Utils.utf8_encode(value)
53
53
  end
54
54
  rescue => e
55
- Datadog.logger.debug("Unable to set the tag #{key}, ignoring it. Caused by: #{e}")
55
+ Datadog.logger.debug("Unable to set the tag #{key}, ignoring it. Caused by: #{e.class}: #{e.message}")
56
56
  end
57
57
 
58
58
  # Sets tags from given hash, for each key in hash it sets the tag with that key
@@ -102,7 +102,7 @@ module Datadog
102
102
  # Encode strings in UTF-8 to facilitate downstream serialization
103
103
  metrics[Core::Utils.utf8_encode(key)] = value
104
104
  rescue => e
105
- Datadog.logger.debug("Unable to set the metric #{key}, ignoring it. Caused by: #{e}")
105
+ Datadog.logger.debug("Unable to set the metric #{key}, ignoring it. Caused by: #{e.class}: #{e.message}")
106
106
  end
107
107
 
108
108
  # This method removes a metric for the given key. It acts like {#clear_tag}.
@@ -51,7 +51,7 @@ module Datadog
51
51
  end
52
52
  rescue => e
53
53
  Datadog.logger.debug(
54
- "trace dropped entirely due to `Pipeline.before_flush` error: #{e}"
54
+ "trace dropped entirely due to `Pipeline.before_flush` error: #{e.class}: #{e.message}"
55
55
  )
56
56
 
57
57
  nil
@@ -43,7 +43,7 @@ module Datadog
43
43
 
44
44
  Datadog.send(:components).telemetry.client_configuration_change!(env_vars)
45
45
  rescue => e
46
- content.errored("#{e.class}: #{e}: #{Array(e.backtrace).join("\n")}")
46
+ content.errored("#{e.class}: #{e.message}: #{Array(e.backtrace).join("\n")}")
47
47
  end
48
48
 
49
49
  def receivers(_telemetry)
@@ -34,7 +34,7 @@ module Datadog
34
34
  @matcher.match?(trace)
35
35
  rescue => e
36
36
  Datadog.logger.error(
37
- "Matcher failed. Cause: #{e.class}: #{e} Source: #{Array(e.backtrace).first}"
37
+ "Matcher failed. Cause: #{e.class}: #{e.message} Source: #{Array(e.backtrace).first}"
38
38
  )
39
39
  Datadog::Core::Telemetry::Logger.report(e, description: 'Matcher failed')
40
40
  nil
@@ -85,7 +85,7 @@ module Datadog
85
85
  new(parsed_rules, rate_limit: rate_limit, default_sample_rate: default_sample_rate)
86
86
  rescue => e
87
87
  Datadog.logger.warn do
88
- "Could not parse trace sampling rules '#{rules}': #{e.class}: #{e} at #{Array(e.backtrace).first}"
88
+ "Could not parse trace sampling rules '#{rules}': #{e.class}: #{e.message} at #{Array(e.backtrace).first}"
89
89
  end
90
90
 
91
91
  nil
@@ -133,7 +133,7 @@ module Datadog
133
133
  apply_rule!(trace, rule)
134
134
  rescue => e
135
135
  Datadog.logger.error(
136
- "Rule sampling failed. Cause: #{e.class}: #{e} Source: #{Array(e.backtrace).first}"
136
+ "Rule sampling failed. Cause: #{e.class}: #{e.message} Source: #{Array(e.backtrace).first}"
137
137
  )
138
138
  Datadog::Core::Telemetry::Logger.report(e, description: 'Rule sampling failed')
139
139
 
@@ -29,7 +29,7 @@ module Datadog
29
29
  rescue => e
30
30
  Datadog.logger.warn(
31
31
  "Error parsing Span Sampling Rules `#{rules.inspect}`: " \
32
- "#{e.class}: #{e} at #{Array(e.backtrace).first}"
32
+ "#{e.class}: #{e.message} at #{Array(e.backtrace).first}"
33
33
  )
34
34
  return nil
35
35
  end
@@ -61,7 +61,7 @@ module Datadog
61
61
  rescue => e
62
62
  Datadog.logger.warn(
63
63
  "Cannot parse Span Sampling Rule #{hash.inspect}: " \
64
- "#{e.class.name} #{e} at #{Array(e.backtrace).first}"
64
+ "#{e.class}: #{e.message} at #{Array(e.backtrace).first}"
65
65
  )
66
66
  return nil
67
67
  end
@@ -158,7 +158,7 @@ module Datadog
158
158
  begin
159
159
  start
160
160
  rescue => e
161
- logger.debug { "Failed to start span: #{e}" }
161
+ logger.debug { "Failed to start span: #{e.class}: #{e.message}" }
162
162
  ensure
163
163
  # We should yield to the provided block when possible, as this
164
164
  # block is application code that we don't want to hinder.
@@ -459,7 +459,7 @@ module Datadog
459
459
  rescue => e
460
460
  logger.debug do
461
461
  "Custom on_error handler #{@handler} failed, using fallback behavior. \
462
- Cause: #{e.class}: #{e} Location: #{Array(e.backtrace).first}"
462
+ Cause: #{e.class}: #{e.message} Location: #{Array(e.backtrace).first}"
463
463
  end
464
464
 
465
465
  original&.call(op, error)
@@ -471,7 +471,7 @@ module Datadog
471
471
  @handler.call(*args)
472
472
  rescue => e
473
473
  logger.debug do
474
- "Error in on_error handler '#{@handler}': #{e.class}: #{e} at #{Array(e.backtrace).first}"
474
+ "Error in on_error handler '#{@handler}': #{e.class}: #{e.message} at #{Array(e.backtrace).first}"
475
475
  end
476
476
  end
477
477
 
@@ -181,7 +181,7 @@ module Datadog
181
181
 
182
182
  events.trace_resource_change.publish(self)
183
183
  rescue => e
184
- logger.debug { "Error updating trace resource: #{e} Backtrace: #{e.backtrace.first(3)}" }
184
+ logger.debug { "Error updating trace resource: #{e.class}: #{e.message} Backtrace: #{e.backtrace.first(3)}" }
185
185
  end
186
186
 
187
187
  def name
@@ -322,7 +322,7 @@ module Datadog
322
322
  id: id
323
323
  )
324
324
  rescue => e
325
- logger.debug { "Failed to build new span: #{e}" }
325
+ logger.debug { "Failed to build new span: #{e.class}: #{e.message}" }
326
326
 
327
327
  # Return dummy span
328
328
  SpanOperation.new(op_name, logger: logger)
@@ -543,7 +543,7 @@ module Datadog
543
543
  # Publish :span_before_start event
544
544
  events.span_before_start.publish(span_op, self)
545
545
  rescue => e
546
- logger.debug { "Error starting span on trace: #{e} Backtrace: #{e.backtrace.first(3)}" }
546
+ logger.debug { "Error starting span on trace: #{e.class}: #{e.message} Backtrace: #{e.backtrace.first(3)}" }
547
547
  end
548
548
 
549
549
  # For traces with automatic context management (auto_finish),
@@ -572,7 +572,7 @@ module Datadog
572
572
  # Publish :trace_finished event
573
573
  events.trace_finished.publish(self) if finished?
574
574
  rescue => e
575
- logger.debug { "Error finishing span on trace: #{e} Backtrace: #{e.backtrace.first(3)}" }
575
+ logger.debug { "Error finishing span on trace: #{e.class}: #{e.message} Backtrace: #{e.backtrace.first(3)}" }
576
576
  end
577
577
 
578
578
  # Track the root {SpanOperation} object from the current execution context.
@@ -153,7 +153,7 @@ module Datadog
153
153
  active_trace
154
154
  end
155
155
  rescue => e
156
- logger.debug { "Failed to trace: #{e}" }
156
+ logger.debug { "Failed to trace: #{e.class}: #{e.message}" }
157
157
 
158
158
  # Tracing failed: fallback and run code without tracing.
159
159
  return skip_trace(name, &block)
@@ -303,7 +303,7 @@ module Datadog
303
303
  @sampler.sample!(trace_op) if trace_op.sampling_priority.nil?
304
304
  rescue => e
305
305
  SAMPLE_TRACE_LOG_ONLY_ONCE.run do
306
- logger.warn { "Failed to sample trace: #{e.class.name} #{e} at #{Array(e.backtrace).first}" }
306
+ logger.warn { "Failed to sample trace: #{e.class}: #{e.message} at #{Array(e.backtrace).first}" }
307
307
  end
308
308
  end
309
309
 
@@ -315,7 +315,7 @@ module Datadog
315
315
  rescue => e
316
316
  RECONSIDER_RESOURCE_SAMPLE_TRACE_LOG_ONLY_ONCE.run do
317
317
  logger.warn do
318
- "Failed to reconsider trace sampling: #{e.class.name} #{e} at #{Array(e.backtrace).first}"
318
+ "Failed to reconsider trace sampling: #{e.class}: #{e.message} at #{Array(e.backtrace).first}"
319
319
  end
320
320
  end
321
321
  end
@@ -563,7 +563,7 @@ module Datadog
563
563
  @span_sampler.sample!(trace_op, span)
564
564
  rescue => e
565
565
  SAMPLE_SPAN_LOG_ONLY_ONCE.run do
566
- logger.warn { "Failed to sample span: #{e.class.name} #{e} at #{Array(e.backtrace).first}" }
566
+ logger.warn { "Failed to sample span: #{e.class}: #{e.message} at #{Array(e.backtrace).first}" }
567
567
  end
568
568
  end
569
569
 
@@ -576,7 +576,7 @@ module Datadog
576
576
  write(trace) if trace && !trace.empty?
577
577
  rescue => e
578
578
  FLUSH_TRACE_LOG_ONLY_ONCE.run do
579
- logger.warn { "Failed to flush trace: #{e.class.name} #{e} at #{Array(e.backtrace).first}" }
579
+ logger.warn { "Failed to flush trace: #{e.class}: #{e.message} at #{Array(e.backtrace).first}" }
580
580
  end
581
581
  end
582
582
 
@@ -44,7 +44,7 @@ module Datadog
44
44
  response
45
45
  rescue => e
46
46
  message =
47
- "Internal error during IO transport request. Cause: #{e.class}: #{e} " \
47
+ "Internal error during IO transport request. Cause: #{e.class}: #{e.message} " \
48
48
  "Location: #{Array(e.backtrace).first}"
49
49
 
50
50
  # Log error