datadog 2.12.2 → 2.14.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 (125) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -1
  3. data/ext/datadog_profiling_native_extension/collectors_thread_context.c +14 -13
  4. data/lib/datadog/appsec/actions_handler/serializable_backtrace.rb +89 -0
  5. data/lib/datadog/appsec/actions_handler.rb +22 -1
  6. data/lib/datadog/appsec/anonymizer.rb +16 -0
  7. data/lib/datadog/appsec/configuration/settings.rb +62 -10
  8. data/lib/datadog/appsec/contrib/auto_instrument.rb +1 -1
  9. data/lib/datadog/appsec/contrib/devise/configuration.rb +7 -31
  10. data/lib/datadog/appsec/contrib/devise/data_extractor.rb +79 -0
  11. data/lib/datadog/appsec/contrib/devise/ext.rb +21 -0
  12. data/lib/datadog/appsec/contrib/devise/integration.rb +0 -1
  13. data/lib/datadog/appsec/contrib/devise/patcher.rb +36 -23
  14. data/lib/datadog/appsec/contrib/devise/patches/signin_tracking_patch.rb +102 -0
  15. data/lib/datadog/appsec/contrib/devise/patches/signup_tracking_patch.rb +69 -0
  16. data/lib/datadog/appsec/contrib/devise/{patcher/rememberable_patch.rb → patches/skip_signin_tracking_patch.rb} +2 -2
  17. data/lib/datadog/appsec/contrib/devise/tracking_middleware.rb +93 -0
  18. data/lib/datadog/appsec/contrib/rack/ext.rb +14 -0
  19. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +10 -3
  20. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +0 -2
  21. data/lib/datadog/appsec/event.rb +1 -1
  22. data/lib/datadog/appsec/ext.rb +4 -2
  23. data/lib/datadog/appsec/instrumentation/gateway/argument.rb +4 -2
  24. data/lib/datadog/appsec/monitor/gateway/watcher.rb +8 -3
  25. data/lib/datadog/appsec/security_engine/runner.rb +2 -2
  26. data/lib/datadog/appsec/utils.rb +0 -2
  27. data/lib/datadog/core/configuration/components.rb +2 -1
  28. data/lib/datadog/core/configuration/ext.rb +4 -0
  29. data/lib/datadog/core/configuration/options.rb +2 -2
  30. data/lib/datadog/core/configuration/settings.rb +53 -30
  31. data/lib/datadog/core/environment/agent_info.rb +4 -3
  32. data/lib/datadog/core/metrics/client.rb +1 -1
  33. data/lib/datadog/core/remote/client.rb +1 -1
  34. data/lib/datadog/core/remote/component.rb +3 -6
  35. data/lib/datadog/core/remote/configuration/repository.rb +2 -1
  36. data/lib/datadog/core/remote/negotiation.rb +9 -9
  37. data/lib/datadog/core/remote/transport/config.rb +4 -3
  38. data/lib/datadog/core/remote/transport/http/client.rb +4 -3
  39. data/lib/datadog/core/remote/transport/http/config.rb +6 -32
  40. data/lib/datadog/core/remote/transport/http/negotiation.rb +6 -32
  41. data/lib/datadog/core/remote/transport/http.rb +22 -57
  42. data/lib/datadog/core/remote/transport/negotiation.rb +4 -3
  43. data/lib/datadog/core/runtime/metrics.rb +8 -1
  44. data/lib/datadog/core/telemetry/http/adapters/net.rb +1 -1
  45. data/lib/datadog/core/transport/http/api/instance.rb +17 -0
  46. data/lib/datadog/core/transport/http/api/spec.rb +17 -0
  47. data/lib/datadog/core/transport/http/builder.rb +5 -3
  48. data/lib/datadog/core/transport/http.rb +39 -2
  49. data/lib/datadog/di/component.rb +0 -2
  50. data/lib/datadog/di/probe_notifier_worker.rb +16 -16
  51. data/lib/datadog/di/transport/diagnostics.rb +4 -3
  52. data/lib/datadog/di/transport/http/api.rb +2 -12
  53. data/lib/datadog/di/transport/http/client.rb +4 -3
  54. data/lib/datadog/di/transport/http/diagnostics.rb +7 -33
  55. data/lib/datadog/di/transport/http/input.rb +7 -33
  56. data/lib/datadog/di/transport/http.rb +14 -56
  57. data/lib/datadog/di/transport/input.rb +4 -3
  58. data/lib/datadog/di/utils.rb +5 -0
  59. data/lib/datadog/kit/appsec/events.rb +12 -0
  60. data/lib/datadog/kit/identity.rb +5 -1
  61. data/lib/datadog/opentelemetry/api/baggage.rb +90 -0
  62. data/lib/datadog/opentelemetry/api/baggage.rbs +26 -0
  63. data/lib/datadog/opentelemetry/api/context.rb +16 -2
  64. data/lib/datadog/opentelemetry/sdk/trace/span.rb +1 -1
  65. data/lib/datadog/opentelemetry.rb +2 -1
  66. data/lib/datadog/profiling/collectors/thread_context.rb +1 -1
  67. data/lib/datadog/profiling.rb +5 -2
  68. data/lib/datadog/tracing/component.rb +15 -12
  69. data/lib/datadog/tracing/configuration/ext.rb +7 -1
  70. data/lib/datadog/tracing/configuration/settings.rb +18 -2
  71. data/lib/datadog/tracing/context_provider.rb +1 -1
  72. data/lib/datadog/tracing/contrib/configuration/settings.rb +1 -1
  73. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -5
  74. data/lib/datadog/tracing/contrib/excon/middleware.rb +5 -3
  75. data/lib/datadog/tracing/contrib/ext.rb +1 -0
  76. data/lib/datadog/tracing/contrib/faraday/middleware.rb +5 -3
  77. data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +7 -1
  78. data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +3 -0
  79. data/lib/datadog/tracing/contrib/http/circuit_breaker.rb +0 -15
  80. data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +4 -1
  81. data/lib/datadog/tracing/contrib/http/instrumentation.rb +5 -5
  82. data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +5 -11
  83. data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +6 -10
  84. data/lib/datadog/tracing/contrib/karafka/configuration/settings.rb +27 -0
  85. data/lib/datadog/tracing/contrib/karafka/distributed/propagation.rb +46 -0
  86. data/lib/datadog/tracing/contrib/karafka/ext.rb +27 -0
  87. data/lib/datadog/tracing/contrib/karafka/integration.rb +45 -0
  88. data/lib/datadog/tracing/contrib/karafka/monitor.rb +66 -0
  89. data/lib/datadog/tracing/contrib/karafka/patcher.rb +71 -0
  90. data/lib/datadog/tracing/contrib/karafka.rb +37 -0
  91. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +5 -3
  92. data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +6 -1
  93. data/lib/datadog/tracing/contrib/sidekiq/distributed/propagation.rb +3 -0
  94. data/lib/datadog/tracing/contrib.rb +1 -0
  95. data/lib/datadog/tracing/correlation.rb +9 -2
  96. data/lib/datadog/tracing/distributed/baggage.rb +131 -0
  97. data/lib/datadog/tracing/distributed/datadog.rb +2 -0
  98. data/lib/datadog/tracing/distributed/propagation.rb +25 -4
  99. data/lib/datadog/tracing/distributed/propagation_policy.rb +42 -0
  100. data/lib/datadog/tracing/metadata/ext.rb +5 -0
  101. data/lib/datadog/tracing/sampling/span/rule.rb +0 -1
  102. data/lib/datadog/tracing/span_operation.rb +2 -1
  103. data/lib/datadog/tracing/sync_writer.rb +1 -2
  104. data/lib/datadog/tracing/trace_digest.rb +9 -2
  105. data/lib/datadog/tracing/trace_operation.rb +29 -17
  106. data/lib/datadog/tracing/trace_segment.rb +6 -4
  107. data/lib/datadog/tracing/tracer.rb +38 -2
  108. data/lib/datadog/tracing/transport/http/api.rb +2 -10
  109. data/lib/datadog/tracing/transport/http/client.rb +5 -4
  110. data/lib/datadog/tracing/transport/http/traces.rb +13 -41
  111. data/lib/datadog/tracing/transport/http.rb +11 -44
  112. data/lib/datadog/tracing/transport/trace_formatter.rb +7 -0
  113. data/lib/datadog/tracing/transport/traces.rb +26 -9
  114. data/lib/datadog/tracing/workers/trace_writer.rb +2 -6
  115. data/lib/datadog/tracing/writer.rb +2 -6
  116. data/lib/datadog/tracing.rb +16 -3
  117. data/lib/datadog/version.rb +2 -2
  118. data/lib/datadog.rb +1 -1
  119. metadata +24 -13
  120. data/lib/datadog/appsec/contrib/devise/event.rb +0 -54
  121. data/lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb +0 -72
  122. data/lib/datadog/appsec/contrib/devise/patcher/registration_controller_patch.rb +0 -47
  123. data/lib/datadog/appsec/contrib/devise/resource.rb +0 -35
  124. data/lib/datadog/appsec/contrib/devise/tracking.rb +0 -57
  125. data/lib/datadog/appsec/utils/trace_operation.rb +0 -15
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative '../../../distributed/fetcher'
4
4
  require_relative '../../../distributed/propagation'
5
+ require_relative '../../../distributed/propagation_policy'
5
6
  require_relative '../../../distributed/b3_multi'
6
7
  require_relative '../../../distributed/b3_single'
7
8
  require_relative '../../../distributed/datadog'
@@ -31,6 +32,8 @@ module Datadog
31
32
  Tracing::Distributed::Datadog.new(fetcher: Tracing::Distributed::Fetcher),
32
33
  Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_TRACE_CONTEXT =>
33
34
  Tracing::Distributed::TraceContext.new(fetcher: Tracing::Distributed::Fetcher),
35
+ Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_BAGGAGE =>
36
+ Tracing::Distributed::Baggage.new(fetcher: Tracing::Distributed::Fetcher),
34
37
  Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_NONE => Tracing::Distributed::None.new
35
38
  },
36
39
  propagation_style_inject: propagation_style_inject,
@@ -55,6 +55,7 @@ require_relative 'contrib/httpclient/integration'
55
55
  require_relative 'contrib/httprb/integration'
56
56
  require_relative 'contrib/integration'
57
57
  require_relative 'contrib/kafka/integration'
58
+ require_relative 'contrib/karafka'
58
59
  require_relative 'contrib/lograge/integration'
59
60
  require_relative 'contrib/mongodb/integration'
60
61
  require_relative 'contrib/mysql2/integration'
@@ -94,8 +94,15 @@ module Datadog
94
94
  end
95
95
 
96
96
  def format_trace_id(trace_id)
97
- if Datadog.configuration.tracing.trace_id_128_bit_logging_enabled &&
98
- !Tracing::Utils::TraceId.to_high_order(trace_id).zero?
97
+ if Datadog.configuration.tracing.trace_id_128_bit_logging_enabled
98
+ format_trace_id_128(trace_id)
99
+ else
100
+ Tracing::Utils::TraceId.to_low_order(trace_id).to_s
101
+ end
102
+ end
103
+
104
+ def format_trace_id_128(trace_id)
105
+ if !Tracing::Utils::TraceId.to_high_order(trace_id).zero?
99
106
  Kernel.format('%032x', trace_id)
100
107
  else
101
108
  Tracing::Utils::TraceId.to_low_order(trace_id).to_s
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../metadata/ext'
4
+ require_relative '../trace_digest'
5
+ require_relative 'datadog_tags_codec'
6
+ require_relative '../utils'
7
+ require_relative 'helpers'
8
+ require 'uri'
9
+
10
+ module Datadog
11
+ module Tracing
12
+ module Distributed
13
+ # W3C Baggage propagator implementation.
14
+ # The baggage header is propagated through `baggage`.
15
+ # @see https://www.w3.org/TR/baggage/
16
+ class Baggage
17
+ BAGGAGE_KEY = 'baggage'
18
+ DD_TRACE_BAGGAGE_MAX_ITEMS = 64
19
+ DD_TRACE_BAGGAGE_MAX_BYTES = 8192
20
+ SAFE_CHARACTERS_KEY = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$!#&'*+-.^_`|~"
21
+ SAFE_CHARACTERS_VALUE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$!#&'()*+-./:<>?@[]^_`{|}~"
22
+
23
+ def initialize(
24
+ fetcher:,
25
+ baggage_key: BAGGAGE_KEY
26
+ )
27
+ @baggage_key = baggage_key
28
+ @fetcher = fetcher
29
+ end
30
+
31
+ def inject!(digest, data)
32
+ return if digest.nil? || digest.baggage.nil?
33
+
34
+ baggage_items = digest.baggage.reject { |k, v| k.nil? || v.nil? }
35
+ return if baggage_items.empty?
36
+
37
+ begin
38
+ if baggage_items.size > DD_TRACE_BAGGAGE_MAX_ITEMS
39
+ ::Datadog.logger.warn('Baggage item limit exceeded, dropping excess items')
40
+ baggage_items = baggage_items.first(DD_TRACE_BAGGAGE_MAX_ITEMS)
41
+ end
42
+
43
+ encoded_items = []
44
+ total_size = 0
45
+
46
+ baggage_items.each do |key, value|
47
+ item = "#{encode_item(key, SAFE_CHARACTERS_KEY)}=#{encode_item(value, SAFE_CHARACTERS_VALUE)}"
48
+ item_size = item.bytesize + (encoded_items.empty? ? 0 : 1) # +1 for comma if not first item
49
+ if total_size + item_size > DD_TRACE_BAGGAGE_MAX_BYTES
50
+ ::Datadog.logger.warn('Baggage header size exceeded, dropping excess items')
51
+ break # stop adding items when size limit is reached
52
+ end
53
+ encoded_items << item
54
+ total_size += item_size
55
+ end
56
+
57
+ # edge case where a single item is too large
58
+ return if encoded_items.empty?
59
+
60
+ header_value = encoded_items.join(',')
61
+ data[@baggage_key] = header_value
62
+ rescue => e
63
+ ::Datadog.logger.warn("Failed to encode and inject baggage header: #{e.message}")
64
+ end
65
+ end
66
+
67
+ def extract(data)
68
+ fetcher = @fetcher.new(data)
69
+ data = fetcher[@baggage_key]
70
+ return unless data
71
+
72
+ baggage = parse_baggage_header(fetcher[@baggage_key])
73
+ return unless baggage
74
+
75
+ TraceDigest.new(
76
+ baggage: baggage,
77
+ )
78
+ end
79
+
80
+ private
81
+
82
+ def encode_item(item, safe_characters)
83
+ # Strip whitespace and URL-encode the item
84
+ result = URI.encode_www_form_component(item.strip)
85
+ # Replace '+' with '%20' for space encoding consistency with W3C spec
86
+ result = result.gsub('+', '%20')
87
+ # Selectively decode percent-encoded characters that are considered "safe" in W3C Baggage spec
88
+ result.gsub(/%[0-9A-F]{2}/) do |encoded|
89
+ if encoded.size >= 3 && encoded[1..2] =~ /\A[0-9A-F]{2}\z/
90
+ hex_str = encoded[1..2]
91
+ next encoded unless hex_str && !hex_str.empty?
92
+
93
+ # Convert hex representation back to character
94
+ char = [hex_str.hex].pack('C')
95
+ # Keep the character as-is if it's in the safe character set, otherwise keep it encoded
96
+ safe_characters.include?(char) ? char : encoded
97
+ else
98
+ encoded
99
+ end
100
+ end
101
+ end
102
+
103
+ # Parses a W3C Baggage header string into a hash of key-value pairs
104
+ # The header format follows the W3C Baggage specification:
105
+ # - Multiple baggage items are separated by commas
106
+ # - Each baggage item is a key-value pair separated by '='
107
+ # - Keys and values are URL-encoded
108
+ # - Returns an empty hash if the baggage header is malformed
109
+ #
110
+ # @param baggage_header [String] The W3C Baggage header string to parse
111
+ # @return [Hash<String, String>] A hash of decoded baggage items
112
+ def parse_baggage_header(baggage_header)
113
+ baggage = {}
114
+ baggages = baggage_header.split(',')
115
+ baggages.each do |key_value|
116
+ key, value = key_value.split('=', 2)
117
+ # If baggage is malformed, return an empty hash
118
+ return {} unless key && value
119
+
120
+ key = URI.decode_www_form_component(key.strip)
121
+ value = URI.decode_www_form_component(value.strip)
122
+ return {} if key.empty? || value.empty?
123
+
124
+ baggage[key] = value
125
+ end
126
+ baggage
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -116,6 +116,8 @@ module Datadog
116
116
  def extract_trace_id!(trace_id, tags)
117
117
  return trace_id unless tags
118
118
  return trace_id unless (high_order = tags.delete(Tracing::Metadata::Ext::Distributed::TAG_TID))
119
+ return trace_id unless high_order.size == 16
120
+ return trace_id unless /\A[0-9a-f]+\z/i.match?(high_order)
119
121
 
120
122
  Tracing::Utils::TraceId.concatenate(high_order.to_i(16), trace_id)
121
123
  end
@@ -4,6 +4,7 @@ require_relative '../configuration/ext'
4
4
  require_relative '../trace_digest'
5
5
  require_relative '../trace_operation'
6
6
  require_relative '../../core/telemetry/logger'
7
+ require_relative 'baggage'
7
8
 
8
9
  module Datadog
9
10
  module Tracing
@@ -26,9 +27,13 @@ module Datadog
26
27
  )
27
28
  @propagation_styles = propagation_styles
28
29
  @propagation_extract_first = propagation_extract_first
29
-
30
30
  @propagation_style_inject = propagation_style_inject.map { |style| propagation_styles[style] }
31
31
  @propagation_style_extract = propagation_style_extract.map { |style| propagation_styles[style] }
32
+
33
+ # The baggage propagator is unique in that baggage should always be extracted, if present.
34
+ # Therefore we remove it from the `propagation_style_extract` list.
35
+ @baggage_propagator = @propagation_style_extract.find { |propagator| propagator.is_a?(Baggage) }
36
+ @propagation_style_extract.delete(@baggage_propagator) if @baggage_propagator
32
37
  end
33
38
 
34
39
  # inject! populates the env with span ID, trace ID and sampling priority
@@ -57,9 +62,8 @@ module Datadog
57
62
  end
58
63
 
59
64
  digest = digest.to_digest if digest.respond_to?(:to_digest)
60
-
61
- if digest.trace_id.nil?
62
- ::Datadog.logger.debug('Cannot inject distributed trace data: digest.trace_id is nil.')
65
+ if digest.trace_id.nil? && digest.baggage.nil?
66
+ ::Datadog.logger.debug('Cannot inject distributed trace data: digest.trace_id and digest.baggage are both nil.')
63
67
  return nil
64
68
  end
65
69
 
@@ -138,12 +142,29 @@ module Datadog
138
142
  "Error extracting distributed trace data. Cause: #{e} Location: #{Array(e.backtrace).first}"
139
143
  )
140
144
  end
145
+ # Handle baggage after all other styles if present
146
+ extracted_trace_digest = propagate_baggage(data, extracted_trace_digest) if @baggage_propagator
141
147
 
142
148
  extracted_trace_digest
143
149
  end
144
150
 
145
151
  private
146
152
 
153
+ def propagate_baggage(data, extracted_trace_digest)
154
+ if extracted_trace_digest
155
+ # Merge with baggage if present
156
+ digest = @baggage_propagator.extract(data)
157
+ if digest
158
+ extracted_trace_digest.merge(baggage: digest.baggage)
159
+ else
160
+ extracted_trace_digest
161
+ end
162
+ else
163
+ # Baggage is the only style
164
+ @baggage_propagator.extract(data)
165
+ end
166
+ end
167
+
147
168
  def last_datadog_parent_id(headers, tracecontext_tags)
148
169
  dd_propagator = @propagation_style_extract.find { |propagator| propagator.is_a?(Datadog) }
149
170
  if tracecontext_tags&.fetch(
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Tracing
5
+ module Distributed
6
+ # Helper method to decide when to skip distributed tracing
7
+ module PropagationPolicy
8
+ module_function
9
+
10
+ # Skips distributed tracing if disabled for this instrumentation
11
+ # or if APM is disabled unless there is an AppSec event (from upstream distributed trace or local)
12
+ #
13
+ # Both pin_config and global_config are configuration for integrations.
14
+ # pin_config is a Datadog::Core::Pin object, which gives the configuration of a single instance of an integration.
15
+ # global_config is the config for all instances of an integration.
16
+ def enabled?(pin_config: nil, global_config: nil, trace: nil)
17
+ return false unless Tracing.enabled?
18
+
19
+ unless ::Datadog.configuration.apm.tracing.enabled
20
+ return false if trace.nil?
21
+
22
+ trace_source = trace.get_tag(::Datadog::Tracing::Metadata::Ext::Distributed::TAG_TRACE_SOURCE)&.to_i(16)
23
+ return false if trace_source.nil?
24
+
25
+ # If AppSec is enabled and AppSec bit is set in the trace, we should not skip distributed tracing
26
+ # Other products that will use dd.p.ts should implement similar behavior here
27
+ if ::Datadog.configuration.appsec.enabled && (trace_source & ::Datadog::AppSec::Ext::PRODUCT_BIT) != 0
28
+ return true
29
+ end
30
+
31
+ return false
32
+ end
33
+
34
+ return pin_config[:distributed_tracing] if pin_config && pin_config.key?(:distributed_tracing)
35
+ return global_config[:distributed_tracing] if global_config
36
+
37
+ true
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -31,6 +31,8 @@ module Datadog
31
31
  # See Datadog-internal "RFC: Identifying which spans have profiling enabled " for details
32
32
  TAG_PROFILING_ENABLED = '_dd.profiling.enabled'
33
33
 
34
+ TAG_APM_ENABLED = '_dd.apm.enabled'
35
+
34
36
  # Defines constants for trace analytics
35
37
  # @public_api
36
38
  module Analytics
@@ -55,6 +57,9 @@ module Datadog
55
57
  # @see Datadog::Tracing::Sampling::Ext::Mechanism
56
58
  TAG_DECISION_MAKER = '_dd.p.dm'
57
59
 
60
+ # Bitmask for which product generated an event. E.g.: 2 for an AppSec event.
61
+ TAG_TRACE_SOURCE = '_dd.p.ts'
62
+
58
63
  TAG_ORIGIN = '_dd.origin'
59
64
  TAG_SAMPLING_PRIORITY = '_sampling_priority_v1'
60
65
 
@@ -24,7 +24,6 @@ module Datadog
24
24
  sample_rate: Span::Ext::DEFAULT_SAMPLE_RATE,
25
25
  rate_limit: Span::Ext::DEFAULT_MAX_PER_SECOND
26
26
  )
27
-
28
27
  @matcher = matcher
29
28
  @sample_rate = sample_rate
30
29
  @rate_limit = rate_limit
@@ -269,7 +269,8 @@ module Datadog
269
269
 
270
270
  def duration
271
271
  return @duration_end - @duration_start if @duration_start && @duration_end
272
- return @end_time - @start_time if @start_time && @end_time
272
+
273
+ @end_time - @start_time if @start_time && @end_time
273
274
  end
274
275
 
275
276
  def set_error(e)
@@ -32,8 +32,7 @@ module Datadog
32
32
  @agent_settings = agent_settings
33
33
 
34
34
  @transport = transport || begin
35
- transport_options = transport_options.merge(agent_settings: agent_settings) if agent_settings
36
- Transport::HTTP.default(**transport_options)
35
+ Transport::HTTP.default(agent_settings: agent_settings, logger: logger, **transport_options)
37
36
  end
38
37
 
39
38
  @events = Writer::Events.new
@@ -80,6 +80,9 @@ module Datadog
80
80
  # This allows later propagation to include those unknown fields, as they can represent future versions of the spec
81
81
  # sending data through this service. This value ends in a trailing `;` to facilitate serialization.
82
82
  # @return [String]
83
+ # @!attribute [r] baggage
84
+ # The W3C "baggage" extracted from a distributed context. This field is a hash of key/value pairs.
85
+ # @return [Hash<String,String>]
83
86
  # TODO: The documentation for the last attribute above won't be rendered.
84
87
  # TODO: This might be a YARD bug as adding an attribute, making it now second-last attribute, renders correctly.
85
88
  attr_reader \
@@ -102,7 +105,8 @@ module Datadog
102
105
  :trace_flags,
103
106
  :trace_state,
104
107
  :trace_state_unknown_fields,
105
- :span_remote
108
+ :span_remote,
109
+ :baggage
106
110
 
107
111
  def initialize(
108
112
  span_id: nil,
@@ -124,7 +128,8 @@ module Datadog
124
128
  trace_flags: nil,
125
129
  trace_state: nil,
126
130
  trace_state_unknown_fields: nil,
127
- span_remote: true
131
+ span_remote: true,
132
+ baggage: nil
128
133
  )
129
134
  @span_id = span_id
130
135
  @span_name = span_name && span_name.dup.freeze
@@ -146,6 +151,7 @@ module Datadog
146
151
  @trace_state = trace_state && trace_state.dup.freeze
147
152
  @trace_state_unknown_fields = trace_state_unknown_fields && trace_state_unknown_fields.dup.freeze
148
153
  @span_remote = span_remote
154
+ @baggage = baggage && baggage.dup.freeze
149
155
  freeze
150
156
  end
151
157
 
@@ -177,6 +183,7 @@ module Datadog
177
183
  trace_state: trace_state,
178
184
  trace_state_unknown_fields: trace_state_unknown_fields,
179
185
  span_remote: span_remote,
186
+ baggage: baggage
180
187
  }.merge!(field_value_pairs)
181
188
  )
182
189
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require_relative '../core/environment/identity'
4
4
  require_relative '../core/utils'
5
- require_relative 'tracer'
6
5
  require_relative 'event'
7
6
  require_relative 'metadata/tagging'
8
7
  require_relative 'sampling/ext'
@@ -37,7 +36,8 @@ module Datadog
37
36
  :rule_sample_rate,
38
37
  :sample_rate,
39
38
  :sampling_priority,
40
- :remote_parent
39
+ :remote_parent,
40
+ :baggage
41
41
 
42
42
  attr_reader \
43
43
  :active_span_count,
@@ -71,19 +71,21 @@ module Datadog
71
71
  sampling_priority: nil,
72
72
  service: nil,
73
73
  profiling_enabled: nil,
74
+ apm_tracing_enabled: nil,
74
75
  tags: nil,
75
76
  metrics: nil,
76
77
  trace_state: nil,
77
78
  trace_state_unknown_fields: nil,
78
79
  remote_parent: false,
79
- tracer: nil
80
+ tracer: nil,
81
+ baggage: nil
80
82
 
81
83
  )
82
84
  # Attributes
83
85
  @id = id || Tracing::Utils::TraceId.next_id
84
86
  @max_length = max_length || DEFAULT_MAX_LENGTH
85
87
  @parent_span_id = parent_span_id
86
- @sampled = sampled.nil? ? true : sampled
88
+ @sampled = sampled.nil? || sampled
87
89
  @remote_parent = remote_parent
88
90
 
89
91
  # Tags
@@ -98,9 +100,11 @@ module Datadog
98
100
  @sampling_priority = sampling_priority
99
101
  @service = service
100
102
  @profiling_enabled = profiling_enabled
103
+ @apm_tracing_enabled = apm_tracing_enabled
101
104
  @trace_state = trace_state
102
105
  @trace_state_unknown_fields = trace_state_unknown_fields
103
106
  @tracer = tracer
107
+ @baggage = baggage
104
108
 
105
109
  # Generic tags
106
110
  set_tags(tags) if tags
@@ -173,6 +177,12 @@ module Datadog
173
177
  super || (root_span && root_span.get_metric(key))
174
178
  end
175
179
 
180
+ def set_distributed_source(product_bit)
181
+ source = get_tag(Metadata::Ext::Distributed::TAG_TRACE_SOURCE)&.to_i(16) || 0
182
+ source |= product_bit
183
+ set_tag(Metadata::Ext::Distributed::TAG_TRACE_SOURCE, format('%02X', source))
184
+ end
185
+
176
186
  def tags
177
187
  all_tags = {}
178
188
  all_tags.merge!(root_span&.tags || {}) if root_span
@@ -315,10 +325,10 @@ module Datadog
315
325
 
316
326
  TraceDigest.new(
317
327
  span_id: span_id,
318
- span_name: (@active_span && @active_span.name),
319
- span_resource: (@active_span && @active_span.resource),
320
- span_service: (@active_span && @active_span.service),
321
- span_type: (@active_span && @active_span.type),
328
+ span_name: @active_span && @active_span.name,
329
+ span_resource: @active_span && @active_span.resource,
330
+ span_service: @active_span && @active_span.service,
331
+ span_type: @active_span && @active_span.type,
322
332
  trace_distributed_tags: distributed_tags,
323
333
  trace_hostname: @hostname,
324
334
  trace_id: @id,
@@ -331,7 +341,8 @@ module Datadog
331
341
  trace_service: service,
332
342
  trace_state: @trace_state,
333
343
  trace_state_unknown_fields: @trace_state_unknown_fields,
334
- span_remote: (@remote_parent && @active_span.nil?),
344
+ span_remote: @remote_parent && @active_span.nil?,
345
+ baggage: @baggage.nil? || @baggage.empty? ? nil : @baggage
335
346
  ).freeze
336
347
  end
337
348
 
@@ -351,22 +362,22 @@ module Datadog
351
362
  def fork_clone
352
363
  self.class.new(
353
364
  agent_sample_rate: @agent_sample_rate,
354
- events: (@events && @events.dup),
355
- hostname: (@hostname && @hostname.dup),
365
+ events: @events && @events.dup,
366
+ hostname: @hostname && @hostname.dup,
356
367
  id: @id,
357
368
  max_length: @max_length,
358
- name: (name && name.dup),
359
- origin: (@origin && @origin.dup),
369
+ name: name && name.dup,
370
+ origin: @origin && @origin.dup,
360
371
  parent_span_id: (@active_span && @active_span.id) || @parent_span_id,
361
372
  rate_limiter_rate: @rate_limiter_rate,
362
- resource: (resource && resource.dup),
373
+ resource: resource && resource.dup,
363
374
  rule_sample_rate: @rule_sample_rate,
364
375
  sample_rate: @sample_rate,
365
376
  sampled: @sampled,
366
377
  sampling_priority: @sampling_priority,
367
- service: (service && service.dup),
368
- trace_state: (@trace_state && @trace_state.dup),
369
- trace_state_unknown_fields: (@trace_state_unknown_fields && @trace_state_unknown_fields.dup),
378
+ service: service && service.dup,
379
+ trace_state: @trace_state && @trace_state.dup,
380
+ trace_state_unknown_fields: @trace_state_unknown_fields && @trace_state_unknown_fields.dup,
370
381
  tags: meta.dup,
371
382
  metrics: metrics.dup,
372
383
  remote_parent: @remote_parent
@@ -510,6 +521,7 @@ module Datadog
510
521
  metrics: metrics,
511
522
  root_span_id: !partial ? root_span && root_span.id : nil,
512
523
  profiling_enabled: @profiling_enabled,
524
+ apm_tracing_enabled: @apm_tracing_enabled
513
525
  )
514
526
  end
515
527
 
@@ -34,7 +34,8 @@ module Datadog
34
34
  :sampling_decision_maker,
35
35
  :sampling_priority,
36
36
  :service,
37
- :profiling_enabled
37
+ :profiling_enabled,
38
+ :apm_tracing_enabled
38
39
 
39
40
  # rubocop:disable Metrics/CyclomaticComplexity
40
41
  # rubocop:disable Metrics/PerceivedComplexity
@@ -58,7 +59,8 @@ module Datadog
58
59
  service: nil,
59
60
  tags: nil,
60
61
  metrics: nil,
61
- profiling_enabled: nil
62
+ profiling_enabled: nil,
63
+ apm_tracing_enabled: nil
62
64
  )
63
65
  @id = id
64
66
  @root_span_id = root_span_id
@@ -85,6 +87,7 @@ module Datadog
85
87
  @sampling_priority = sampling_priority || sampling_priority_tag
86
88
  @service = Core::Utils::SafeDup.frozen_or_dup(service || service_tag)
87
89
  @profiling_enabled = profiling_enabled
90
+ @apm_tracing_enabled = apm_tracing_enabled
88
91
  end
89
92
  # rubocop:enable Metrics/PerceivedComplexity
90
93
  # rubocop:enable Metrics/CyclomaticComplexity
@@ -128,8 +131,7 @@ module Datadog
128
131
  end
129
132
 
130
133
  def sampled?
131
- sampling_priority == Sampling::Ext::Priority::AUTO_KEEP \
132
- || sampling_priority == Sampling::Ext::Priority::USER_KEEP
134
+ [Sampling::Ext::Priority::AUTO_KEEP, Sampling::Ext::Priority::USER_KEEP].include?(sampling_priority)
133
135
  end
134
136
 
135
137
  # Returns the high order part of the trace id as a hexadecimal string; the most significant 64 bits.
@@ -338,24 +338,30 @@ module Datadog
338
338
  hostname = hostname && !hostname.empty? ? hostname : nil
339
339
 
340
340
  if digest
341
+ sampling_priority = if propagate_sampling_priority?(upstream_tags: digest.trace_distributed_tags)
342
+ digest.trace_sampling_priority
343
+ end
341
344
  TraceOperation.new(
342
345
  hostname: hostname,
343
346
  profiling_enabled: profiling_enabled,
347
+ apm_tracing_enabled: apm_tracing_enabled,
344
348
  id: digest.trace_id,
345
349
  origin: digest.trace_origin,
346
350
  parent_span_id: digest.span_id,
347
- sampling_priority: digest.trace_sampling_priority,
351
+ sampling_priority: sampling_priority,
348
352
  # Distributed tags are just regular trace tags with special meaning to Datadog
349
353
  tags: digest.trace_distributed_tags,
350
354
  trace_state: digest.trace_state,
351
355
  trace_state_unknown_fields: digest.trace_state_unknown_fields,
352
356
  remote_parent: digest.span_remote,
353
- tracer: self
357
+ tracer: self,
358
+ baggage: digest.baggage
354
359
  )
355
360
  else
356
361
  TraceOperation.new(
357
362
  hostname: hostname,
358
363
  profiling_enabled: profiling_enabled,
364
+ apm_tracing_enabled: apm_tracing_enabled,
359
365
  remote_parent: false,
360
366
  tracer: self
361
367
  )
@@ -545,10 +551,40 @@ module Datadog
545
551
  end
546
552
  end
547
553
 
554
+ # Decide whether upstream sampling priority should be propagated, by taking into account
555
+ # the upstream tags and the configuration.
556
+ # We should always propagate if APM is enabled.
557
+ #
558
+ # e.g.: upstream tags containing dd.p.ts: 02, and appsec is enabled, return true.
559
+ def propagate_sampling_priority?(upstream_tags:)
560
+ return true if apm_tracing_enabled
561
+
562
+ if upstream_tags&.key?(Tracing::Metadata::Ext::Distributed::TAG_TRACE_SOURCE)
563
+ appsec_bit = upstream_tags[Tracing::Metadata::Ext::Distributed::TAG_TRACE_SOURCE].to_i(16) &
564
+ Datadog::AppSec::Ext::PRODUCT_BIT
565
+ return appsec_enabled if appsec_bit != 0
566
+ end
567
+
568
+ false
569
+ end
570
+
548
571
  def profiling_enabled
549
572
  @profiling_enabled ||=
550
573
  !!(defined?(Datadog::Profiling) && Datadog::Profiling.respond_to?(:enabled?) && Datadog::Profiling.enabled?)
551
574
  end
575
+
576
+ def appsec_enabled
577
+ @appsec_enabled ||= Datadog.configuration.appsec.enabled
578
+ end
579
+
580
+ # Due to APM Tracing (the product) and Tracing (the transport) being intertwined, we cannot completely disabled APM
581
+ # without also disabling the tracer. When setting `@apm_tracing_enabled` to `false`, it does not disable the tracer,
582
+ # but rather only sends heartbeat traces (1 per minutes), so that the service is considered alive in the backend.
583
+ # Other products (like ASM) can then set the sampling priority of their traces to `MANUAL_KEEP`,
584
+ # effectively allowing standalone products to work without APM.
585
+ def apm_tracing_enabled
586
+ @apm_tracing_enabled ||= Datadog.configuration.apm.tracing.enabled
587
+ end
552
588
  end
553
589
  end
554
590
  end
@@ -22,14 +22,14 @@ module Datadog
22
22
 
23
23
  def defaults
24
24
  Core::Transport::HTTP::API::Map[
25
- V4 => Spec.new do |s|
25
+ V4 => Traces::API::Spec.new do |s|
26
26
  s.traces = Traces::API::Endpoint.new(
27
27
  '/v0.4/traces',
28
28
  Core::Encoding::MsgpackEncoder,
29
29
  service_rates: true
30
30
  )
31
31
  end,
32
- V3 => Spec.new do |s|
32
+ V3 => Traces::API::Spec.new do |s|
33
33
  s.traces = Traces::API::Endpoint.new(
34
34
  '/v0.3/traces',
35
35
  Core::Encoding::MsgpackEncoder
@@ -37,14 +37,6 @@ module Datadog
37
37
  end,
38
38
  ].with_fallbacks(V4 => V3)
39
39
  end
40
-
41
- class Instance < Core::Transport::HTTP::API::Instance
42
- include Traces::API::Instance
43
- end
44
-
45
- class Spec < Core::Transport::HTTP::API::Spec
46
- include Traces::API::Spec
47
- end
48
40
  end
49
41
  end
50
42
  end