datadog 2.35.0 → 2.37.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 (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +85 -1
  3. data/ext/datadog_profiling_native_extension/clock_id_from_mach.c +5 -12
  4. data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +71 -31
  5. data/ext/datadog_profiling_native_extension/collectors_discrete_dynamic_sampler.c +1 -1
  6. data/ext/datadog_profiling_native_extension/collectors_idle_sampling_helper.c +18 -4
  7. data/ext/datadog_profiling_native_extension/collectors_stack.c +37 -18
  8. data/ext/datadog_profiling_native_extension/collectors_stack.h +8 -2
  9. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +596 -341
  10. data/ext/datadog_profiling_native_extension/collectors_thread_context.h +11 -7
  11. data/ext/datadog_profiling_native_extension/datadog_ruby_common.c +7 -8
  12. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +0 -12
  13. data/ext/datadog_profiling_native_extension/extconf.rb +13 -2
  14. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.c +4 -43
  15. data/ext/datadog_profiling_native_extension/gvl_profiling_helper.h +15 -47
  16. data/ext/datadog_profiling_native_extension/heap_recorder.c +44 -26
  17. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +14 -35
  18. data/ext/datadog_profiling_native_extension/profiling.c +41 -4
  19. data/ext/datadog_profiling_native_extension/ruby_helpers.c +40 -34
  20. data/ext/datadog_profiling_native_extension/stack_recorder.c +24 -3
  21. data/ext/datadog_profiling_native_extension/stack_recorder.h +1 -0
  22. data/ext/datadog_profiling_native_extension/unsafe_api_calls_check.h +4 -2
  23. data/ext/libdatadog_api/datadog_ruby_common.c +7 -8
  24. data/ext/libdatadog_api/datadog_ruby_common.h +0 -12
  25. data/ext/libdatadog_api/di.c +76 -0
  26. data/ext/libdatadog_api/extconf.rb +6 -0
  27. data/ext/libdatadog_extconf_helpers.rb +1 -1
  28. data/lib/datadog/ai_guard/api_client.rb +6 -4
  29. data/lib/datadog/appsec/api_security/route_extractor.rb +13 -4
  30. data/lib/datadog/appsec/component.rb +1 -1
  31. data/lib/datadog/appsec/configuration.rb +13 -0
  32. data/lib/datadog/appsec/context.rb +4 -2
  33. data/lib/datadog/appsec/contrib/aws_lambda/waf_addresses.rb +37 -4
  34. data/lib/datadog/appsec/contrib/excon/ssrf_detection_middleware.rb +59 -4
  35. data/lib/datadog/appsec/contrib/faraday/ssrf_detection_middleware.rb +59 -5
  36. data/lib/datadog/appsec/contrib/graphql/gateway/multiplex.rb +64 -19
  37. data/lib/datadog/appsec/contrib/graphql/integration.rb +1 -0
  38. data/lib/datadog/appsec/contrib/rack/buffered_input.rb +83 -0
  39. data/lib/datadog/appsec/contrib/rack/gateway/request.rb +41 -3
  40. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +24 -7
  41. data/lib/datadog/appsec/contrib/rack/input_peeker.rb +79 -0
  42. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +21 -0
  43. data/lib/datadog/appsec/contrib/rails/gateway/request.rb +35 -1
  44. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +17 -1
  45. data/lib/datadog/appsec/contrib/rest_client/request_ssrf_detection_patch.rb +60 -7
  46. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +24 -3
  47. data/lib/datadog/appsec/default_header_tags.rb +10 -6
  48. data/lib/datadog/appsec/ext.rb +1 -0
  49. data/lib/datadog/appsec/metrics/collector.rb +18 -1
  50. data/lib/datadog/appsec/metrics/exporter.rb +9 -3
  51. data/lib/datadog/appsec/remote.rb +9 -3
  52. data/lib/datadog/appsec/route_normalizer/rails_route_pattern.rb +176 -0
  53. data/lib/datadog/appsec/route_normalizer/route_pattern.rb +378 -0
  54. data/lib/datadog/appsec/route_normalizer/route_text.rb +57 -0
  55. data/lib/datadog/appsec/route_normalizer.rb +80 -0
  56. data/lib/datadog/appsec/utils/http/body_reader.rb +61 -0
  57. data/lib/datadog/core/configuration/components.rb +83 -2
  58. data/lib/datadog/core/configuration/components_state.rb +6 -1
  59. data/lib/datadog/core/configuration/settings.rb +1 -5
  60. data/lib/datadog/core/configuration/supported_configurations.rb +4 -0
  61. data/lib/datadog/core/evp.rb +11 -0
  62. data/lib/datadog/core/remote/client/capabilities.rb +34 -7
  63. data/lib/datadog/core/remote/component.rb +1 -1
  64. data/lib/datadog/core/telemetry/event/app_started.rb +8 -22
  65. data/lib/datadog/core/utils/at_fork_monkey_patch.rb +1 -1
  66. data/lib/datadog/core/utils/forking.rb +3 -1
  67. data/lib/datadog/core/utils/spawn_monkey_patch.rb +3 -1
  68. data/lib/datadog/core.rb +3 -0
  69. data/lib/datadog/di/base.rb +7 -2
  70. data/lib/datadog/di/code_tracker.rb +5 -2
  71. data/lib/datadog/di/component.rb +110 -36
  72. data/lib/datadog/di/error.rb +10 -0
  73. data/lib/datadog/di/fatal_exceptions.rb +26 -0
  74. data/lib/datadog/di/instrumenter.rb +381 -165
  75. data/lib/datadog/di/probe.rb +14 -0
  76. data/lib/datadog/di/probe_file_loader.rb +8 -3
  77. data/lib/datadog/di/probe_manager.rb +67 -9
  78. data/lib/datadog/di/probe_notification_builder.rb +4 -1
  79. data/lib/datadog/di/probe_notifier_worker.rb +52 -32
  80. data/lib/datadog/di/redactor.rb +16 -1
  81. data/lib/datadog/di/remote.rb +175 -14
  82. data/lib/datadog/di/serializer.rb +12 -5
  83. data/lib/datadog/di/transport/input.rb +8 -4
  84. data/lib/datadog/di.rb +63 -0
  85. data/lib/datadog/error_tracking/collector.rb +2 -1
  86. data/lib/datadog/error_tracking/component.rb +2 -2
  87. data/lib/datadog/kit/appsec/events/v2.rb +60 -2
  88. data/lib/datadog/kit/tracing/method_tracer.rb +4 -1
  89. data/lib/datadog/open_feature/component.rb +29 -6
  90. data/lib/datadog/open_feature/configuration.rb +8 -0
  91. data/lib/datadog/open_feature/ext.rb +2 -0
  92. data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +286 -0
  93. data/lib/datadog/open_feature/flag_evaluation/writer.rb +433 -0
  94. data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +101 -0
  95. data/lib/datadog/open_feature/hooks/{flag_eval_hook.rb → flag_eval_metrics_hook.rb} +1 -1
  96. data/lib/datadog/open_feature/native_evaluator.rb +22 -0
  97. data/lib/datadog/open_feature/provider.rb +59 -27
  98. data/lib/datadog/open_feature/transport.rb +55 -1
  99. data/lib/datadog/opentelemetry/sdk/logs_exporter.rb +5 -12
  100. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +4 -11
  101. data/lib/datadog/opentelemetry/sdk/propagator.rb +9 -3
  102. data/lib/datadog/opentelemetry/sdk/span_processor.rb +4 -1
  103. data/lib/datadog/opentelemetry/sdk.rb +19 -0
  104. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
  105. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +5 -3
  106. data/lib/datadog/profiling/collectors/thread_context.rb +1 -0
  107. data/lib/datadog/profiling/component.rb +13 -15
  108. data/lib/datadog/profiling/ext/dir_monkey_patches.rb +3 -3
  109. data/lib/datadog/ruby_version.rb +25 -0
  110. data/lib/datadog/symbol_database/component.rb +453 -128
  111. data/lib/datadog/symbol_database/configuration.rb +12 -4
  112. data/lib/datadog/symbol_database/extensions.rb +19 -0
  113. data/lib/datadog/symbol_database/extractor.rb +326 -150
  114. data/lib/datadog/symbol_database/file_hash.rb +3 -1
  115. data/lib/datadog/symbol_database/remote.rb +8 -3
  116. data/lib/datadog/symbol_database/scope_batcher.rb +7 -3
  117. data/lib/datadog/symbol_database/uploader.rb +9 -2
  118. data/lib/datadog/symbol_database.rb +22 -0
  119. data/lib/datadog/tracing/configuration/ext.rb +13 -0
  120. data/lib/datadog/tracing/configuration/settings.rb +17 -0
  121. data/lib/datadog/tracing/contrib/configuration/resolver.rb +7 -0
  122. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +2 -0
  123. data/lib/datadog/tracing/contrib/grpc.rb +1 -0
  124. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +2 -0
  125. data/lib/datadog/tracing/contrib/http.rb +1 -0
  126. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +2 -0
  127. data/lib/datadog/tracing/contrib/karafka.rb +1 -0
  128. data/lib/datadog/tracing/contrib/rack/middlewares.rb +3 -1
  129. data/lib/datadog/tracing/contrib/rack/route_inference.rb +3 -1
  130. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +2 -0
  131. data/lib/datadog/tracing/contrib/sidekiq.rb +1 -0
  132. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +2 -0
  133. data/lib/datadog/tracing/contrib/waterdrop.rb +1 -0
  134. data/lib/datadog/tracing/distributed/baggage.rb +2 -1
  135. data/lib/datadog/tracing/distributed/propagation.rb +33 -1
  136. data/lib/datadog/tracing/distributed/trace_context.rb +11 -2
  137. data/lib/datadog/tracing/ext.rb +9 -0
  138. data/lib/datadog/tracing/remote.rb +34 -3
  139. data/lib/datadog/tracing/trace_digest.rb +7 -0
  140. data/lib/datadog/tracing/trace_operation.rb +4 -1
  141. data/lib/datadog/tracing/tracer.rb +1 -0
  142. data/lib/datadog/version.rb +1 -1
  143. data/lib/datadog.rb +5 -1
  144. metadata +22 -6
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'digest/sha1'
4
+ require_relative '../di/fatal_exceptions'
4
5
 
5
6
  module Datadog
6
7
  module SymbolDatabase
@@ -37,7 +38,8 @@ module Datadog
37
38
  # This is not a security vulnerability - we're computing file content hashes
38
39
  # to match against Git objects, not using SHA-1 for authentication/integrity.
39
40
  Digest::SHA1.hexdigest(git_blob) # nosemgrep: ruby.lang.security.weak-hashes-sha1.weak-hashes-sha1
40
- rescue => e
41
+ rescue Exception => e # standard:disable Lint/RescueException
42
+ Datadog::DI.reraise_if_fatal(e)
41
43
  logger.debug { "symdb: file hash failed for #{file_path}: #{e.class}: #{e.message}" }
42
44
  nil
43
45
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../di/fatal_exceptions'
4
+
3
5
  module Datadog
4
6
  module SymbolDatabase
5
7
  # Provides remote configuration integration for symbol database.
@@ -44,7 +46,8 @@ module Datadog
44
46
  telemetry = lookup_telemetry
45
47
  component = begin
46
48
  Datadog.send(:components, allow_initialization: false)&.symbol_database
47
- rescue => e
49
+ rescue Exception => e # standard:disable Lint/RescueException
50
+ Datadog::DI.reraise_if_fatal(e)
48
51
  Datadog.logger.debug { "symdb: failed to look up component in RC receiver: #{e.class}: #{e.message}" }
49
52
  telemetry&.report(e, description: 'symdb: failed to look up component in RC receiver')
50
53
  nil
@@ -76,7 +79,8 @@ module Datadog
76
79
  # @api private
77
80
  def lookup_telemetry
78
81
  Datadog.send(:components, allow_initialization: false)&.telemetry
79
- rescue
82
+ rescue Exception => e # standard:disable Lint/RescueException
83
+ Datadog::DI.reraise_if_fatal(e)
80
84
  nil
81
85
  end
82
86
 
@@ -107,7 +111,8 @@ module Datadog
107
111
  # the Repository::Change union type where `Deleted` lacks `content`.
108
112
  change.content.errored("Unrecognized change type: #{change.type}") if change.respond_to?(:content) # steep:ignore NoMethod
109
113
  end
110
- rescue => e
114
+ rescue Exception => e # standard:disable Lint/RescueException
115
+ Datadog::DI.reraise_if_fatal(e)
111
116
  component.logger.debug { "symdb: error processing remote config change: #{e.class}: #{e.message}" }
112
117
  telemetry&.report(e, description: 'symdb: error processing remote config change')
113
118
  # Rescue runs regardless of which branch raised — Steep cannot narrow the
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'set'
4
+ require_relative '../di/fatal_exceptions'
4
5
 
5
6
  module Datadog
6
7
  module SymbolDatabase
@@ -122,7 +123,8 @@ module Datadog
122
123
 
123
124
  # Upload outside mutex (if batch was full)
124
125
  perform_upload(scopes_to_upload) if scopes_to_upload
125
- rescue => e
126
+ rescue Exception => e # standard:disable Lint/RescueException
127
+ Datadog::DI.reraise_if_fatal(e)
126
128
  @logger.debug { "symdb: failed to add scope: #{e.class}: #{e.message}" }
127
129
  # Don't propagate, continue operation
128
130
  end
@@ -267,7 +269,8 @@ module Datadog
267
269
  flush
268
270
  end
269
271
  end
270
- rescue => e
272
+ rescue Exception => e # standard:disable Lint/RescueException
273
+ Datadog::DI.reraise_if_fatal(e)
271
274
  @logger.debug { "symdb: timer thread error: #{e.class}: #{e.message}" }
272
275
  end
273
276
 
@@ -279,7 +282,8 @@ module Datadog
279
282
 
280
283
  @uploader.upload_scopes(scopes)
281
284
  @on_upload&.call(scopes) # Notify tests after upload
282
- rescue => e
285
+ rescue Exception => e # standard:disable Lint/RescueException
286
+ Datadog::DI.reraise_if_fatal(e)
283
287
  @logger.debug { "symdb: upload failed: #{e.class}: #{e.message}" }
284
288
  # Don't propagate, uploader handles retries
285
289
  end
@@ -6,8 +6,10 @@ require 'zlib'
6
6
  require 'stringio'
7
7
  require_relative '../core/environment/identity'
8
8
  require_relative '../core/vendor/multipart-post/multipart/post/composite_read_io'
9
+ require_relative '../tracing/ext'
9
10
  require_relative 'service_version'
10
11
  require_relative 'transport/http'
12
+ require_relative '../di/fatal_exceptions'
11
13
 
12
14
  module Datadog
13
15
  module SymbolDatabase
@@ -68,7 +70,11 @@ module Datadog
68
70
  compressed_data = Zlib.gzip(json_data)
69
71
 
70
72
  # Emitted unconditionally so the rare oversized case is observable.
71
- @telemetry&.distribution('tracers', 'symbol_database.payload_size', compressed_data.bytesize)
73
+ @telemetry&.distribution(
74
+ Tracing::Ext::TELEMETRY_METRICS_NAMESPACE,
75
+ 'symbol_database.payload_size',
76
+ compressed_data.bytesize
77
+ )
72
78
 
73
79
  # Symbols for very large applications (>50MB after gzip) are dropped:
74
80
  # the upload is skipped and the customer sees no autocomplete /
@@ -81,7 +87,8 @@ module Datadog
81
87
  end
82
88
 
83
89
  perform_http_upload(compressed_data, scopes.size, upload_id: upload_id, batch_num: batch_num)
84
- rescue => e
90
+ rescue Exception => e # standard:disable Lint/RescueException
91
+ Datadog::DI.reraise_if_fatal(e)
85
92
  @logger.debug { "symdb: upload failed: #{e.class}: #{e.message}" }
86
93
  @telemetry&.report(e, description: 'symdb: upload failed')
87
94
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'ruby_version'
4
+
3
5
  module Datadog
4
6
  # Namespace for Datadog symbol database upload.
5
7
  #
@@ -45,5 +47,25 @@ module Datadog
45
47
  # Reference: Symbol Database Backend RFC, section "Scope" and "Edge Cases"
46
48
  # @see https://www.postgresql.org/docs/current/datatype-numeric.html
47
49
  UNKNOWN_MAX_LINE = 2147483647
50
+
51
+ # Collapses the symbol_database.enabled tri-state setting to a boolean.
52
+ # An explicit true/false wins; nil (unconfigured) yields the caller-supplied
53
+ # fallback.
54
+ # @param setting_value [Boolean, nil] the symbol_database.enabled setting
55
+ # @param di_fallback [Boolean] value used when the setting is unconfigured
56
+ # @return [Boolean]
57
+ def self.resolve_enabled(setting_value, di_fallback)
58
+ setting_value.nil? ? di_fallback : setting_value
59
+ end
60
+
61
+ # Whether the current Ruby runtime can run symbol database extraction:
62
+ # MRI (CRuby) on Ruby 2.7 or later. Used by the remote-config layer to avoid
63
+ # advertising the product on runtimes where Component.build would return nil
64
+ # (e.g. Ruby 2.6, which Dynamic Instrumentation supports but Symbol Database
65
+ # does not).
66
+ # @return [Boolean]
67
+ def self.supported_runtime?
68
+ RUBY_ENGINE == 'ruby' && RubyVersion.is?('>= 2.7')
69
+ end
48
70
  end
49
71
  end
@@ -62,6 +62,19 @@ module Datadog
62
62
 
63
63
  ENV_PROPAGATION_STYLE_EXTRACT = 'DD_TRACE_PROPAGATION_STYLE_EXTRACT'
64
64
 
65
+ PROPAGATION_BEHAVIOR_EXTRACT_CONTINUE = 'continue'
66
+ PROPAGATION_BEHAVIOR_EXTRACT_RESTART = 'restart'
67
+ PROPAGATION_BEHAVIOR_EXTRACT_IGNORE = 'ignore'
68
+
69
+ PROPAGATION_BEHAVIOR_EXTRACT_SUPPORTED = [
70
+ PROPAGATION_BEHAVIOR_EXTRACT_CONTINUE,
71
+ PROPAGATION_BEHAVIOR_EXTRACT_RESTART,
72
+ PROPAGATION_BEHAVIOR_EXTRACT_IGNORE,
73
+ ].freeze
74
+
75
+ # Behavior applied to a distributed-trace context extracted from incoming requests.
76
+ ENV_PROPAGATION_BEHAVIOR_EXTRACT = 'DD_TRACE_PROPAGATION_BEHAVIOR_EXTRACT'
77
+
65
78
  # A no-op propagator. Compatible with OpenTelemetry's `none` propagator.
66
79
  # @see https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#get_otel__propagators
67
80
  PROPAGATION_STYLE_NONE = 'none'
@@ -117,6 +117,23 @@ module Datadog
117
117
  end
118
118
  end
119
119
 
120
+ # Behavior applied to a distributed-trace context extracted from incoming requests.
121
+ option :propagation_behavior_extract do |o|
122
+ o.env Tracing::Configuration::Ext::Distributed::ENV_PROPAGATION_BEHAVIOR_EXTRACT
123
+ o.default Tracing::Configuration::Ext::Distributed::PROPAGATION_BEHAVIOR_EXTRACT_CONTINUE
124
+ o.type :string
125
+ o.env_parser do |value|
126
+ value = value&.downcase
127
+ if Tracing::Configuration::Ext::Distributed::PROPAGATION_BEHAVIOR_EXTRACT_SUPPORTED.include?(value)
128
+ value
129
+ else
130
+ Datadog.logger.warn("Unsupported propagation behavior extract: #{value.inspect}. " \
131
+ "Set to default value (#{Tracing::Configuration::Ext::Distributed::PROPAGATION_BEHAVIOR_EXTRACT_CONTINUE}).")
132
+ nil
133
+ end
134
+ end
135
+ end
136
+
120
137
  # Strictly stop at the first successfully serialized style.
121
138
  # This prevents the tracer from enriching the extracted context with information from
122
139
  # other valid propagations styles present in the request.
@@ -101,6 +101,13 @@ module Datadog
101
101
  # shrink a Hash using an "st_table" back to an "ar_table")
102
102
  @cache = Hash[*1..20]
103
103
  @cache.clear
104
+ # Workaround for the segfault from https://github.com/DataDog/dd-trace-rb/issues/5718#issuecomment-4421844775.
105
+ # It crashes on a simple {Hash} lookup, {Hash#key?}, called from `@cache.fetch(value)`.
106
+ # Using an identity-based {Hash} avoids {Hash#key?} calls.
107
+ # We should attempt to remove this workaround when we only support Ruby 4+,
108
+ # as large change around the crash site was done in that version (https://github.com/ruby/ruby/pull/14039/changes#diff-884a5a8a369ef1b4c7597e00aa65974cec8c5f54f25f03ad5d24848f64892869R1743),
109
+ # where `RClass.cc_table` (the NULL dereferenced pointer) became a GC-managed object,
110
+ @cache.compare_by_identity
104
111
  end
105
112
 
106
113
  # (see Resolver#resolve)
@@ -20,6 +20,7 @@ module Datadog
20
20
  def initialize(
21
21
  propagation_style_inject:,
22
22
  propagation_style_extract:,
23
+ propagation_behavior_extract:,
23
24
  propagation_extract_first:
24
25
  )
25
26
  super(
@@ -38,6 +39,7 @@ module Datadog
38
39
  },
39
40
  propagation_style_inject: propagation_style_inject,
40
41
  propagation_style_extract: propagation_style_extract,
42
+ propagation_behavior_extract: propagation_behavior_extract,
41
43
  propagation_extract_first: propagation_extract_first
42
44
  )
43
45
  end
@@ -36,6 +36,7 @@ module Datadog
36
36
  @propagation = GRPC::Distributed::Propagation.new(
37
37
  propagation_style_inject: tracing.propagation_style_inject,
38
38
  propagation_style_extract: tracing.propagation_style_extract,
39
+ propagation_behavior_extract: tracing.propagation_behavior_extract,
39
40
  propagation_extract_first: tracing.propagation_extract_first
40
41
  )
41
42
  end
@@ -19,6 +19,7 @@ module Datadog
19
19
  def initialize(
20
20
  propagation_style_inject:,
21
21
  propagation_style_extract:,
22
+ propagation_behavior_extract:,
22
23
  propagation_extract_first:
23
24
  )
24
25
  super(
@@ -37,6 +38,7 @@ module Datadog
37
38
  },
38
39
  propagation_style_inject: propagation_style_inject,
39
40
  propagation_style_extract: propagation_style_extract,
41
+ propagation_behavior_extract: propagation_behavior_extract,
40
42
  propagation_extract_first: propagation_extract_first
41
43
  )
42
44
  end
@@ -36,6 +36,7 @@ module Datadog
36
36
  @propagation = HTTP::Distributed::Propagation.new(
37
37
  propagation_style_inject: tracing.propagation_style_inject,
38
38
  propagation_style_extract: tracing.propagation_style_extract,
39
+ propagation_behavior_extract: tracing.propagation_behavior_extract,
39
40
  propagation_extract_first: tracing.propagation_extract_first
40
41
  )
41
42
  end
@@ -19,6 +19,7 @@ module Datadog
19
19
  def initialize(
20
20
  propagation_style_inject:,
21
21
  propagation_style_extract:,
22
+ propagation_behavior_extract:,
22
23
  propagation_extract_first:
23
24
  )
24
25
  super(
@@ -37,6 +38,7 @@ module Datadog
37
38
  },
38
39
  propagation_style_inject: propagation_style_inject,
39
40
  propagation_style_extract: propagation_style_extract,
41
+ propagation_behavior_extract: propagation_behavior_extract,
40
42
  propagation_extract_first: propagation_extract_first
41
43
  )
42
44
  end
@@ -28,6 +28,7 @@ module Datadog
28
28
  @propagation = Karafka::Distributed::Propagation.new(
29
29
  propagation_style_inject: tracing.propagation_style_inject,
30
30
  propagation_style_extract: tracing.propagation_style_extract,
31
+ propagation_behavior_extract: tracing.propagation_behavior_extract,
31
32
  propagation_extract_first: tracing.propagation_extract_first
32
33
  )
33
34
  end
@@ -268,7 +268,9 @@ module Datadog
268
268
  # 1. resource renaming is enabled
269
269
  # 2. AppSec is enabled and resource renaming is disabled (by default, not explicitly)
270
270
  if Datadog.configuration.tracing.resource_renaming.enabled ||
271
- Datadog.configuration.appsec.enabled && Datadog.configuration.tracing.resource_renaming.options[:enabled].default_precedence?
271
+ Datadog.configuration.respond_to?(:appsec) &&
272
+ Datadog.configuration.appsec&.enabled &&
273
+ Datadog.configuration.tracing.resource_renaming.options[:enabled].default_precedence?
272
274
  request_span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_ENDPOINT, value)
273
275
  end
274
276
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../../ruby_version'
4
+
3
5
  module Datadog
4
6
  module Tracing
5
7
  module Contrib
@@ -48,7 +50,7 @@ module Datadog
48
50
  nil
49
51
  end
50
52
 
51
- if RUBY_VERSION >= '2.6.'
53
+ if RubyVersion.is?('>= 2.6')
52
54
  def split(path, pattern = nil, &block)
53
55
  path.split(pattern, &block)
54
56
  end
@@ -20,6 +20,7 @@ module Datadog
20
20
  def initialize(
21
21
  propagation_style_inject:,
22
22
  propagation_style_extract:,
23
+ propagation_behavior_extract:,
23
24
  propagation_extract_first:
24
25
  )
25
26
  super(
@@ -38,6 +39,7 @@ module Datadog
38
39
  },
39
40
  propagation_style_inject: propagation_style_inject,
40
41
  propagation_style_extract: propagation_style_extract,
42
+ propagation_behavior_extract: propagation_behavior_extract,
41
43
  propagation_extract_first: propagation_extract_first
42
44
  )
43
45
  end
@@ -28,6 +28,7 @@ module Datadog
28
28
  @propagation = Sidekiq::Distributed::Propagation.new(
29
29
  propagation_style_inject: tracing.propagation_style_inject,
30
30
  propagation_style_extract: tracing.propagation_style_extract,
31
+ propagation_behavior_extract: tracing.propagation_behavior_extract,
31
32
  propagation_extract_first: tracing.propagation_extract_first
32
33
  )
33
34
  end
@@ -19,6 +19,7 @@ module Datadog
19
19
  def initialize(
20
20
  propagation_style_inject:,
21
21
  propagation_style_extract:,
22
+ propagation_behavior_extract:,
22
23
  propagation_extract_first:
23
24
  )
24
25
  super(
@@ -37,6 +38,7 @@ module Datadog
37
38
  },
38
39
  propagation_style_inject: propagation_style_inject,
39
40
  propagation_style_extract: propagation_style_extract,
41
+ propagation_behavior_extract: propagation_behavior_extract,
40
42
  propagation_extract_first: propagation_extract_first
41
43
  )
42
44
  end
@@ -32,6 +32,7 @@ module Datadog
32
32
  @propagation = WaterDrop::Distributed::Propagation.new(
33
33
  propagation_style_inject: tracing.propagation_style_inject,
34
34
  propagation_style_extract: tracing.propagation_style_extract,
35
+ propagation_behavior_extract: tracing.propagation_behavior_extract,
35
36
  propagation_extract_first: tracing.propagation_extract_first
36
37
  )
37
38
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../ext'
3
4
  require_relative '../metadata/ext'
4
5
  require_relative '../trace_digest'
5
6
  require_relative 'datadog_tags_codec'
@@ -243,7 +244,7 @@ module Datadog
243
244
  # Record telemetry metrics for baggage operations
244
245
  def record_telemetry_metric(metric_name, value, tags)
245
246
  telemetry = ::Datadog.send(:components).telemetry
246
- telemetry.inc('instrumentation_telemetry_data.tracers', metric_name, value, tags: tags)
247
+ telemetry.inc(Tracing::Ext::TELEMETRY_METRICS_NAMESPACE, metric_name, value, tags: tags)
247
248
  end
248
249
  end
249
250
  end
@@ -3,6 +3,7 @@
3
3
  require_relative '../configuration/ext'
4
4
  require_relative '../trace_digest'
5
5
  require_relative '../trace_operation'
6
+ require_relative '../span_link'
6
7
  require_relative '../../core/telemetry/logger'
7
8
  require_relative 'baggage'
8
9
 
@@ -13,6 +14,8 @@ module Datadog
13
14
  class Propagation
14
15
  # @param propagation_styles [Hash<String,Object>]
15
16
  # a map of propagation styles to their corresponding implementations
17
+ # @param propagation_behavior_extract [String]
18
+ # the behavior to apply when extracting distributed trace data
16
19
  # @param propagation_style_inject [Array<String>]
17
20
  # a list of styles to use when injecting distributed trace data
18
21
  # @param propagation_style_extract [Array<String>]
@@ -23,9 +26,11 @@ module Datadog
23
26
  propagation_styles:,
24
27
  propagation_style_inject:,
25
28
  propagation_style_extract:,
29
+ propagation_behavior_extract:,
26
30
  propagation_extract_first:
27
31
  )
28
32
  @propagation_styles = propagation_styles
33
+ @propagation_behavior_extract = propagation_behavior_extract
29
34
  @propagation_extract_first = propagation_extract_first
30
35
  @propagation_style_inject = propagation_style_inject.map { |style| propagation_styles[style] }
31
36
  @propagation_style_extract = propagation_style_extract.map { |style| propagation_styles[style] }
@@ -97,12 +102,20 @@ module Datadog
97
102
  return unless data
98
103
  return if data.empty?
99
104
 
105
+ if @propagation_behavior_extract == Tracing::Configuration::Ext::Distributed::PROPAGATION_BEHAVIOR_EXTRACT_IGNORE
106
+ return nil
107
+ end
108
+
100
109
  extracted_trace_digest = nil
110
+ # Name of the propagation style that first produced a trace context.
111
+ # Used as the `context_headers` span-link attribute when restarting a trace.
112
+ extracted_style_name = nil
101
113
 
102
114
  @propagation_style_extract.each do |propagator|
103
115
  # First extraction?
104
116
  unless extracted_trace_digest
105
117
  extracted_trace_digest = propagator.extract(data)
118
+ extracted_style_name = @propagation_styles.key(propagator) if extracted_trace_digest
106
119
  next
107
120
  end
108
121
 
@@ -145,7 +158,26 @@ module Datadog
145
158
  # Handle baggage after all other styles if present
146
159
  extracted_trace_digest = propagate_baggage(data, extracted_trace_digest) if @baggage_propagator
147
160
 
148
- extracted_trace_digest
161
+ # Default mode: Current digest continues the upstream trace.
162
+ return extracted_trace_digest if @propagation_behavior_extract != Tracing::Configuration::Ext::Distributed::PROPAGATION_BEHAVIOR_EXTRACT_RESTART || extracted_trace_digest&.trace_id.nil?
163
+
164
+ # Restart mode: start a new local digest and create a Span Link on
165
+ # the service-entry span with the original propagation data.
166
+ link = SpanLink.new(
167
+ extracted_trace_digest,
168
+ attributes: {
169
+ 'reason' => 'propagation_behavior_extract',
170
+ 'context_headers' => extracted_style_name,
171
+ }
172
+ )
173
+ baggage_tags = extracted_trace_digest.trace_distributed_tags&.select { |k, _| k.start_with?('baggage.') }
174
+ baggage_tags = nil if baggage_tags&.empty?
175
+ TraceDigest.new(
176
+ span_links: [link],
177
+ baggage: extracted_trace_digest.baggage,
178
+ trace_distributed_tags: baggage_tags,
179
+ span_remote: false,
180
+ )
149
181
  end
150
182
 
151
183
  private
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../core/utils'
4
+ require_relative 'helpers'
5
+
3
6
  module Datadog
4
7
  module Tracing
5
8
  module Distributed
@@ -282,8 +285,11 @@ module Datadog
282
285
  trace_flags & TRACE_FLAGS_SAMPLED
283
286
  end
284
287
 
285
- # @return [Array<String,Integer,String,String,Hash>] returns 4 values:
286
- # tracestate, sampling_priority, ts_parent_id, origin, tags.
288
+ # @return [nil] when the tracestate is absent or has no vendor entries.
289
+ # @return [String] when no `dd=` entry is present, the joined vendor list.
290
+ # @return [Array(String, Integer, String, String, Hash, String)] when a `dd=`
291
+ # entry is present: [tracestate without `dd=`, sampling_priority, origin,
292
+ # ts_parent_id, tags, unknown_fields]. All elements past the first may be nil.
287
293
  def extract_tracestate(tracestate)
288
294
  vendors = split_tracestate(tracestate)
289
295
  return unless vendors && !vendors.empty?
@@ -395,6 +401,9 @@ module Datadog
395
401
  tracestate = tracestate.chop
396
402
  end
397
403
 
404
+ tracestate = ::Datadog::Core::Utils.utf8_encode(tracestate, placeholder: nil)
405
+ return unless tracestate
406
+
398
407
  vendors = tracestate.split(',', TRACESTATE_MAX_LIST_MEMBERS + 1)
399
408
  if vendors.length > TRACESTATE_MAX_LIST_MEMBERS || remove_last_member
400
409
  vendors.pop
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Tracing
5
+ module Ext
6
+ TELEMETRY_METRICS_NAMESPACE = 'tracers'
7
+ end
8
+ end
9
+ end
@@ -15,6 +15,9 @@ module Datadog
15
15
  1 << 13, # APM_TRACING_LOGS_INJECTION: Dynamic trace logs injection configuration
16
16
  1 << 14, # APM_TRACING_HTTP_HEADER_TAGS: Dynamic trace HTTP header tags configuration
17
17
  1 << 29, # APM_TRACING_SAMPLE_RULES: Dynamic trace sampling rules configuration
18
+ # APM_TRACING_ENABLE_DYNAMIC_INSTRUMENTATION (bit 38) is declared in
19
+ # DI::Remote.capabilities, not here, so it is registered only when DI
20
+ # is not explicitly disabled and the runtime supports DI.
18
21
  ].freeze
19
22
 
20
23
  def products
@@ -25,7 +28,7 @@ module Datadog
25
28
  CAPABILITIES
26
29
  end
27
30
 
28
- def process_config(config, content)
31
+ def process_config(config, content, repository = nil)
29
32
  lib_config = config['lib_config']
30
33
 
31
34
  env_vars = Datadog::Tracing::Configuration::Dynamic::OPTIONS.map do |name, env_var, option|
@@ -39,9 +42,37 @@ module Datadog
39
42
  [env_var, value]
40
43
  end
41
44
 
45
+ if (di_enabled = lib_config['dynamic_instrumentation_enabled']) != nil # rubocop:disable Style/NonNilCheck
46
+ # repository is forwarded so that an enable signal can reconcile DI
47
+ # against probes delivered in an earlier poll while DI was stopped
48
+ # (see Datadog::DI::Remote.handle_rc_enablement).
49
+ Datadog::DI::Remote.handle_rc_enablement(di_enabled, repository)
50
+
51
+ if di_enabled
52
+ # DI was just (implicitly) enabled. A Symbol Database upload signal
53
+ # received in an earlier poll while DI was inactive was deferred by
54
+ # the component's DI-active gate; re-attempt it now. Mirrors the DI
55
+ # probe replay in handle_rc_enablement above. allow_initialization:
56
+ # false because this runs on the remote-config worker thread.
57
+ Datadog.send(:components, allow_initialization: false)&.symbol_database&.resume_pending_upload
58
+ else
59
+ # DI was disabled via remote configuration. In the nil-default
60
+ # (follows-DI) case, stop Symbol Database too so its TracePoint and
61
+ # scheduler don't keep uploading while DI is off. An explicit
62
+ # symbol_database.enabled = true is independent and keeps running.
63
+ Datadog.send(:components, allow_initialization: false)&.symbol_database&.stop_for_di_disable
64
+ end
65
+ end
66
+
42
67
  content.applied
43
68
 
44
- Datadog.send(:components).telemetry.client_configuration_change!(env_vars)
69
+ # allow_initialization: false because process_config runs on the
70
+ # remote-config worker thread. If components haven't been built yet
71
+ # (e.g. during a teardown/reset window), the default `true` would
72
+ # synchronously build the entire component tree from this thread.
73
+ # The &. chain matches the pattern used by DI::Remote.handle_rc_enablement
74
+ # in the same dispatch path.
75
+ Datadog.send(:components, allow_initialization: false)&.telemetry&.client_configuration_change!(env_vars)
45
76
  rescue => e
46
77
  content.errored("#{e.class}: #{e.message}: #{Array(e.backtrace).join("\n")}")
47
78
  end
@@ -55,7 +86,7 @@ module Datadog
55
86
  case content.path.product
56
87
  when PRODUCT
57
88
  config = parse_content(content)
58
- process_config(config, content)
89
+ process_config(config, content, repository)
59
90
  end
60
91
  end
61
92
  end
@@ -22,6 +22,9 @@ module Datadog
22
22
  # @!attribute [r] span_type
23
23
  # The type of the currently active span.
24
24
  # @return [String]
25
+ # @!attribute [r] span_links
26
+ # Span links associated with this trace context.
27
+ # @return [Array<Datadog::Tracing::SpanLink>, nil]
25
28
  # @!attribute [r] trace_distributed_tags
26
29
  # Datadog-specific tags that support richer distributed tracing association.
27
30
  # @return [Hash<String,String>]
@@ -91,6 +94,7 @@ module Datadog
91
94
  :span_resource,
92
95
  :span_service,
93
96
  :span_type,
97
+ :span_links,
94
98
  :trace_distributed_tags,
95
99
  :trace_hostname,
96
100
  :trace_id,
@@ -114,6 +118,7 @@ module Datadog
114
118
  span_resource: nil,
115
119
  span_service: nil,
116
120
  span_type: nil,
121
+ span_links: nil,
117
122
  trace_distributed_tags: nil,
118
123
  trace_hostname: nil,
119
124
  trace_id: nil,
@@ -136,6 +141,7 @@ module Datadog
136
141
  @span_resource = span_resource && span_resource.dup.freeze
137
142
  @span_service = span_service && span_service.dup.freeze
138
143
  @span_type = span_type && span_type.dup.freeze
144
+ @span_links = span_links && span_links.dup.freeze
139
145
  @trace_distributed_tags = trace_distributed_tags && trace_distributed_tags.dup.freeze
140
146
  @trace_hostname = trace_hostname && trace_hostname.dup.freeze
141
147
  @trace_id = trace_id
@@ -167,6 +173,7 @@ module Datadog
167
173
  span_resource: span_resource,
168
174
  span_service: span_service,
169
175
  span_type: span_type,
176
+ span_links: span_links,
170
177
  trace_distributed_tags: trace_distributed_tags,
171
178
  trace_hostname: trace_hostname,
172
179
  trace_id: trace_id,
@@ -58,6 +58,7 @@ module Datadog
58
58
 
59
59
  # Creates a new TraceOperation.
60
60
  #
61
+ # @param span_links [Array<Datadog::Tracing::SpanLink>, nil] links to attach to the local root span.
61
62
  # @param auto_finish [Boolean] when true, automatically finishes the trace when the local root span finishes.
62
63
  # When false, the trace remains unfinished until {#finish!} is called.
63
64
  # This is useful when this {TraceOperation} represents the continuation of a remote {TraceDigest},
@@ -72,6 +73,7 @@ module Datadog
72
73
  name: nil,
73
74
  origin: nil,
74
75
  parent_span_id: nil,
76
+ span_links: nil,
75
77
  rate_limiter_rate: nil,
76
78
  resource: nil,
77
79
  rule_sample_rate: nil,
@@ -98,7 +100,7 @@ module Datadog
98
100
  @parent_span_id = parent_span_id
99
101
  @sampled = sampled.nil? || sampled
100
102
  @remote_parent = remote_parent
101
-
103
+ @span_links = span_links
102
104
  # Tags
103
105
  @agent_sample_rate = agent_sample_rate
104
106
  @hostname = hostname
@@ -318,6 +320,7 @@ module Datadog
318
320
  events: span_events,
319
321
  on_error: on_error,
320
322
  parent_id: parent_id,
323
+ links: (@root_span.nil? ? @span_links : nil),
321
324
  resource: resource || op_name,
322
325
  service: service,
323
326
  start_time: start_time,