datadog 2.41.0 → 2.42.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -2
- data/ext/datadog_profiling_native_extension/NativeExtensionDesign.md +12 -24
- data/ext/datadog_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +110 -61
- data/ext/datadog_profiling_native_extension/collectors_stack.c +11 -3
- data/ext/datadog_profiling_native_extension/collectors_thread_context.c +141 -103
- data/ext/datadog_profiling_native_extension/collectors_thread_context.h +7 -3
- data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +0 -10
- data/ext/datadog_profiling_native_extension/extconf.rb +52 -93
- data/ext/datadog_profiling_native_extension/heap_recorder.c +338 -255
- data/ext/datadog_profiling_native_extension/heap_recorder.h +46 -31
- data/ext/datadog_profiling_native_extension/native_extension_helpers.rb +0 -24
- data/ext/datadog_profiling_native_extension/private_vm_api_access.c +509 -440
- data/ext/datadog_profiling_native_extension/private_vm_api_access.h +15 -3
- data/ext/datadog_profiling_native_extension/profiling.c +2 -0
- data/ext/datadog_profiling_native_extension/ruby_helpers.c +1 -79
- data/ext/datadog_profiling_native_extension/ruby_helpers.h +0 -7
- data/ext/datadog_profiling_native_extension/stack_recorder.c +93 -61
- data/ext/datadog_profiling_native_extension/stack_recorder.h +12 -4
- data/ext/libdatadog_api/datadog_ruby_common.h +0 -10
- data/ext/libdatadog_api/di.c +10 -0
- data/ext/libdatadog_api/extconf.rb +3 -0
- data/ext/libdatadog_api/init.c +2 -0
- data/ext/libdatadog_api/otel_thread_context.c +232 -0
- data/ext/libdatadog_api/otel_thread_context.h +5 -0
- data/ext/libdatadog_extconf_helpers.rb +1 -1
- data/lib/datadog/appsec/assets/blocked.html +1 -108
- data/lib/datadog/core/configuration/components.rb +1 -0
- data/lib/datadog/core/crashtracking/component.rb +5 -1
- data/lib/datadog/data_streams/pathway_context.rb +20 -22
- data/lib/datadog/data_streams/processor.rb +31 -0
- data/lib/datadog/di/instrumenter.rb +41 -1
- data/lib/datadog/di/logger.rb +2 -2
- data/lib/datadog/di/probe.rb +9 -1
- data/lib/datadog/di/probe_notification_builder.rb +1 -0
- data/lib/datadog/di/remote.rb +3 -3
- data/lib/datadog/open_feature/evaluation_engine.rb +29 -3
- data/lib/datadog/open_feature/exposures/event.rb +10 -3
- data/lib/datadog/open_feature/ext.rb +19 -0
- data/lib/datadog/open_feature/flag_evaluation/aggregator.rb +236 -80
- data/lib/datadog/open_feature/flag_evaluation/writer.rb +179 -68
- data/lib/datadog/open_feature/hooks/flag_eval_evp_hook.rb +24 -21
- data/lib/datadog/open_feature/native_evaluator.rb +33 -6
- data/lib/datadog/open_feature/noop_evaluator.rb +5 -0
- data/lib/datadog/open_feature/provider.rb +11 -2
- data/lib/datadog/opentelemetry/sdk/propagator.rb +1 -1
- data/lib/datadog/opentelemetry/trace.rb +3 -0
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +3 -0
- data/lib/datadog/profiling/collectors/thread_context.rb +0 -4
- data/lib/datadog/profiling/component.rb +8 -16
- data/lib/datadog/tracing/contrib/active_record/events/sql.rb +1 -0
- data/lib/datadog/tracing/distributed/baggage.rb +0 -1
- data/lib/datadog/tracing/distributed/datadog.rb +3 -3
- data/lib/datadog/tracing/distributed/propagation.rb +3 -0
- data/lib/datadog/tracing/distributed/trace_context.rb +14 -271
- data/lib/datadog/tracing/distributed/trace_state/datadog.rb +233 -0
- data/lib/datadog/tracing/distributed/trace_state/ext.rb +44 -0
- data/lib/datadog/tracing/distributed/trace_state/open_telemetry.rb +156 -0
- data/lib/datadog/tracing/distributed/trace_state.rb +121 -0
- data/lib/datadog/tracing/otel_thread_context.rb +30 -0
- data/lib/datadog/tracing/remote.rb +195 -27
- data/lib/datadog/tracing/sampling/rule_sampler.rb +2 -0
- data/lib/datadog/tracing/trace_digest.rb +22 -4
- data/lib/datadog/tracing/trace_operation.rb +22 -10
- data/lib/datadog/tracing/tracer.rb +5 -5
- data/lib/datadog/version.rb +1 -1
- metadata +14 -8
- data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +0 -69
|
@@ -19,7 +19,6 @@ module Datadog
|
|
|
19
19
|
max_frames:,
|
|
20
20
|
tracer:,
|
|
21
21
|
endpoint_collection_enabled:,
|
|
22
|
-
waiting_for_gvl_threshold_ns:,
|
|
23
22
|
otel_context_enabled:,
|
|
24
23
|
native_filenames_enabled:,
|
|
25
24
|
show_classes:
|
|
@@ -31,7 +30,6 @@ module Datadog
|
|
|
31
30
|
max_frames: max_frames,
|
|
32
31
|
tracer_context_key: tracer_context_key,
|
|
33
32
|
endpoint_collection_enabled: endpoint_collection_enabled,
|
|
34
|
-
waiting_for_gvl_threshold_ns: waiting_for_gvl_threshold_ns,
|
|
35
33
|
otel_context_enabled: otel_context_enabled,
|
|
36
34
|
native_filenames_enabled: native_filenames_enabled,
|
|
37
35
|
show_classes: show_classes,
|
|
@@ -44,7 +42,6 @@ module Datadog
|
|
|
44
42
|
max_frames: 400,
|
|
45
43
|
tracer: nil,
|
|
46
44
|
endpoint_collection_enabled: false,
|
|
47
|
-
waiting_for_gvl_threshold_ns: 10_000_000,
|
|
48
45
|
otel_context_enabled: false,
|
|
49
46
|
native_filenames_enabled: true,
|
|
50
47
|
show_classes: false,
|
|
@@ -56,7 +53,6 @@ module Datadog
|
|
|
56
53
|
max_frames: max_frames,
|
|
57
54
|
tracer: tracer,
|
|
58
55
|
endpoint_collection_enabled: endpoint_collection_enabled,
|
|
59
|
-
waiting_for_gvl_threshold_ns: waiting_for_gvl_threshold_ns,
|
|
60
56
|
otel_context_enabled: otel_context_enabled,
|
|
61
57
|
native_filenames_enabled: native_filenames_enabled,
|
|
62
58
|
show_classes: show_classes,
|
|
@@ -65,6 +65,7 @@ module Datadog
|
|
|
65
65
|
gvl_profiling_enabled: enable_gvl_profiling?(settings, logger),
|
|
66
66
|
sighandler_sampling_enabled: settings.profiling.advanced.sighandler_sampling_enabled,
|
|
67
67
|
cpu_sampling_interval_ms: cpu_sampling_interval_ms,
|
|
68
|
+
waiting_for_gvl_threshold_ns: settings.profiling.advanced.waiting_for_gvl_threshold_ns,
|
|
68
69
|
)
|
|
69
70
|
|
|
70
71
|
internal_metadata = {
|
|
@@ -102,7 +103,6 @@ module Datadog
|
|
|
102
103
|
max_frames: settings.profiling.advanced.max_frames,
|
|
103
104
|
tracer: optional_tracer,
|
|
104
105
|
endpoint_collection_enabled: settings.profiling.advanced.endpoint.collection.enabled,
|
|
105
|
-
waiting_for_gvl_threshold_ns: settings.profiling.advanced.waiting_for_gvl_threshold_ns,
|
|
106
106
|
otel_context_enabled: settings.profiling.advanced.preview_otel_context_enabled,
|
|
107
107
|
native_filenames_enabled: settings.profiling.advanced.native_filenames_enabled,
|
|
108
108
|
show_classes: settings.profiling.advanced.experimental_show_classes_enabled,
|
|
@@ -227,11 +227,13 @@ module Datadog
|
|
|
227
227
|
return false
|
|
228
228
|
end
|
|
229
229
|
|
|
230
|
-
# Heap profiling
|
|
231
|
-
# (https://bugs.ruby-lang.org/issues/
|
|
232
|
-
if RubyVersion.is?("
|
|
230
|
+
# Heap profiling tracks live objects using an `ObjectSpace::WeakMap`, which could corrupt its internal
|
|
231
|
+
# state during compaction before these versions (https://bugs.ruby-lang.org/issues/19529).
|
|
232
|
+
if RubyVersion.is?("< 3.1.4") || RubyVersion.is?(">= 3.2", "< 3.2.3")
|
|
233
233
|
logger.warn(
|
|
234
|
-
"
|
|
234
|
+
"Current Ruby version (#{RUBY_VERSION}) cannot support heap profiling due to a VM bug. " \
|
|
235
|
+
"Please upgrade to Ruby >= 3.1.4 or >= 3.2.3 in order to use this feature. " \
|
|
236
|
+
"Heap profiling has been disabled."
|
|
235
237
|
)
|
|
236
238
|
return false
|
|
237
239
|
end
|
|
@@ -252,17 +254,7 @@ module Datadog
|
|
|
252
254
|
private_class_method def self.enable_heap_size_profiling?(settings, heap_profiling_enabled, logger)
|
|
253
255
|
heap_size_profiling_enabled = settings.profiling.advanced.experimental_heap_size_enabled
|
|
254
256
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if RubyVersion.is?(">= 4")
|
|
258
|
-
logger.info(
|
|
259
|
-
"Heap live size profiling is currently incompatible with Ruby 4 and has been disabled. " \
|
|
260
|
-
"Heap live objects is not affected and remains enabled."
|
|
261
|
-
)
|
|
262
|
-
return false
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
true
|
|
257
|
+
heap_profiling_enabled && heap_size_profiling_enabled
|
|
266
258
|
end
|
|
267
259
|
|
|
268
260
|
private_class_method def self.no_signals_workaround_enabled?(settings, logger) # rubocop:disable Metrics/MethodLength
|
|
@@ -46,6 +46,7 @@ module Datadog
|
|
|
46
46
|
span.resource = payload.fetch(:sql)
|
|
47
47
|
span.type = Tracing::Metadata::Ext::SQL::TYPE
|
|
48
48
|
|
|
49
|
+
span.set_tag(Tracing::Metadata::Ext::TAG_KIND, Tracing::Metadata::Ext::SpanKind::TAG_CLIENT)
|
|
49
50
|
span.set_tag(Tracing::Metadata::Ext::TAG_COMPONENT, Ext::TAG_COMPONENT)
|
|
50
51
|
span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_SQL)
|
|
51
52
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "../metadata/ext"
|
|
4
4
|
require_relative "../trace_digest"
|
|
5
|
-
require_relative "
|
|
5
|
+
require_relative "trace_state/datadog"
|
|
6
6
|
require_relative "../utils"
|
|
7
7
|
require_relative "helpers"
|
|
8
8
|
|
|
@@ -129,7 +129,7 @@ module Datadog
|
|
|
129
129
|
def inject_tags!(tags, data)
|
|
130
130
|
return set_tags_propagation_error(reason: "disabled") if tags_disabled?
|
|
131
131
|
|
|
132
|
-
encoded_tags =
|
|
132
|
+
encoded_tags = TraceState::Datadog.encode(tags)
|
|
133
133
|
|
|
134
134
|
return set_tags_propagation_error(reason: "inject_max_size") if tags_too_large?(encoded_tags, scenario: "inject")
|
|
135
135
|
|
|
@@ -155,7 +155,7 @@ module Datadog
|
|
|
155
155
|
return set_tags_propagation_error(reason: "disabled") if tags_disabled?
|
|
156
156
|
return set_tags_propagation_error(reason: "extract_max_size") if tags_too_large?(tags, scenario: "extract")
|
|
157
157
|
|
|
158
|
-
tags_hash =
|
|
158
|
+
tags_hash = TraceState::Datadog.decode(tags)
|
|
159
159
|
# Only extract keys with the expected Datadog prefix
|
|
160
160
|
tags_hash.select! do |key, _|
|
|
161
161
|
key.start_with?(Tracing::Metadata::Ext::Distributed::TAGS_PREFIX) && key != EXCLUDED_TAG
|
|
@@ -146,6 +146,9 @@ module Datadog
|
|
|
146
146
|
span_id: parent_id,
|
|
147
147
|
trace_state: tracecontext_digest.trace_state,
|
|
148
148
|
trace_state_unknown_fields: tracecontext_digest.trace_state_unknown_fields,
|
|
149
|
+
trace_otel_random_value: tracecontext_digest.trace_otel_random_value,
|
|
150
|
+
trace_otel_threshold: tracecontext_digest.trace_otel_threshold,
|
|
151
|
+
trace_otel_unknown_fields: tracecontext_digest.trace_otel_unknown_fields,
|
|
149
152
|
trace_distributed_tags: distributed_tags
|
|
150
153
|
)
|
|
151
154
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "../../core/utils"
|
|
4
3
|
require_relative "helpers"
|
|
4
|
+
require_relative "trace_state"
|
|
5
5
|
|
|
6
6
|
module Datadog
|
|
7
7
|
module Tracing
|
|
@@ -30,7 +30,7 @@ module Datadog
|
|
|
30
30
|
if (traceparent = build_traceparent(digest))
|
|
31
31
|
data[@traceparent_key] = traceparent
|
|
32
32
|
|
|
33
|
-
if (tracestate =
|
|
33
|
+
if (tracestate = TraceState.serialize_digest(digest))
|
|
34
34
|
data[@tracestate_key] = tracestate
|
|
35
35
|
end
|
|
36
36
|
end
|
|
@@ -45,11 +45,12 @@ module Datadog
|
|
|
45
45
|
|
|
46
46
|
return unless trace_id # Could not parse traceparent
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
trace_state = TraceState.extract(fetcher[@tracestate_key])
|
|
49
|
+
dd = trace_state.datadog
|
|
50
|
+
ot = trace_state.open_telemetry
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
tags = dd.tags
|
|
53
|
+
sampling_priority = parse_priority_sampling(sampled, dd.sampling_priority) do |decision|
|
|
53
54
|
case decision
|
|
54
55
|
when String
|
|
55
56
|
tags ||= {}
|
|
@@ -61,17 +62,20 @@ module Datadog
|
|
|
61
62
|
|
|
62
63
|
tags ||= {}
|
|
63
64
|
tags[Tracing::Metadata::Ext::Distributed::TAG_DD_PARENT_ID] =
|
|
64
|
-
ts_parent_id || Tracing::Metadata::Ext::Distributed::DD_PARENT_ID_DEFAULT
|
|
65
|
+
dd.ts_parent_id || Tracing::Metadata::Ext::Distributed::DD_PARENT_ID_DEFAULT
|
|
65
66
|
|
|
66
67
|
TraceDigest.new(
|
|
67
68
|
span_id: parent_id,
|
|
68
69
|
trace_id: trace_id,
|
|
69
|
-
trace_origin: origin,
|
|
70
|
+
trace_origin: dd.origin,
|
|
70
71
|
trace_sampling_priority: sampling_priority,
|
|
71
72
|
trace_distributed_tags: tags,
|
|
72
73
|
trace_flags: trace_flags,
|
|
73
|
-
trace_state:
|
|
74
|
-
trace_state_unknown_fields: unknown_fields,
|
|
74
|
+
trace_state: trace_state.unknown_vendors,
|
|
75
|
+
trace_state_unknown_fields: dd.unknown_fields,
|
|
76
|
+
trace_otel_random_value: ot.random_value,
|
|
77
|
+
trace_otel_threshold: ot.threshold,
|
|
78
|
+
trace_otel_unknown_fields: ot.unknown_fields,
|
|
75
79
|
span_remote: true,
|
|
76
80
|
)
|
|
77
81
|
end
|
|
@@ -121,133 +125,6 @@ module Datadog
|
|
|
121
125
|
trace_flags
|
|
122
126
|
end
|
|
123
127
|
|
|
124
|
-
# @see https://www.w3.org/TR/trace-context/#tracestate-header
|
|
125
|
-
def build_tracestate(digest)
|
|
126
|
-
tracestate = +"dd="
|
|
127
|
-
append_dd(tracestate, last_dd_parent_id(digest))
|
|
128
|
-
append_dd(tracestate, "s:#{digest.trace_sampling_priority};") if digest.trace_sampling_priority
|
|
129
|
-
append_dd(tracestate, "o:#{serialize_origin(digest.trace_origin)};") if digest.trace_origin
|
|
130
|
-
|
|
131
|
-
# Replacing this by safe navigation seems to have a different behaviour on Rubies <= 3.0.
|
|
132
|
-
# It cause a LocalJumpError in the CI.
|
|
133
|
-
if digest.trace_distributed_tags # rubocop:disable Style/SafeNavigation
|
|
134
|
-
digest.trace_distributed_tags.each do |name, value|
|
|
135
|
-
tag = "t.#{serialize_tag_key(name)}:#{serialize_tag_value(value)};"
|
|
136
|
-
|
|
137
|
-
# If tracestate size limit is exceeded, drop the remaining data.
|
|
138
|
-
break unless append_dd(tracestate, tag)
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
append_dd(tracestate, digest.trace_state_unknown_fields) if digest.trace_state_unknown_fields
|
|
143
|
-
vendors = split_tracestate(digest.trace_state)
|
|
144
|
-
|
|
145
|
-
# Is there any Datadog-specific information to propagate.
|
|
146
|
-
# Check for > 3 size because the empty prefix `dd=` has 3 characters.
|
|
147
|
-
if tracestate.bytesize > 3
|
|
148
|
-
# Propagate upstream tracestate with `dd=...` appended to the list
|
|
149
|
-
tracestate.chop! # Removes trailing `;` from Datadog trace state string.
|
|
150
|
-
|
|
151
|
-
if vendors && !vendors.empty?
|
|
152
|
-
# Delete existing `dd=` tracestate fields, if present.
|
|
153
|
-
vendors.reject! { |v| v.start_with?("dd=") }
|
|
154
|
-
|
|
155
|
-
# Ensure the list has at most 31 elements, as we need to prepend Datadog's
|
|
156
|
-
# entry and the limit is 32 elements total.
|
|
157
|
-
vendors.first(TRACESTATE_MAX_LIST_MEMBERS - 1).each do |vendor|
|
|
158
|
-
break if tracestate.bytesize + vendor.bytesize + 1 > TRACESTATE_MAX_SIZE_LIMIT
|
|
159
|
-
|
|
160
|
-
tracestate << "," << vendor
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
tracestate.to_s
|
|
165
|
-
elsif vendors && !vendors.empty?
|
|
166
|
-
vendors.join(",")
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
# Appends a Datadog tracestate field when it fits.
|
|
171
|
-
def append_dd(tracestate, field)
|
|
172
|
-
return true if field.empty?
|
|
173
|
-
|
|
174
|
-
# We add 1 to the limit because of the trailing semicolon, which will be removed before returning.
|
|
175
|
-
return false if tracestate.bytesize + field.bytesize > (TRACESTATE_VALUE_SIZE_LIMIT + 1)
|
|
176
|
-
|
|
177
|
-
tracestate << field
|
|
178
|
-
true
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def last_dd_parent_id(digest)
|
|
182
|
-
if !digest.span_remote
|
|
183
|
-
span_id = digest.span_id || 0 # Fall back to zero (invalid) if not present
|
|
184
|
-
"p:#{format("%016x", span_id)};"
|
|
185
|
-
elsif digest.trace_distributed_tags&.key?(Tracing::Metadata::Ext::Distributed::TAG_DD_PARENT_ID)
|
|
186
|
-
"p:#{digest.trace_distributed_tags[Tracing::Metadata::Ext::Distributed::TAG_DD_PARENT_ID]};"
|
|
187
|
-
else
|
|
188
|
-
""
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
# If any characters in <origin_value> are invalid, replace each invalid character with 0x5F (underscore).
|
|
193
|
-
# Invalid characters are: characters outside the ASCII range 0x20 to 0x7E,
|
|
194
|
-
# 0x2C (comma), 0x3B (semi-colon), and 0x7E (tilde).
|
|
195
|
-
# Then, remap 0x3D (equals) to 0x7E (tilde)
|
|
196
|
-
def serialize_origin(value)
|
|
197
|
-
# DEV: It's unlikely that characters will be out of range, as they mostly
|
|
198
|
-
# DEV: come from Datadog-controlled sources.
|
|
199
|
-
# DEV: Trying to `match?` is measurably faster than a `gsub` that does not match.
|
|
200
|
-
value = if INVALID_ORIGIN_CHARS.match?(value)
|
|
201
|
-
value.gsub(INVALID_ORIGIN_CHARS, "_")
|
|
202
|
-
else
|
|
203
|
-
value
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
if REMAP_ORIGIN_CHARS.match?(value)
|
|
207
|
-
value.gsub(REMAP_ORIGIN_CHARS, "~")
|
|
208
|
-
else
|
|
209
|
-
value
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
# Serialize `_dd.p.{key}` by first removing the `_dd.p.` prefix.
|
|
214
|
-
# Then replacing invalid characters with `_`.
|
|
215
|
-
#
|
|
216
|
-
# The argument `name` is always frozen.
|
|
217
|
-
# Returns a new String object for the serialized key.
|
|
218
|
-
def serialize_tag_key(name)
|
|
219
|
-
key = name.delete_prefix(Tracing::Metadata::Ext::Distributed::TAGS_PREFIX)
|
|
220
|
-
|
|
221
|
-
# DEV: It's unlikely that characters will be out of range, as they mostly
|
|
222
|
-
# DEV: come from Datadog-controlled sources.
|
|
223
|
-
# DEV: Trying to `match?` is measurably faster than a `gsub!` that does not match.
|
|
224
|
-
key.gsub!(INVALID_TAG_KEY_CHARS, "_") if INVALID_TAG_KEY_CHARS.match?(key)
|
|
225
|
-
|
|
226
|
-
key
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
# Replaces invalid characters with `_`, then replaces `=` with `~`.
|
|
230
|
-
#
|
|
231
|
-
# The argument `value` belongs to {TraceDigest}, thus should not be directly modified.
|
|
232
|
-
# Returns a new String object for the serialized value.
|
|
233
|
-
def serialize_tag_value(value)
|
|
234
|
-
# DEV: It's unlikely that characters will be out of range, as they mostly
|
|
235
|
-
# DEV: come from Datadog-controlled sources.
|
|
236
|
-
# DEV: Trying to `match?` is measurably faster than a `gsub` that does not match.
|
|
237
|
-
ret = if INVALID_TAG_VALUE_CHARS.match?(value)
|
|
238
|
-
value.gsub(INVALID_TAG_VALUE_CHARS, "_")
|
|
239
|
-
else
|
|
240
|
-
value
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
# DEV: Checking for an unlikely '=' is faster than a no-op `tr`.
|
|
244
|
-
if ret.include?("=")
|
|
245
|
-
ret.tr("=", "~")
|
|
246
|
-
else
|
|
247
|
-
ret
|
|
248
|
-
end
|
|
249
|
-
end
|
|
250
|
-
|
|
251
128
|
def extract_traceparent(traceparent)
|
|
252
129
|
trace_id, parent_id, trace_flags = parse_traceparent_string(traceparent)
|
|
253
130
|
|
|
@@ -285,78 +162,6 @@ module Datadog
|
|
|
285
162
|
trace_flags & TRACE_FLAGS_SAMPLED
|
|
286
163
|
end
|
|
287
164
|
|
|
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.
|
|
293
|
-
def extract_tracestate(tracestate)
|
|
294
|
-
vendors = split_tracestate(tracestate)
|
|
295
|
-
return unless vendors && !vendors.empty?
|
|
296
|
-
|
|
297
|
-
tracestate = vendors.join(",")
|
|
298
|
-
|
|
299
|
-
# Find Datadog's `dd=` tracestate field.
|
|
300
|
-
idx = vendors.index { |v| v.start_with?("dd=") }
|
|
301
|
-
return tracestate unless idx
|
|
302
|
-
|
|
303
|
-
# Delete `dd=` prefix
|
|
304
|
-
dd_tracestate = vendors.delete_at(idx)
|
|
305
|
-
dd_tracestate.slice!(0..2)
|
|
306
|
-
|
|
307
|
-
origin, sampling_priority, ts_parent_id, tags, unknown_fields = extract_datadog_fields(dd_tracestate)
|
|
308
|
-
|
|
309
|
-
[vendors.join(","), sampling_priority, origin, ts_parent_id, tags, unknown_fields]
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
def extract_datadog_fields(dd_tracestate)
|
|
313
|
-
sampling_priority = nil
|
|
314
|
-
origin = nil
|
|
315
|
-
ts_parent_id = nil
|
|
316
|
-
tags = nil
|
|
317
|
-
unknown_fields = nil
|
|
318
|
-
|
|
319
|
-
# DEV: Since Ruby 2.6 `split` can receive a block, so `each` can be removed then.
|
|
320
|
-
dd_tracestate.split(";").each do |pair|
|
|
321
|
-
key, value = pair.split(":", 2)
|
|
322
|
-
case key
|
|
323
|
-
when "s"
|
|
324
|
-
sampling_priority = begin
|
|
325
|
-
Integer(value)
|
|
326
|
-
rescue
|
|
327
|
-
nil
|
|
328
|
-
end
|
|
329
|
-
when "o"
|
|
330
|
-
origin = value
|
|
331
|
-
when "p"
|
|
332
|
-
ts_parent_id = value
|
|
333
|
-
when /^t\./
|
|
334
|
-
key.slice!(0..1) # Delete `t.` prefix
|
|
335
|
-
|
|
336
|
-
# Ignore the high order 64 bit trace id propagation tag to avoid confusion,
|
|
337
|
-
# the single source of truth is from traceparent
|
|
338
|
-
next if key == Tracing::Metadata::Ext::Distributed::TID
|
|
339
|
-
|
|
340
|
-
value = deserialize_tag_value(value)
|
|
341
|
-
|
|
342
|
-
tags ||= {}
|
|
343
|
-
tags["#{Tracing::Metadata::Ext::Distributed::TAGS_PREFIX}#{key}"] = value
|
|
344
|
-
else
|
|
345
|
-
unknown_fields ||= +""
|
|
346
|
-
unknown_fields << pair
|
|
347
|
-
unknown_fields << ";"
|
|
348
|
-
end
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
[origin, sampling_priority, ts_parent_id, tags, unknown_fields]
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
# Restore `~` back to `=`.
|
|
355
|
-
def deserialize_tag_value(value)
|
|
356
|
-
value.tr!("~", "=")
|
|
357
|
-
value
|
|
358
|
-
end
|
|
359
|
-
|
|
360
165
|
# If `sampled` and `sampling_priority` disagree, `sampled` overrides the decision.
|
|
361
166
|
# @return [Integer] one of the {Datadog::Tracing::Sampling::Ext::Priority} values
|
|
362
167
|
# @yieldparam the new decision maker (either :drop or a new decision maker String value).
|
|
@@ -382,47 +187,9 @@ module Datadog
|
|
|
382
187
|
end
|
|
383
188
|
end
|
|
384
189
|
|
|
385
|
-
# We MUST NOT propagate partial members, but we SHOULD try
|
|
386
|
-
# to parse as much of the tracestate as possible.
|
|
387
|
-
def split_tracestate(tracestate)
|
|
388
|
-
return unless tracestate
|
|
389
|
-
|
|
390
|
-
remove_last_member = false
|
|
391
|
-
if tracestate.bytesize > TRACESTATE_MAX_SIZE_LIMIT
|
|
392
|
-
# We parse 1 byte over the limit to detect if the last member
|
|
393
|
-
# is a partial member (toss) or ends exactly at the limit (keep).
|
|
394
|
-
remove_last_member = tracestate.byteslice(TRACESTATE_MAX_SIZE_LIMIT, 1) != ","
|
|
395
|
-
|
|
396
|
-
# To ensure we don't have a trailing partial UTF-8 codepoint, we keep one extra byte
|
|
397
|
-
# and safely remove it with `#chop`.
|
|
398
|
-
# `#chop` walks back the string until it finds a valid character boundary and deletes
|
|
399
|
-
# from there.
|
|
400
|
-
tracestate = tracestate.byteslice(0, TRACESTATE_MAX_SIZE_LIMIT + 1) #: String
|
|
401
|
-
tracestate = tracestate.chop
|
|
402
|
-
end
|
|
403
|
-
|
|
404
|
-
tracestate = ::Datadog::Core::Utils.utf8_encode(tracestate, placeholder: nil)
|
|
405
|
-
return unless tracestate
|
|
406
|
-
|
|
407
|
-
vendors = tracestate.split(",", TRACESTATE_MAX_LIST_MEMBERS + 1)
|
|
408
|
-
if vendors.length > TRACESTATE_MAX_LIST_MEMBERS || remove_last_member
|
|
409
|
-
vendors.pop
|
|
410
|
-
end
|
|
411
|
-
|
|
412
|
-
vendors.each(&:strip!)
|
|
413
|
-
vendors.pop while vendors.last == ""
|
|
414
|
-
vendors
|
|
415
|
-
end
|
|
416
|
-
|
|
417
190
|
TRACEPARENT_MAX_SIZE_LIMIT = 512
|
|
418
191
|
private_constant :TRACEPARENT_MAX_SIZE_LIMIT
|
|
419
192
|
|
|
420
|
-
TRACESTATE_MAX_SIZE_LIMIT = 512
|
|
421
|
-
private_constant :TRACESTATE_MAX_SIZE_LIMIT
|
|
422
|
-
|
|
423
|
-
TRACESTATE_MAX_LIST_MEMBERS = 32
|
|
424
|
-
private_constant :TRACESTATE_MAX_LIST_MEMBERS
|
|
425
|
-
|
|
426
193
|
# Version 0xFF is invalid as per spec
|
|
427
194
|
# @see https://www.w3.org/TR/trace-context/#version
|
|
428
195
|
INVALID_VERSION = "ff"
|
|
@@ -437,30 +204,6 @@ module Datadog
|
|
|
437
204
|
# @see https://www.w3.org/TR/trace-context/#trace-flags
|
|
438
205
|
TRACE_FLAGS_SAMPLED = 0b00000001
|
|
439
206
|
private_constant :TRACE_FLAGS_SAMPLED
|
|
440
|
-
|
|
441
|
-
# The limit is inclusive: sizes *greater* than 256 are disallowed.
|
|
442
|
-
# @see https://www.w3.org/TR/trace-context/#value
|
|
443
|
-
TRACESTATE_VALUE_SIZE_LIMIT = 256
|
|
444
|
-
private_constant :TRACESTATE_VALUE_SIZE_LIMIT
|
|
445
|
-
|
|
446
|
-
# Replace all characters with `_`, except ASCII characters 0x20-0x7E.
|
|
447
|
-
# Additionally, `,`, ';', and `~` must also be replaced by `_`.
|
|
448
|
-
INVALID_ORIGIN_CHARS = /[\u0000-\u0019,;~\u007F-\u{10FFFF}]/.freeze
|
|
449
|
-
private_constant :INVALID_ORIGIN_CHARS
|
|
450
|
-
|
|
451
|
-
# Additionally, remap `=` to `~`
|
|
452
|
-
REMAP_ORIGIN_CHARS = /=/.freeze
|
|
453
|
-
private_constant :REMAP_ORIGIN_CHARS
|
|
454
|
-
|
|
455
|
-
# Replace all characters with `_`, except ASCII characters 0x21-0x7E.
|
|
456
|
-
# Additionally, `,` and `=` must also be replaced by `_`.
|
|
457
|
-
INVALID_TAG_KEY_CHARS = /[\u0000-\u0020,=\u007F-\u{10FFFF}]/.freeze
|
|
458
|
-
private_constant :INVALID_TAG_KEY_CHARS
|
|
459
|
-
|
|
460
|
-
# Replace all characters with `_`, except ASCII characters 0x20-0x7D.
|
|
461
|
-
# Additionally, `,` and `;` must also be replaced by `_`.
|
|
462
|
-
INVALID_TAG_VALUE_CHARS = /[\u0000-\u001F,;\u007E-\u{10FFFF}]/.freeze
|
|
463
|
-
private_constant :INVALID_TAG_VALUE_CHARS
|
|
464
207
|
end
|
|
465
208
|
end
|
|
466
209
|
end
|