ddtrace 1.16.1 → 1.17.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4e85c12caf77e324eee9de8f9ddf143b0e49c3487230ff841ab7a5be05e4c7d
4
- data.tar.gz: 44d799e085ae66280177800c136c656085d6f1fc5d32085c2852158c4290ef59
3
+ metadata.gz: 2da890705f074bf11ffaa1b5099472a4695d318fc1a1a98238d61a061d6582ec
4
+ data.tar.gz: 8c6db62249ee456939c3eacdeddb0f985148e9daa78adb844144d8da1191e8bb
5
5
  SHA512:
6
- metadata.gz: 8c651f6acbdff86fac984f61df4f2322eb449e3355220810a955496c51d33565ddbd6d15f3ca5049f9f0738c93508db9def7c24f046a51d3ef616b6182a1ca4d
7
- data.tar.gz: 75fc3aa27da60ec0998087a814f77fe33a4f4c0bcbbda52b647cc00552945e412fa8d7e276d69d470b258da8c204db5186f24118cf4e0dc24bec9c1681ed1c33
6
+ metadata.gz: a70b6856159a46f30a4ff67bc672011bb016b2aa7b1562834957a325eee90be681f9d0e153ef94c97a4ce675f7b63ac9f5a68f5573b4b9144ee40cf89a70395f
7
+ data.tar.gz: 0d8d1a5d2ee255e53daab1cea7e82ec6f61c8e4a67b89ab1293853786d634c6cde2407a1ea0ceb4ff8bbb3391940dec5bdcfc56dd4b55b7b3826e35bdcff84eb
data/CHANGELOG.md CHANGED
@@ -2,6 +2,37 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.17.0] - 2023-11-22
6
+
7
+ For W3C Trace Context, this release adds [`tracecontext`](https://www.w3.org/TR/trace-context/) to the default trace propagation extraction and injection styles. The new defaults are:
8
+ * Extraction: `Datadog,b3multi,b3,tracecontext`
9
+ * Injection: `Datadog,tracecontext`
10
+
11
+ And to increase interoperability with `tracecontext`, 128-bit Trace ID generation is now the default.
12
+
13
+ For OpenTelemetry, this release adds support for converting [OpenTelemetry Trace Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/general/trace/) into equivalent Datadog trace semantics. Also, it's now possible to configure top-level Datadog span fields using OpenTelemetry span attributes (https://github.com/DataDog/dd-trace-rb/pull/3262).
14
+
15
+ For CI Visibility, you can now manually create CI traces and spans with the [newly released API](https://github.com/DataDog/datadog-ci-rb/releases/tag/v0.4.0).
16
+
17
+ ### Added
18
+
19
+ * OpenTelemetry: Parse OpenTelemetry semantic conventions to Datadog's ([#3273][])
20
+ * OpenTelemetry: Support span reserved attribute overrides ([#3262][])
21
+ * Tracing: Ensure W3C `tracestate` is always propagated ([#3255][])
22
+
23
+ ### Changed
24
+
25
+ * Tracing: Set 128-bit trace_id to true by default ([#3266][])
26
+ * Tracing: Default trace propagation styles to `Datadog,b3multi,b3,tracecontext` ([#3248][],#3267)
27
+ * Ci-App: Upgraded `datadog-ci` dependency to 0.4 ([#3270][])
28
+
29
+ ## [1.16.2] - 2023-11-10
30
+
31
+ This release reverts a change to appsec response body parsing that was introduced in [1.16.0 ](https://github.com/DataDog/dd-trace-rb/releases/tag/v1.16.0) that may cause memory leaks.
32
+
33
+ ### Fixed
34
+ * Appsec: [Revert parse response body fix introduced in 1.16.0](https://github.com/DataDog/dd-trace-rb/pull/3153) ([#3252][])
35
+
5
36
  ## [1.16.1] - 2023-11-08
6
37
 
7
38
  ### Fixed
@@ -2627,7 +2658,9 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
2627
2658
  Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
2628
2659
 
2629
2660
 
2630
- [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v1.16.1...master
2661
+ [Unreleased]: https://github.com/DataDog/dd-trace-rb/compare/v1.17.0...master
2662
+ [1.17.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.16.2...v1.17.0
2663
+ [1.16.2]: https://github.com/DataDog/dd-trace-rb/compare/v1.16.1...v1.16.2
2631
2664
  [1.16.1]: https://github.com/DataDog/dd-trace-rb/compare/v1.16.0...v1.16.1
2632
2665
  [1.16.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.15.0...v1.16.0
2633
2666
  [1.15.0]: https://github.com/DataDog/dd-trace-rb/compare/v1.14.0...v1.15.0
@@ -3841,6 +3874,14 @@ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
3841
3874
  [#3235]: https://github.com/DataDog/dd-trace-rb/issues/3235
3842
3875
  [#3240]: https://github.com/DataDog/dd-trace-rb/issues/3240
3843
3876
  [#3242]: https://github.com/DataDog/dd-trace-rb/issues/3242
3877
+ [#3248]: https://github.com/DataDog/dd-trace-rb/issues/3248
3878
+ [#3252]: https://github.com/DataDog/dd-trace-rb/issues/3252
3879
+ [#3255]: https://github.com/DataDog/dd-trace-rb/issues/3255
3880
+ [#3262]: https://github.com/DataDog/dd-trace-rb/issues/3262
3881
+ [#3266]: https://github.com/DataDog/dd-trace-rb/issues/3266
3882
+ [#3267]: https://github.com/DataDog/dd-trace-rb/issues/3267
3883
+ [#3270]: https://github.com/DataDog/dd-trace-rb/issues/3270
3884
+ [#3273]: https://github.com/DataDog/dd-trace-rb/issues/3273
3844
3885
  [@AdrianLC]: https://github.com/AdrianLC
3845
3886
  [@Azure7111]: https://github.com/Azure7111
3846
3887
  [@BabyGroot]: https://github.com/BabyGroot
@@ -188,12 +188,6 @@ module Datadog
188
188
  end
189
189
  end
190
190
  end
191
-
192
- option :parse_response_body do |o|
193
- o.type :bool
194
- o.env 'DD_API_SECURITY_PARSE_RESPONSE_BODY'
195
- o.default true
196
- end
197
191
  end
198
192
  end
199
193
  end
@@ -19,55 +19,9 @@ module Datadog
19
19
  @scope = scope
20
20
  end
21
21
 
22
- def parsed_body
23
- return unless Datadog.configuration.appsec.parse_response_body
24
-
25
- unless body.instance_of?(Array)
26
- Datadog.logger.debug do
27
- "Response body type unsupported: #{body.class}"
28
- end
29
- return
30
- end
31
-
32
- return unless json_content_type?
33
-
34
- result = ''.dup
35
- all_body_parts_are_string = true
36
-
37
- body.each do |body_part|
38
- if body_part.is_a?(String)
39
- result.concat(body_part)
40
- else
41
- all_body_parts_are_string = false
42
- break
43
- end
44
- end
45
-
46
- return unless all_body_parts_are_string
47
-
48
- begin
49
- JSON.parse(result)
50
- rescue JSON::ParserError => e
51
- Datadog.logger.debug { "Failed to parse response body. Error #{e.class}. Message #{e.message}" }
52
- nil
53
- end
54
- end
55
-
56
22
  def response
57
23
  @response ||= ::Rack::Response.new(body, status, headers)
58
24
  end
59
-
60
- private
61
-
62
- VALID_JSON_TYPES = [
63
- 'application/json',
64
- 'text/json'
65
- ].freeze
66
-
67
- def json_content_type?
68
- content_type = headers['content-type']
69
- VALID_JSON_TYPES.include?(content_type)
70
- end
71
25
  end
72
26
  end
73
27
  end
@@ -10,7 +10,6 @@ module Datadog
10
10
  ADDRESSES = [
11
11
  'response.status',
12
12
  'response.headers',
13
- 'response.body',
14
13
  ].freeze
15
14
  private_constant :ADDRESSES
16
15
 
@@ -18,7 +17,6 @@ module Datadog
18
17
  catch(:block) do
19
18
  op.publish('response.status', gateway_response.status)
20
19
  op.publish('response.headers', gateway_response.headers)
21
- op.publish('response.body', gateway_response.parsed_body)
22
20
 
23
21
  nil
24
22
  end
@@ -31,7 +29,6 @@ module Datadog
31
29
  response_status = values[0]
32
30
  response_headers = values[1]
33
31
  response_headers_no_cookies = response_headers.dup.tap { |h| h.delete('set-cookie') }
34
- response_body = values[2]
35
32
 
36
33
  waf_args = {
37
34
  'server.response.status' => response_status.to_s,
@@ -39,8 +36,6 @@ module Datadog
39
36
  'server.response.headers.no_cookies' => response_headers_no_cookies,
40
37
  }
41
38
 
42
- waf_args['server.response.body'] = response_body if response_body
43
-
44
39
  waf_timeout = Datadog.configuration.appsec.waf_timeout
45
40
  result = waf_context.run(waf_args, waf_timeout)
46
41
 
@@ -66,16 +66,6 @@ module Datadog
66
66
  request_return = AppSec::Response.negotiate(env, blocked_event.last[:actions]).to_rack if blocked_event
67
67
  end
68
68
 
69
- if request_return[2].respond_to?(:to_ary)
70
- # Following the Rack specification. The response body should only call :each once.
71
- # Calling :to_ary returns an array with identical content as the produced when calling :each
72
- # replacing request_return[2] with that new value allow us to safely operate on the response body.
73
- # On Gateway::Response#parsed_body we might iterate over the reposne body using :each
74
- # https://github.com/rack/rack/blob/main/SPEC.rdoc#enumerable-body-
75
- consumed_body = request_return[2].to_ary
76
- request_return[2] = consumed_body if consumed_body
77
- end
78
-
79
69
  gateway_response = Gateway::Response.new(
80
70
  request_return[2],
81
71
  request_return[0],
@@ -330,7 +330,7 @@ module Datadog
330
330
  # Caveat 3 (severe):
331
331
  # Ruby 3.2.0 to 3.2.2 have a bug in the newobj tracepoint (https://bugs.ruby-lang.org/issues/19482,
332
332
  # https://github.com/ruby/ruby/pull/7464) so that's an extra reason why it's not safe on those Rubies.
333
- # This bug is fixed on Ruby versions 3.2.2 and 3.3.0.
333
+ # This bug is fixed on Ruby versions 3.2.3 and 3.3.0.
334
334
  #
335
335
  # @default `true` on Ruby 2.x and 3.1.4+, 3.2.3+ and 3.3.0+; `false` for Ruby 3.0 and unpatched Rubies.
336
336
  option :allocation_counting_enabled do |o|
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'trace/span'
4
+
3
5
  module Datadog
4
6
  module OpenTelemetry
5
7
  module SDK
@@ -76,16 +78,11 @@ module Datadog
76
78
  end
77
79
 
78
80
  def start_datadog_span(span)
79
- tags = span.resource.attribute_enumerator.to_h
81
+ attributes = span.attributes.dup # Dup to allow modification of frozen Hash
80
82
 
81
- kind = span.kind || 'internal'
82
- tags[Tracing::Metadata::Ext::TAG_KIND] = kind
83
+ name, kwargs = span_arguments(span, attributes)
83
84
 
84
- datadog_span = Tracing.trace(
85
- span.name,
86
- tags: tags,
87
- start_time: ns_to_time(span.start_timestamp)
88
- )
85
+ datadog_span = Tracing.trace(name, **kwargs)
89
86
 
90
87
  datadog_span.set_error([nil, span.status.description]) unless span.status.ok?
91
88
  datadog_span.set_tags(span.attributes)
@@ -93,6 +90,40 @@ module Datadog
93
90
  datadog_span
94
91
  end
95
92
 
93
+ # Some special attributes can override Datadog Span fields
94
+ def span_arguments(span, attributes)
95
+ if attributes.key?('analytics.event') && (analytics_event = attributes['analytics.event']).respond_to?(:casecmp)
96
+ attributes[Tracing::Metadata::Ext::Analytics::TAG_SAMPLE_RATE] = analytics_event.casecmp('true') == 0 ? 1 : 0
97
+ end
98
+ attributes[Tracing::Metadata::Ext::TAG_KIND] = span.kind || 'internal'
99
+
100
+ kwargs = { start_time: ns_to_time(span.start_timestamp) }
101
+
102
+ name = if attributes.key?('operation.name')
103
+ attributes['operation.name']
104
+ elsif (rich_name = Datadog::OpenTelemetry::Trace::Span.enrich_name(span.kind, attributes))
105
+ rich_name.downcase
106
+ else
107
+ span.kind
108
+ end
109
+
110
+ kwargs[:resource] = attributes.key?('resource.name') ? attributes['resource.name'] : span.name
111
+ kwargs[:service] = attributes['service.name'] if attributes.key?('service.name')
112
+ kwargs[:type] = attributes['span.type'] if attributes.key?('span.type')
113
+
114
+ attributes.reject! { |key, _| OpenTelemetry::Trace::Span::DATADOG_SPAN_ATTRIBUTE_OVERRIDES.include?(key) }
115
+
116
+ # DEV: There's no `flat_map!`; we have to split it into two operations
117
+ attributes = attributes.map do |key, value|
118
+ Datadog::OpenTelemetry::Trace::Span.serialize_attribute(key, value)
119
+ end
120
+ attributes.flatten!(1)
121
+
122
+ kwargs[:tags] = attributes
123
+
124
+ [name, kwargs]
125
+ end
126
+
96
127
  # From nanoseconds, used by OpenTelemetry, to a {Time} object, used by the Datadog Tracer.
97
128
  def ns_to_time(timestamp_ns)
98
129
  Time.at(timestamp_ns / 1000000000.0)
@@ -36,6 +36,78 @@ module Datadog
36
36
  span.set_error(status.description) if status && status.code == ::OpenTelemetry::Trace::Status::ERROR
37
37
  end
38
38
 
39
+ # Serialize values into Datadog span tags and metrics.
40
+ # Notably, arrays are exploded into many keys, each with
41
+ # a numeric suffix representing the array index, for example:
42
+ # `'foo' => ['a','b']` becomes `'foo.0' => 'a', 'foo.1' => 'b'`
43
+ def self.serialize_attribute(key, value)
44
+ if value.is_a?(Array)
45
+ value.flat_map.with_index do |v, idx|
46
+ serialize_attribute("#{key}.#{idx}", v)
47
+ end
48
+ elsif value.is_a?(TrueClass) || value.is_a?(FalseClass)
49
+ [[key, value.to_s]]
50
+ else
51
+ [[key, value]]
52
+ end
53
+ end
54
+
55
+ # Create a meaningful Datadog operation name from the OpenTelemetry
56
+ # semantic convention for span kind and span attributes.
57
+ # @see https://opentelemetry.io/docs/specs/semconv/general/trace/
58
+
59
+ # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
60
+ def self.enrich_name(kind, attrs)
61
+ if attrs.key?('http.request.method')
62
+ return 'http.server.request' if kind == :server
63
+ return 'http.client.request' if kind == :client
64
+ end
65
+
66
+ return "#{attrs['db.system']}.query" if attrs.key?('db.system') && kind == :client
67
+
68
+ if (attrs.key?('messaging.system') || attrs.key?('messaging.operation')) &&
69
+ [:consumer, :producer, :server, :client].include?(kind)
70
+
71
+ return "#{attrs['messaging.system']}.#{attrs['messaging.operation']}"
72
+ end
73
+
74
+ if attrs.key?('rpc.system')
75
+ if attrs['rpc.system'] == 'aws-api' && kind == :client
76
+ service = attrs['rpc.service']
77
+ return "aws.#{service || 'client'}.request"
78
+ end
79
+
80
+ if kind == :client
81
+ return "#{attrs['rpc.system']}.client.request"
82
+ elsif kind == :server
83
+ return "#{attrs['rpc.system']}.server.request"
84
+ end
85
+ end
86
+
87
+ if attrs.key?('faas.invoked_provider') && attrs.key?('faas.invoked_name') && kind == :client
88
+ provider = attrs['faas.invoked_provider']
89
+ name = attrs['faas.invoked_name']
90
+ return "#{provider}.#{name}.invoke"
91
+ end
92
+
93
+ return "#{attrs['faas.trigger']}.invoke" if attrs.key?('faas.trigger') && kind == :server
94
+
95
+ return 'graphql.server.request' if attrs.key?('graphql.operation.type')
96
+
97
+ if kind == :server
98
+ protocol = attrs['network.protocol.name']
99
+ return protocol ? "#{protocol}.server.request" : 'server.request'
100
+ end
101
+
102
+ if kind == :client
103
+ protocol = attrs['network.protocol.name']
104
+ return protocol ? "#{protocol}.client.request" : 'client.request'
105
+ end
106
+
107
+ kind.to_s
108
+ end
109
+ # rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
110
+
39
111
  private
40
112
 
41
113
  def datadog_set_attribute(key)
@@ -46,10 +118,18 @@ module Datadog
46
118
  # DEV: Accesses `@attributes` directly, since using `#attributes`
47
119
  # DEV: clones the hash, causing unnecessary overhead.
48
120
  if @attributes.key?(key)
49
- value = @attributes[key]
50
- span.set_tag(key, value)
121
+ # Try to find a richer operation name, unless an explicit override was provided.
122
+ if !@attributes.key?('operation.name') && (rich_name = Span.enrich_name(kind, @attributes))
123
+ span.name = rich_name.downcase
124
+ end
125
+
126
+ Span.serialize_attribute(key, @attributes[key]).each do |new_key, value|
127
+ override_datadog_values(span, new_key, value)
51
128
 
52
- span.service = value if key == 'service.name'
129
+ # When an attribute is used to override a Datadog Span property,
130
+ # it should NOT be set as a Datadog Span tag.
131
+ span.set_tag(new_key, value) unless DATADOG_SPAN_ATTRIBUTE_OVERRIDES.include?(new_key)
132
+ end
53
133
  else
54
134
  span.clear_tag(key)
55
135
 
@@ -61,6 +141,25 @@ module Datadog
61
141
  end
62
142
  end
63
143
 
144
+ # Some special attributes can override Datadog Span fields beyond tags and metrics.
145
+ # @return [Boolean] true if the key is a Datadog Span override attribute, false otherwise
146
+ def override_datadog_values(span, key, value)
147
+ span.name = value if key == 'operation.name'
148
+ span.resource = value if key == 'resource.name'
149
+ span.service = value if key == 'service.name'
150
+ span.type = value if key == 'span.type'
151
+
152
+ if key == 'analytics.event' && value.respond_to?(:casecmp)
153
+ Datadog::Tracing::Analytics.set_sample_rate(
154
+ span,
155
+ value.casecmp('true') == 0 ? 1 : 0
156
+ )
157
+ end
158
+ end
159
+
160
+ DATADOG_SPAN_ATTRIBUTE_OVERRIDES = ['analytics.event', 'operation.name', 'resource.name', 'service.name',
161
+ 'span.type'].freeze
162
+
64
163
  ::OpenTelemetry::SDK::Trace::Span.prepend(self)
65
164
  end
66
165
  end
@@ -33,7 +33,8 @@ module Datadog
33
33
  carrier[Tracing::Distributed::Datadog::ORIGIN_KEY] = digest.trace_origin
34
34
  carrier[Tracing::Distributed::Datadog::PARENT_ID_KEY] = digest.span_id
35
35
  carrier[Tracing::Distributed::Datadog::SAMPLING_PRIORITY_KEY] = digest.trace_sampling_priority
36
- carrier[Tracing::Distributed::Datadog::TRACE_ID_KEY] = digest.trace_id
36
+ carrier[Tracing::Distributed::Datadog::TRACE_ID_KEY] =
37
+ Datadog::Tracing::Utils::TraceId.to_low_order(digest.trace_id)
37
38
 
38
39
  nil
39
40
  end
@@ -62,6 +62,9 @@ module Datadog
62
62
  # @see https://opentelemetry.io/docs/concepts/sdk-configuration/general-sdk-configuration/#get_otel__propagators
63
63
  PROPAGATION_STYLE_NONE = 'none'
64
64
 
65
+ # Strictly stop at the first successfully serialized style.
66
+ EXTRACT_FIRST = 'DD_TRACE_PROPAGATION_EXTRACT_FIRST'
67
+
65
68
  ENV_X_DATADOG_TAGS_MAX_LENGTH = 'DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH'
66
69
  end
67
70
 
@@ -63,6 +63,7 @@ module Datadog
63
63
  Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG,
64
64
  Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_B3_MULTI_HEADER,
65
65
  Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_B3_SINGLE_HEADER,
66
+ Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_TRACE_CONTEXT,
66
67
  ]
67
68
  )
68
69
  o.after_set do |styles|
@@ -93,7 +94,10 @@ module Datadog
93
94
  o.deprecated_env Tracing::Configuration::Ext::Distributed::ENV_PROPAGATION_STYLE_INJECT_OLD
94
95
  o.env Tracing::Configuration::Ext::Distributed::ENV_PROPAGATION_STYLE_INJECT
95
96
  # DEV-2.0: Change default value to `tracecontext, Datadog`.
96
- o.default [Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG]
97
+ o.default [
98
+ Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG,
99
+ Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_TRACE_CONTEXT,
100
+ ]
97
101
  o.after_set do |styles|
98
102
  # Modernize B3 options
99
103
  # DEV-2.0: Can be removed with the removal of deprecated B3 constants.
@@ -142,6 +146,17 @@ module Datadog
142
146
  set_option(:propagation_inject_style, styles)
143
147
  end
144
148
  end
149
+
150
+ # Strictly stop at the first successfully serialized style.
151
+ # This prevents the tracer from enriching the extracted context with information from
152
+ # other valid propagations styles present in the request.
153
+ # @default `DD_TRACE_PROPAGATION_EXTRACT_FIRST` environment variable, otherwise `false`.
154
+ # @return [Boolean]
155
+ option :propagation_extract_first do |o|
156
+ o.env Tracing::Configuration::Ext::Distributed::EXTRACT_FIRST
157
+ o.default false
158
+ o.type :bool
159
+ end
145
160
  end
146
161
 
147
162
  # Enable trace collection and span generation.
@@ -177,11 +192,11 @@ module Datadog
177
192
 
178
193
  # Enable 128 bit trace id generation.
179
194
  #
180
- # @default `DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED` environment variable, otherwise `false`
195
+ # @default `DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED` environment variable, otherwise `true`
181
196
  # @return [Boolean]
182
197
  option :trace_id_128_bit_generation_enabled do |o|
183
198
  o.env Tracing::Configuration::Ext::ENV_TRACE_ID_128_BIT_GENERATION_ENABLED
184
- o.default false
199
+ o.default true
185
200
  o.type :bool
186
201
  end
187
202
 
@@ -49,7 +49,6 @@ module Datadog
49
49
  build_tags(digest).tap do |tags|
50
50
  inject_tags!(tags, data) unless tags.empty?
51
51
  end
52
-
53
52
  data
54
53
  end
55
54
 
@@ -83,19 +83,40 @@ module Datadog
83
83
 
84
84
  extracted_trace_digest = nil
85
85
 
86
- ::Datadog.configuration.tracing.distributed_tracing.propagation_extract_style.each do |style|
86
+ config = ::Datadog.configuration.tracing.distributed_tracing
87
+
88
+ config.propagation_extract_style.each do |style|
87
89
  propagator = @propagation_styles[style]
88
90
  next if propagator.nil?
89
91
 
90
92
  begin
91
- extracted_trace_digest = propagator.extract(data)
93
+ if extracted_trace_digest
94
+ # Return if we are only inspecting the first valid style.
95
+ next if config.propagation_extract_first
96
+
97
+ # Continue parsing styles to find the W3C `tracestate` header, if present.
98
+ # `tracestate` must always be propagated, as it might contain pass-through data that we don't control.
99
+ # @see https://www.w3.org/TR/2021/REC-trace-context-1-20211123/#mutating-the-tracestate-field
100
+ next if style != Configuration::Ext::Distributed::PROPAGATION_STYLE_TRACE_CONTEXT
101
+
102
+ if (tracecontext_digest = propagator.extract(data))
103
+ # Only parse if it represent the same trace as the successfully extracted one
104
+ next unless tracecontext_digest.trace_id == extracted_trace_digest.trace_id
105
+
106
+ # Preserve the `tracestate`
107
+ extracted_trace_digest = extracted_trace_digest.merge(
108
+ trace_state: tracecontext_digest.trace_state,
109
+ trace_state_unknown_fields: tracecontext_digest.trace_state_unknown_fields
110
+ )
111
+ end
112
+ else
113
+ extracted_trace_digest = propagator.extract(data)
114
+ end
92
115
  rescue => e
93
116
  ::Datadog.logger.error(
94
117
  "Error extracting distributed trace data. Cause: #{e} Location: #{Array(e.backtrace).first}"
95
118
  )
96
119
  end
97
-
98
- break if extracted_trace_digest
99
120
  end
100
121
 
101
122
  extracted_trace_digest
@@ -141,6 +141,37 @@ module Datadog
141
141
 
142
142
  freeze
143
143
  end
144
+
145
+ # Creates a copy of this object, modifying the provided fields.
146
+ # @param field_value_pairs [Hash<String>] the fields to be overwritten
147
+ # @return [TraceDigest] returns a copy of this object with the `field_value_pairs` modified
148
+ def merge(field_value_pairs)
149
+ # DEV: Because we want to sometimes freeze the values provided to `TraceDigest`, it's best
150
+ # DEV: to let `#initialize` decide how to handle each field, instead of duplicating that logic here.
151
+ TraceDigest.new(
152
+ **{
153
+ span_id: span_id,
154
+ span_name: span_name,
155
+ span_resource: span_resource,
156
+ span_service: span_service,
157
+ span_type: span_type,
158
+ trace_distributed_tags: trace_distributed_tags,
159
+ trace_hostname: trace_hostname,
160
+ trace_id: trace_id,
161
+ trace_name: trace_name,
162
+ trace_origin: trace_origin,
163
+ trace_process_id: trace_process_id,
164
+ trace_resource: trace_resource,
165
+ trace_runtime_id: trace_runtime_id,
166
+ trace_sampling_priority: trace_sampling_priority,
167
+ trace_service: trace_service,
168
+ trace_distributed_id: trace_distributed_id,
169
+ trace_flags: trace_flags,
170
+ trace_state: trace_state,
171
+ trace_state_unknown_fields: trace_state_unknown_fields,
172
+ }.merge!(field_value_pairs)
173
+ )
174
+ end
144
175
  end
145
176
  end
146
177
  end
@@ -3,8 +3,8 @@
3
3
  module DDTrace
4
4
  module VERSION
5
5
  MAJOR = 1
6
- MINOR = 16
7
- PATCH = 1
6
+ MINOR = 17
7
+ PATCH = 0
8
8
  PRE = nil
9
9
  BUILD = nil
10
10
  # PRE and BUILD above are modified for dev gems during gem build GHA workflow
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ddtrace
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.16.1
4
+ version: 1.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-08 00:00:00.000000000 Z
11
+ date: 2023-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.3.0
75
+ version: 0.4.0
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 0.3.0
82
+ version: 0.4.0
83
83
  description: |
84
84
  ddtrace is Datadog's tracing client for Ruby. It is used to trace requests
85
85
  as they flow across web servers, databases and microservices so that developers