ddtrace 1.6.0 → 1.7.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 +50 -1
- data/ext/ddtrace_profiling_loader/extconf.rb +1 -1
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.c +66 -6
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +51 -54
- data/ext/ddtrace_profiling_native_extension/collectors_stack.c +11 -13
- data/ext/ddtrace_profiling_native_extension/extconf.rb +1 -1
- data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +3 -2
- data/ext/ddtrace_profiling_native_extension/setup_signal_handler.c +96 -0
- data/ext/ddtrace_profiling_native_extension/setup_signal_handler.h +7 -0
- data/ext/ddtrace_profiling_native_extension/stack_recorder.c +70 -18
- data/ext/ddtrace_profiling_native_extension/stack_recorder.h +1 -0
- data/lib/datadog/appsec/assets/blocked.html +98 -3
- data/lib/datadog/appsec/assets/blocked.json +1 -0
- data/lib/datadog/appsec/assets/blocked.text +5 -0
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +35 -46
- data/lib/datadog/appsec/assets/waf_rules/risky.json +1 -1
- data/lib/datadog/appsec/assets/waf_rules/strict.json +46 -1
- data/lib/datadog/appsec/assets.rb +2 -2
- data/lib/datadog/appsec/configuration/settings.rb +6 -0
- data/lib/datadog/appsec/configuration.rb +4 -0
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +4 -8
- data/lib/datadog/appsec/contrib/rack/request.rb +17 -0
- data/lib/datadog/appsec/contrib/rack/request_body_middleware.rb +2 -2
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +2 -2
- data/lib/datadog/appsec/contrib/rails/patcher.rb +3 -6
- data/lib/datadog/appsec/contrib/sinatra/ext.rb +1 -0
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +1 -1
- data/lib/datadog/appsec/contrib/sinatra/patcher.rb +11 -8
- data/lib/datadog/appsec/extensions.rb +10 -0
- data/lib/datadog/appsec/processor.rb +18 -0
- data/lib/datadog/appsec/response.rb +54 -0
- data/lib/datadog/core/runtime/ext.rb +1 -1
- data/lib/datadog/opentracer/distributed_headers.rb +5 -7
- data/lib/datadog/opentracer/rack_propagator.rb +0 -3
- data/lib/datadog/opentracer/text_map_propagator.rb +5 -7
- data/lib/datadog/profiling/collectors/cpu_and_wall_time.rb +10 -4
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +4 -0
- data/lib/datadog/profiling/collectors/old_stack.rb +7 -0
- data/lib/datadog/profiling/exporter.rb +5 -0
- data/lib/datadog/profiling/old_recorder.rb +8 -0
- data/lib/datadog/profiling/profiler.rb +7 -0
- data/lib/datadog/profiling/scheduler.rb +4 -7
- data/lib/datadog/profiling/stack_recorder.rb +22 -0
- data/lib/datadog/profiling/tasks/setup.rb +0 -7
- data/lib/datadog/tracing/contrib/delayed_job/plugin.rb +4 -0
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +2 -1
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +6 -12
- data/lib/datadog/tracing/contrib/grpc/distributed/fetcher.rb +27 -0
- data/lib/datadog/tracing/contrib/grpc/distributed/propagation.rb +38 -0
- data/lib/datadog/tracing/contrib/grpc/patcher.rb +0 -2
- data/lib/datadog/tracing/contrib/http/distributed/fetcher.rb +32 -0
- data/lib/datadog/tracing/contrib/http/distributed/propagation.rb +33 -0
- data/lib/datadog/tracing/contrib/kafka/consumer_event.rb +1 -0
- data/lib/datadog/tracing/contrib/kafka/events/produce_operation/send_messages.rb +1 -0
- data/lib/datadog/tracing/contrib/kafka/events/producer/deliver_messages.rb +1 -0
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +2 -0
- data/lib/datadog/tracing/contrib/que/tracer.rb +2 -0
- data/lib/datadog/tracing/contrib/racecar/events/batch.rb +4 -1
- data/lib/datadog/tracing/contrib/racecar/events/message.rb +4 -1
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +2 -0
- data/lib/datadog/tracing/contrib/redis/instrumentation.rb +2 -0
- data/lib/datadog/tracing/contrib/redis/integration.rb +2 -1
- data/lib/datadog/tracing/contrib/redis/patcher.rb +2 -3
- data/lib/datadog/tracing/contrib/resque/resque_job.rb +2 -0
- data/lib/datadog/tracing/contrib/shoryuken/tracer.rb +2 -0
- data/lib/datadog/tracing/contrib/sidekiq/client_tracer.rb +5 -0
- data/lib/datadog/tracing/contrib/sidekiq/server_tracer.rb +5 -0
- data/lib/datadog/tracing/contrib/sneakers/tracer.rb +2 -0
- data/lib/datadog/tracing/distributed/b3.rb +66 -0
- data/lib/datadog/tracing/distributed/b3_single.rb +66 -0
- data/lib/datadog/tracing/distributed/datadog.rb +153 -0
- data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +1 -0
- data/lib/datadog/tracing/distributed/fetcher.rb +30 -0
- data/lib/datadog/tracing/distributed/headers/ext.rb +18 -16
- data/lib/datadog/tracing/distributed/helpers.rb +7 -6
- data/lib/datadog/tracing/distributed/propagation.rb +127 -0
- data/lib/datadog/tracing/propagation/http.rb +3 -106
- data/lib/datadog/tracing/trace_segment.rb +1 -1
- data/lib/ddtrace/transport/trace_formatter.rb +2 -5
- data/lib/ddtrace/version.rb +1 -1
- metadata +19 -14
- data/lib/datadog/tracing/distributed/headers/b3.rb +0 -55
- data/lib/datadog/tracing/distributed/headers/b3_single.rb +0 -67
- data/lib/datadog/tracing/distributed/headers/datadog.rb +0 -144
- data/lib/datadog/tracing/distributed/headers/parser.rb +0 -37
- data/lib/datadog/tracing/distributed/metadata/b3.rb +0 -55
- data/lib/datadog/tracing/distributed/metadata/b3_single.rb +0 -66
- data/lib/datadog/tracing/distributed/metadata/datadog.rb +0 -73
- data/lib/datadog/tracing/distributed/metadata/parser.rb +0 -34
- data/lib/datadog/tracing/propagation/grpc.rb +0 -98
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
3
|
+
|
|
4
|
+
require_relative 'helpers'
|
|
5
|
+
require_relative '../trace_digest'
|
|
6
|
+
|
|
7
|
+
module Datadog
|
|
8
|
+
module Tracing
|
|
9
|
+
module Distributed
|
|
10
|
+
# B3 single header-style trace propagation.
|
|
11
|
+
#
|
|
12
|
+
# DEV: Format:
|
|
13
|
+
# DEV: b3: {TraceId}-{SpanId}-{SamplingState}-{ParentSpanId}
|
|
14
|
+
# DEV: https://github.com/apache/incubator-zipkin-b3-propagation/tree/7c6e9f14d6627832bd80baa87ac7dabee7be23cf#single-header
|
|
15
|
+
# DEV: `{SamplingState}` and `{ParentSpanId}` are optional
|
|
16
|
+
#
|
|
17
|
+
# @see https://github.com/openzipkin/b3-propagation#single-header
|
|
18
|
+
class B3Single
|
|
19
|
+
B3_SINGLE_HEADER_KEY = 'b3'
|
|
20
|
+
|
|
21
|
+
def initialize(fetcher:, key: B3_SINGLE_HEADER_KEY)
|
|
22
|
+
@key = key
|
|
23
|
+
@fetcher = fetcher
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def inject!(digest, env)
|
|
27
|
+
return if digest.nil?
|
|
28
|
+
|
|
29
|
+
# DEV: We need these to be hex encoded
|
|
30
|
+
value = "#{digest.trace_id.to_s(16)}-#{digest.span_id.to_s(16)}"
|
|
31
|
+
|
|
32
|
+
if digest.trace_sampling_priority
|
|
33
|
+
sampling_priority = Helpers.clamp_sampling_priority(
|
|
34
|
+
digest.trace_sampling_priority
|
|
35
|
+
)
|
|
36
|
+
value += "-#{sampling_priority}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
env[@key] = value
|
|
40
|
+
env
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def extract(env)
|
|
44
|
+
fetcher = @fetcher.new(env)
|
|
45
|
+
value = fetcher[@key]
|
|
46
|
+
|
|
47
|
+
return unless value
|
|
48
|
+
|
|
49
|
+
parts = value.split('-')
|
|
50
|
+
trace_id = Helpers.value_to_id(parts[0], 16) unless parts.empty?
|
|
51
|
+
span_id = Helpers.value_to_id(parts[1], 16) if parts.length > 1
|
|
52
|
+
sampling_priority = Helpers.value_to_number(parts[2]) if parts.length > 2
|
|
53
|
+
|
|
54
|
+
# Return if this propagation is not valid
|
|
55
|
+
return unless trace_id && span_id
|
|
56
|
+
|
|
57
|
+
TraceDigest.new(
|
|
58
|
+
span_id: span_id,
|
|
59
|
+
trace_id: trace_id,
|
|
60
|
+
trace_sampling_priority: sampling_priority
|
|
61
|
+
)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
3
|
+
|
|
4
|
+
require_relative '../metadata/ext'
|
|
5
|
+
require_relative '../trace_digest'
|
|
6
|
+
require_relative 'datadog_tags_codec'
|
|
7
|
+
|
|
8
|
+
module Datadog
|
|
9
|
+
module Tracing
|
|
10
|
+
module Distributed
|
|
11
|
+
# Datadog-style trace propagation.
|
|
12
|
+
class Datadog
|
|
13
|
+
TRACE_ID_KEY = 'x-datadog-trace-id'
|
|
14
|
+
PARENT_ID_KEY = 'x-datadog-parent-id'
|
|
15
|
+
SAMPLING_PRIORITY_KEY = 'x-datadog-sampling-priority'
|
|
16
|
+
ORIGIN_KEY = 'x-datadog-origin'
|
|
17
|
+
# Distributed trace-level tags
|
|
18
|
+
TAGS_KEY = 'x-datadog-tags'
|
|
19
|
+
|
|
20
|
+
# Prefix used by all Datadog-specific distributed tags
|
|
21
|
+
TAGS_PREFIX = 'x-datadog-'
|
|
22
|
+
|
|
23
|
+
def initialize(
|
|
24
|
+
fetcher:,
|
|
25
|
+
trace_id_key: TRACE_ID_KEY,
|
|
26
|
+
parent_id_key: PARENT_ID_KEY,
|
|
27
|
+
sampling_priority_key: SAMPLING_PRIORITY_KEY,
|
|
28
|
+
origin_key: ORIGIN_KEY,
|
|
29
|
+
tags_key: TAGS_KEY
|
|
30
|
+
)
|
|
31
|
+
@trace_id_key = trace_id_key
|
|
32
|
+
@parent_id_key = parent_id_key
|
|
33
|
+
@sampling_priority_key = sampling_priority_key
|
|
34
|
+
@origin_key = origin_key
|
|
35
|
+
@tags_key = tags_key
|
|
36
|
+
@fetcher = fetcher
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def inject!(digest, data)
|
|
40
|
+
return if digest.nil?
|
|
41
|
+
|
|
42
|
+
data[@trace_id_key] = digest.trace_id.to_s
|
|
43
|
+
data[@parent_id_key] = digest.span_id.to_s
|
|
44
|
+
data[@sampling_priority_key] = digest.trace_sampling_priority.to_s if digest.trace_sampling_priority
|
|
45
|
+
data[@origin_key] = digest.trace_origin.to_s if digest.trace_origin
|
|
46
|
+
|
|
47
|
+
inject_tags(digest, data)
|
|
48
|
+
|
|
49
|
+
data
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def extract(data)
|
|
53
|
+
fetcher = @fetcher.new(data)
|
|
54
|
+
trace_id = fetcher.id(@trace_id_key)
|
|
55
|
+
parent_id = fetcher.id(@parent_id_key)
|
|
56
|
+
sampling_priority = fetcher.number(@sampling_priority_key)
|
|
57
|
+
origin = fetcher[@origin_key]
|
|
58
|
+
|
|
59
|
+
# Return early if this propagation is not valid
|
|
60
|
+
# DEV: To be valid we need to have a trace id and a parent id
|
|
61
|
+
# or when it is a synthetics trace, just the trace id.
|
|
62
|
+
# DEV: `Fetcher#id` will not return 0
|
|
63
|
+
return unless (trace_id && parent_id) || (origin && trace_id)
|
|
64
|
+
|
|
65
|
+
trace_distributed_tags = extract_tags(fetcher)
|
|
66
|
+
|
|
67
|
+
TraceDigest.new(
|
|
68
|
+
span_id: parent_id,
|
|
69
|
+
trace_id: trace_id,
|
|
70
|
+
trace_origin: origin,
|
|
71
|
+
trace_sampling_priority: sampling_priority,
|
|
72
|
+
trace_distributed_tags: trace_distributed_tags,
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
# Export trace distributed tags through the `x-datadog-tags` key.
|
|
79
|
+
#
|
|
80
|
+
# DEV: This method accesses global state (the active trace) to record its error state as a trace tag.
|
|
81
|
+
# DEV: This means errors cannot be reported if there's not active span.
|
|
82
|
+
# DEV: Ideally, we'd have a dedicated error reporting stream for all of ddtrace.
|
|
83
|
+
def inject_tags(digest, data)
|
|
84
|
+
return if digest.trace_distributed_tags.nil? || digest.trace_distributed_tags.empty?
|
|
85
|
+
return set_tags_propagation_error(reason: 'disabled') if tags_disabled?
|
|
86
|
+
|
|
87
|
+
tags = DatadogTagsCodec.encode(digest.trace_distributed_tags)
|
|
88
|
+
|
|
89
|
+
return set_tags_propagation_error(reason: 'inject_max_size') if tags_too_large?(tags.size, scenario: 'inject')
|
|
90
|
+
|
|
91
|
+
data[@tags_key] = tags
|
|
92
|
+
rescue => e
|
|
93
|
+
set_tags_propagation_error(reason: 'encoding_error')
|
|
94
|
+
::Datadog.logger.warn(
|
|
95
|
+
"Failed to inject x-datadog-tags: #{e.class.name} #{e.message} at #{Array(e.backtrace).first}"
|
|
96
|
+
)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Import `x-datadog-tags` tags as trace distributed tags.
|
|
100
|
+
# Only tags that have the `_dd.p.` prefix are processed.
|
|
101
|
+
#
|
|
102
|
+
# DEV: This method accesses global state (the active trace) to record its error state as a trace tag.
|
|
103
|
+
# DEV: This means errors cannot be reported if there's not active span.
|
|
104
|
+
# DEV: Ideally, we'd have a dedicated error reporting stream for all of ddtrace.
|
|
105
|
+
def extract_tags(fetcher)
|
|
106
|
+
tags = fetcher[@tags_key]
|
|
107
|
+
|
|
108
|
+
return if !tags || tags.empty?
|
|
109
|
+
return set_tags_propagation_error(reason: 'disabled') if tags_disabled?
|
|
110
|
+
return set_tags_propagation_error(reason: 'extract_max_size') if tags_too_large?(tags.size, scenario: 'extract')
|
|
111
|
+
|
|
112
|
+
tags_hash = DatadogTagsCodec.decode(tags)
|
|
113
|
+
# Only extract keys with the expected Datadog prefix
|
|
114
|
+
tags_hash.select! do |key, _|
|
|
115
|
+
key.start_with?(Tracing::Metadata::Ext::Distributed::TAGS_PREFIX) && key != EXCLUDED_TAG
|
|
116
|
+
end
|
|
117
|
+
tags_hash
|
|
118
|
+
rescue => e
|
|
119
|
+
set_tags_propagation_error(reason: 'decoding_error')
|
|
120
|
+
::Datadog.logger.warn(
|
|
121
|
+
"Failed to extract x-datadog-tags: #{e.class.name} #{e.message} at #{Array(e.backtrace).first}"
|
|
122
|
+
)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def set_tags_propagation_error(reason:)
|
|
126
|
+
active_trace = Tracing.active_trace
|
|
127
|
+
active_trace.set_tag('_dd.propagation_error', reason) if active_trace
|
|
128
|
+
nil
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def tags_disabled?
|
|
132
|
+
::Datadog.configuration.tracing.x_datadog_tags_max_length <= 0
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def tags_too_large?(size, scenario:)
|
|
136
|
+
return false if size <= ::Datadog.configuration.tracing.x_datadog_tags_max_length
|
|
137
|
+
|
|
138
|
+
::Datadog.logger.warn(
|
|
139
|
+
"Failed to #{scenario} x-datadog-tags: tags are too large for configured limit (size:#{size} >= " \
|
|
140
|
+
"limit:#{::Datadog.configuration.tracing.x_datadog_tags_max_length}). This limit can be configured " \
|
|
141
|
+
'through the DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH environment variable.'
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
true
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# We want to exclude tags that we don't want to propagate downstream.
|
|
148
|
+
EXCLUDED_TAG = '_dd.p.upstream_services'
|
|
149
|
+
private_constant :EXCLUDED_TAG
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
3
|
+
|
|
4
|
+
require_relative 'helpers'
|
|
5
|
+
|
|
6
|
+
module Datadog
|
|
7
|
+
module Tracing
|
|
8
|
+
module Distributed
|
|
9
|
+
# Common fetcher that retrieves fields from a Hash data input
|
|
10
|
+
class Fetcher
|
|
11
|
+
# @param data [Hash]
|
|
12
|
+
def initialize(data)
|
|
13
|
+
@data = data
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def [](key)
|
|
17
|
+
@data[key]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def id(key, base: 10)
|
|
21
|
+
Helpers.value_to_id(self[key], base)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def number(key, base: 10)
|
|
25
|
+
Helpers.value_to_number(self[key], base)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -1,31 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
# typed: true
|
|
2
3
|
|
|
3
4
|
module Datadog
|
|
4
5
|
module Tracing
|
|
5
6
|
module Distributed
|
|
6
7
|
module Headers
|
|
7
|
-
#
|
|
8
|
-
#
|
|
8
|
+
# DEV-2.0: This module only exists for backwards compatibility with the public API. It should be removed.
|
|
9
|
+
# @deprecated use [Datadog::Tracing::Distributed::Ext]
|
|
9
10
|
# @public_api
|
|
10
11
|
module Ext
|
|
11
|
-
HTTP_HEADER_TRACE_ID = 'x-datadog-trace-id'
|
|
12
|
-
HTTP_HEADER_PARENT_ID = 'x-datadog-parent-id'
|
|
13
|
-
HTTP_HEADER_SAMPLING_PRIORITY = 'x-datadog-sampling-priority'
|
|
14
|
-
HTTP_HEADER_ORIGIN = 'x-datadog-origin'
|
|
12
|
+
HTTP_HEADER_TRACE_ID = 'x-datadog-trace-id'
|
|
13
|
+
HTTP_HEADER_PARENT_ID = 'x-datadog-parent-id'
|
|
14
|
+
HTTP_HEADER_SAMPLING_PRIORITY = 'x-datadog-sampling-priority'
|
|
15
|
+
HTTP_HEADER_ORIGIN = 'x-datadog-origin'
|
|
15
16
|
# Distributed trace-level tags
|
|
16
|
-
HTTP_HEADER_TAGS = 'x-datadog-tags'
|
|
17
|
+
HTTP_HEADER_TAGS = 'x-datadog-tags'
|
|
17
18
|
|
|
18
|
-
# B3
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
# B3 keys used for distributed tracing.
|
|
20
|
+
# @see https://github.com/openzipkin/b3-propagation
|
|
21
|
+
B3_HEADER_TRACE_ID = 'x-b3-traceid'
|
|
22
|
+
B3_HEADER_SPAN_ID = 'x-b3-spanid'
|
|
23
|
+
B3_HEADER_SAMPLED = 'x-b3-sampled'
|
|
24
|
+
B3_HEADER_SINGLE = 'b3'
|
|
23
25
|
|
|
24
26
|
# gRPC metadata keys for distributed tracing. https://github.com/grpc/grpc-go/blob/v1.10.x/Documentation/grpc-metadata.md
|
|
25
|
-
GRPC_METADATA_TRACE_ID = 'x-datadog-trace-id'
|
|
26
|
-
GRPC_METADATA_PARENT_ID = 'x-datadog-parent-id'
|
|
27
|
-
GRPC_METADATA_SAMPLING_PRIORITY = 'x-datadog-sampling-priority'
|
|
28
|
-
GRPC_METADATA_ORIGIN = 'x-datadog-origin'
|
|
27
|
+
GRPC_METADATA_TRACE_ID = 'x-datadog-trace-id'
|
|
28
|
+
GRPC_METADATA_PARENT_ID = 'x-datadog-parent-id'
|
|
29
|
+
GRPC_METADATA_SAMPLING_PRIORITY = 'x-datadog-sampling-priority'
|
|
30
|
+
GRPC_METADATA_ORIGIN = 'x-datadog-origin'
|
|
29
31
|
end
|
|
30
32
|
end
|
|
31
33
|
end
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
# typed: true
|
|
2
3
|
|
|
3
|
-
require_relative '../../core/configuration'
|
|
4
4
|
require_relative '../sampling/ext'
|
|
5
5
|
|
|
6
6
|
module Datadog
|
|
7
7
|
module Tracing
|
|
8
8
|
module Distributed
|
|
9
|
-
# Helpers module provides common helper functions for distributed tracing
|
|
9
|
+
# Helpers module provides common helper functions for distributed tracing data
|
|
10
10
|
module Helpers
|
|
11
|
-
# Base provides common methods for distributed
|
|
11
|
+
# Base provides common methods for distributed helper classes
|
|
12
12
|
def self.clamp_sampling_priority(sampling_priority)
|
|
13
13
|
# B3 doesn't have our -1 (USER_REJECT) and 2 (USER_KEEP) priorities so convert to acceptable 0/1
|
|
14
14
|
if sampling_priority < 0
|
|
@@ -39,6 +39,7 @@ module Datadog
|
|
|
39
39
|
value.sub(/^0*(?=(0$)|[^0])/, '')
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
# DEV: move `base` to a keyword argument
|
|
42
43
|
def self.value_to_id(value, base = 10)
|
|
43
44
|
id = value_to_number(value, base)
|
|
44
45
|
|
|
@@ -51,9 +52,9 @@ module Datadog
|
|
|
51
52
|
id < 0 ? id + (2**64) : id
|
|
52
53
|
end
|
|
53
54
|
|
|
55
|
+
# DEV: move `base` to a keyword argument
|
|
54
56
|
def self.value_to_number(value, base = 10)
|
|
55
|
-
# It's important to make a difference between no
|
|
56
|
-
# and a header defined to zero.
|
|
57
|
+
# It's important to make a difference between no data and zero.
|
|
57
58
|
return if value.nil?
|
|
58
59
|
|
|
59
60
|
# Be sure we have a string
|
|
@@ -62,7 +63,7 @@ module Datadog
|
|
|
62
63
|
# If we are parsing base16 number then truncate to 64-bit
|
|
63
64
|
value = Helpers.truncate_base16_number(value) if base == 16
|
|
64
65
|
|
|
65
|
-
# Convert
|
|
66
|
+
# Convert value to an integer
|
|
66
67
|
# DEV: Ruby `.to_i` will return `0` if a number could not be parsed
|
|
67
68
|
num = value.to_i(base)
|
|
68
69
|
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: false
|
|
3
|
+
|
|
4
|
+
require_relative '../configuration/ext'
|
|
5
|
+
require_relative '../trace_digest'
|
|
6
|
+
require_relative '../trace_operation'
|
|
7
|
+
|
|
8
|
+
module Datadog
|
|
9
|
+
module Tracing
|
|
10
|
+
module Distributed
|
|
11
|
+
# Provides extraction and injection of distributed trace data.
|
|
12
|
+
class Propagation
|
|
13
|
+
# DEV: This class should receive the value for
|
|
14
|
+
# DEV: `Datadog.configuration.tracing.distributed_tracing.propagation_inject_style`
|
|
15
|
+
# DEV: at initialization time, instead of constantly reading global values.
|
|
16
|
+
# DEV: This means this class should be reconfigured on `Datadog.configure` calls, thus
|
|
17
|
+
# DEV: singleton instances should not used as they will become stale.
|
|
18
|
+
#
|
|
19
|
+
# @param propagation_styles [Hash<String,Object>]
|
|
20
|
+
def initialize(propagation_styles:)
|
|
21
|
+
@propagation_styles = propagation_styles
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# inject! populates the env with span ID, trace ID and sampling priority
|
|
25
|
+
#
|
|
26
|
+
# This method will never raise errors, but instead log them to `Datadog.logger`.
|
|
27
|
+
#
|
|
28
|
+
# DEV-2.0: inject! should work without arguments, injecting the active_trace's digest
|
|
29
|
+
# DEV-2.0: and returning a new Hash with the injected data.
|
|
30
|
+
# DEV-2.0: inject! should also accept either a `trace` or a `digest`, as a `trace`
|
|
31
|
+
# DEV-2.0: argument is the common use case, but also allows us to set error tags in the `trace`
|
|
32
|
+
# DEV-2.0: if needed.
|
|
33
|
+
# DEV-2.0: Ideally, we'd have a separate stream to report tracer errors and never
|
|
34
|
+
# DEV-2.0: touch the active span.
|
|
35
|
+
#
|
|
36
|
+
# @param digest [TraceDigest]
|
|
37
|
+
# @param data [Hash]
|
|
38
|
+
# @return [Boolean] `true` if injected successfully, `false` if no propagation style is configured
|
|
39
|
+
# @return [nil] in case of error, see `Datadog.logger` output for details.
|
|
40
|
+
def inject!(digest, data)
|
|
41
|
+
if digest.nil?
|
|
42
|
+
::Datadog.logger.debug('Cannot inject distributed trace data: digest is nil.')
|
|
43
|
+
return nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
digest = digest.to_digest if digest.respond_to?(:to_digest)
|
|
47
|
+
|
|
48
|
+
result = false
|
|
49
|
+
|
|
50
|
+
# Inject all configured propagation styles
|
|
51
|
+
::Datadog.configuration.tracing.distributed_tracing.propagation_inject_style.each do |style|
|
|
52
|
+
propagator = @propagation_styles[style]
|
|
53
|
+
begin
|
|
54
|
+
if propagator
|
|
55
|
+
propagator.inject!(digest, data)
|
|
56
|
+
result = true
|
|
57
|
+
end
|
|
58
|
+
rescue => e
|
|
59
|
+
result = nil
|
|
60
|
+
::Datadog.logger.error(
|
|
61
|
+
"Error injecting distributed trace data. Cause: #{e} Location: #{Array(e.backtrace).first}"
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
result
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# extract returns {TraceDigest} containing the distributed trace information.
|
|
70
|
+
# sampling priority defined in data.
|
|
71
|
+
#
|
|
72
|
+
# This method will never raise errors, but instead log them to `Datadog.logger`.
|
|
73
|
+
#
|
|
74
|
+
# @param data [Hash]
|
|
75
|
+
def extract(data)
|
|
76
|
+
trace_digest = nil
|
|
77
|
+
dd_trace_digest = nil
|
|
78
|
+
|
|
79
|
+
::Datadog.configuration.tracing.distributed_tracing.propagation_extract_style.each do |style|
|
|
80
|
+
propagator = @propagation_styles[style]
|
|
81
|
+
next if propagator.nil?
|
|
82
|
+
|
|
83
|
+
begin
|
|
84
|
+
extracted_trace_digest = propagator.extract(data)
|
|
85
|
+
rescue => e
|
|
86
|
+
::Datadog.logger.error(
|
|
87
|
+
'Error extracting distributed trace data. ' \
|
|
88
|
+
"Cause: #{e} Location: #{Array(e.backtrace).first}"
|
|
89
|
+
)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Skip if no valid data was found
|
|
93
|
+
next if extracted_trace_digest.nil?
|
|
94
|
+
|
|
95
|
+
# Keep track of the Datadog extracted digest, we want to return it if we have it.
|
|
96
|
+
if extracted_trace_digest && style == Tracing::Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG
|
|
97
|
+
dd_trace_digest = extracted_trace_digest
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# No previously extracted trace data, use the one we just extracted
|
|
101
|
+
if trace_digest.nil?
|
|
102
|
+
trace_digest = extracted_trace_digest
|
|
103
|
+
else
|
|
104
|
+
unless trace_digest.trace_id == extracted_trace_digest.trace_id \
|
|
105
|
+
&& trace_digest.span_id == extracted_trace_digest.span_id
|
|
106
|
+
# We have two mismatched propagation contexts
|
|
107
|
+
::Datadog.logger.debug do
|
|
108
|
+
'Cannot extract distributed trace data: extracted styles differ, ' \
|
|
109
|
+
"#{trace_digest.trace_id} != #{extracted_trace_digest.trace_id} && " \
|
|
110
|
+
"#{trace_digest.span_id} != #{extracted_trace_digest.span_id}"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# It's safer to create a new context than to attach ourselves to the wrong context
|
|
114
|
+
return TraceDigest.new # Early return from the whole method
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Return the extracted trace digest if we found one or else a new empty digest.
|
|
120
|
+
# Always return the Datadog trace digest if one exists since it has more
|
|
121
|
+
# information than the B3 digest, e.g. origin, priority sampling values.
|
|
122
|
+
dd_trace_digest || trace_digest || nil
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -1,117 +1,14 @@
|
|
|
1
1
|
# typed: false
|
|
2
2
|
|
|
3
|
-
require_relative '
|
|
4
|
-
|
|
5
|
-
require_relative '../configuration/ext'
|
|
6
|
-
require_relative '../sampling/ext'
|
|
7
|
-
require_relative '../distributed/headers/b3'
|
|
8
|
-
require_relative '../distributed/headers/b3_single'
|
|
9
|
-
require_relative '../distributed/headers/datadog'
|
|
10
|
-
|
|
11
|
-
require_relative '../trace_digest'
|
|
12
|
-
require_relative '../trace_operation'
|
|
3
|
+
require_relative '../contrib/http/distributed/propagation'
|
|
13
4
|
|
|
14
5
|
module Datadog
|
|
15
6
|
module Tracing
|
|
16
7
|
module Propagation
|
|
17
8
|
# Propagation::HTTP helps extracting and injecting HTTP headers.
|
|
9
|
+
# DEV-2.0: This file has been moved to Contrib. Should be deleted in the next release.
|
|
18
10
|
# @public_api
|
|
19
|
-
|
|
20
|
-
PROPAGATION_STYLES = {
|
|
21
|
-
Configuration::Ext::Distributed::PROPAGATION_STYLE_B3 => Distributed::Headers::B3,
|
|
22
|
-
Configuration::Ext::Distributed::PROPAGATION_STYLE_B3_SINGLE_HEADER => Distributed::Headers::B3Single,
|
|
23
|
-
Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG => Distributed::Headers::Datadog
|
|
24
|
-
}.freeze
|
|
25
|
-
|
|
26
|
-
# inject! populates the env with span ID, trace ID and sampling priority
|
|
27
|
-
#
|
|
28
|
-
# DEV-2.0: inject! should work without arguments, injecting the active_trace's digest
|
|
29
|
-
# DEV-2.0: and returning a new Hash with the injected headers.
|
|
30
|
-
# DEV-2.0: inject! should also accept either a `trace` or a `digest`, as a `trace`
|
|
31
|
-
# DEV-2.0: argument is the common use case, but also allows us to set error tags in the `trace`
|
|
32
|
-
# DEV-2.0: if needed.
|
|
33
|
-
# DEV-2.0: Ideally, we'd have a separate stream to report tracer errors and never
|
|
34
|
-
# DEV-2.0: touch the active span.
|
|
35
|
-
def self.inject!(digest, env)
|
|
36
|
-
# Prevent propagation from being attempted if trace headers provided are nil.
|
|
37
|
-
if digest.nil?
|
|
38
|
-
Datadog.logger.debug(
|
|
39
|
-
'Cannot inject trace headers into env to propagate over HTTP: trace headers are nil.'.freeze
|
|
40
|
-
)
|
|
41
|
-
return
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
digest = digest.to_digest if digest.is_a?(TraceOperation)
|
|
45
|
-
|
|
46
|
-
# Inject all configured propagation styles
|
|
47
|
-
Datadog.configuration.tracing.distributed_tracing.propagation_inject_style.each do |style|
|
|
48
|
-
propagator = PROPAGATION_STYLES[style]
|
|
49
|
-
begin
|
|
50
|
-
propagator.inject!(digest, env) unless propagator.nil?
|
|
51
|
-
rescue => e
|
|
52
|
-
Datadog.logger.error(
|
|
53
|
-
'Error injecting propagated trace headers into the environment. ' \
|
|
54
|
-
"Cause: #{e} Location: #{Array(e.backtrace).first}"
|
|
55
|
-
)
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# extract returns trace headers containing the span ID, trace ID and
|
|
61
|
-
# sampling priority defined in env.
|
|
62
|
-
def self.extract(env)
|
|
63
|
-
trace_digest = nil
|
|
64
|
-
dd_trace_digest = nil
|
|
65
|
-
|
|
66
|
-
Datadog.configuration.tracing.distributed_tracing.propagation_extract_style.each do |style|
|
|
67
|
-
propagator = PROPAGATION_STYLES[style]
|
|
68
|
-
next if propagator.nil?
|
|
69
|
-
|
|
70
|
-
# Extract trace headers
|
|
71
|
-
# DEV: `propagator.extract` will return `nil`, where `Propagation::HTTP#extract` will not
|
|
72
|
-
begin
|
|
73
|
-
extracted_trace_digest = propagator.extract(env)
|
|
74
|
-
rescue => e
|
|
75
|
-
Datadog.logger.error(
|
|
76
|
-
'Error extracting propagated trace headers from the environment. ' \
|
|
77
|
-
"Cause: #{e} Location: #{Array(e.backtrace).first}"
|
|
78
|
-
)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# Skip this style if no valid headers were found
|
|
82
|
-
next if extracted_trace_digest.nil?
|
|
83
|
-
|
|
84
|
-
# Keep track of the Datadog extract trace headers, we want to return
|
|
85
|
-
# this one if we have one
|
|
86
|
-
if extracted_trace_digest && style == Configuration::Ext::Distributed::PROPAGATION_STYLE_DATADOG
|
|
87
|
-
dd_trace_digest = extracted_trace_digest
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# No previously extracted trace headers, use the one we just extracted
|
|
91
|
-
if trace_digest.nil?
|
|
92
|
-
trace_digest = extracted_trace_digest
|
|
93
|
-
else
|
|
94
|
-
unless trace_digest.trace_id == extracted_trace_digest.trace_id \
|
|
95
|
-
&& trace_digest.span_id == extracted_trace_digest.span_id
|
|
96
|
-
# Return an empty/new trace headers if we have a mismatch in values extracted
|
|
97
|
-
msg = "#{trace_digest.trace_id} != #{extracted_trace_digest.trace_id} && " \
|
|
98
|
-
"#{trace_digest.span_id} != #{extracted_trace_digest.span_id}"
|
|
99
|
-
Datadog.logger.debug(
|
|
100
|
-
"Cannot extract trace headers from HTTP: extracted trace headers differ, #{msg}"
|
|
101
|
-
)
|
|
102
|
-
# DEV: This will return from `self.extract` not this `each` block
|
|
103
|
-
return TraceDigest.new
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# Return the extracted trace headers if we found one or else a new empty trace headers
|
|
109
|
-
# Always return the Datadog trace headers if one exists since it has more
|
|
110
|
-
# information than the B3 headers e.g. origin, expanded priority
|
|
111
|
-
# sampling values, etc
|
|
112
|
-
dd_trace_digest || trace_digest || nil
|
|
113
|
-
end
|
|
114
|
-
end
|
|
11
|
+
HTTP = Tracing::Contrib::HTTP::Distributed::Propagation.new
|
|
115
12
|
end
|
|
116
13
|
end
|
|
117
14
|
end
|
|
@@ -94,7 +94,7 @@ module Datadog
|
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
def tag_lang!
|
|
97
|
-
return if trace.lang.nil?
|
|
97
|
+
return if trace.lang.nil?
|
|
98
98
|
|
|
99
99
|
root_span.set_tag(
|
|
100
100
|
Core::Runtime::Ext::TAG_LANG,
|
|
@@ -114,10 +114,7 @@ module Datadog
|
|
|
114
114
|
def tag_process_id!
|
|
115
115
|
return unless trace.process_id
|
|
116
116
|
|
|
117
|
-
root_span.set_tag(
|
|
118
|
-
Core::Runtime::Ext::TAG_PID,
|
|
119
|
-
trace.process_id
|
|
120
|
-
)
|
|
117
|
+
root_span.set_tag(Core::Runtime::Ext::TAG_PROCESS_ID, trace.process_id)
|
|
121
118
|
end
|
|
122
119
|
|
|
123
120
|
def tag_rate_limiter_rate!
|