ddtrace 0.15.0.beta1 → 0.15.0.internaltracinfeature1
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 +5 -5
- data/Appraisals +14 -8
- data/CHANGELOG.md +39 -6
- data/Rakefile +7 -0
- data/docs/GettingStarted.md +63 -26
- data/lib/ddtrace.rb +14 -13
- data/lib/ddtrace/buffer.rb +20 -8
- data/lib/ddtrace/context.rb +104 -60
- data/lib/ddtrace/contrib/concurrent_ruby/configuration/settings.rb +13 -0
- data/lib/ddtrace/contrib/concurrent_ruby/context_composite_executor_service.rb +35 -0
- data/lib/ddtrace/contrib/concurrent_ruby/future_patch.rb +23 -0
- data/lib/ddtrace/contrib/concurrent_ruby/integration.rb +28 -0
- data/lib/ddtrace/contrib/concurrent_ruby/patcher.rb +34 -0
- data/lib/ddtrace/contrib/grape/endpoint.rb +1 -0
- data/lib/ddtrace/contrib/graphql/patcher.rb +10 -2
- data/lib/ddtrace/contrib/http/patcher.rb +20 -7
- data/lib/ddtrace/propagation/distributed_headers.rb +8 -6
- data/lib/ddtrace/registry.rb +4 -2
- data/lib/ddtrace/sampler.rb +24 -6
- data/lib/ddtrace/tracer.rb +4 -1
- data/lib/ddtrace/transport.rb +105 -33
- data/lib/ddtrace/utils.rb +1 -0
- data/lib/ddtrace/utils/internal_traces.rb +53 -0
- data/lib/ddtrace/version.rb +1 -1
- metadata +9 -3
data/lib/ddtrace/context.rb
CHANGED
@@ -13,14 +13,16 @@ module Datadog
|
|
13
13
|
# \Context, it will be related to the original trace.
|
14
14
|
#
|
15
15
|
# This data structure is thread-safe.
|
16
|
+
# rubocop:disable Metrics/ClassLength
|
16
17
|
class Context
|
17
18
|
# 100k spans is about a 100Mb footprint
|
18
19
|
DEFAULT_MAX_LENGTH = 100_000
|
19
20
|
|
20
21
|
attr_reader :max_length
|
21
22
|
|
22
|
-
# Initialize a new \Context.
|
23
|
+
# Initialize a new thread-safe \Context.
|
23
24
|
def initialize(options = {})
|
25
|
+
@mutex = Mutex.new
|
24
26
|
# max_length is the amount of spans above which, for a given trace,
|
25
27
|
# the context will simply drop and ignore spans, avoiding high memory usage.
|
26
28
|
@max_length = options.fetch(:max_length, DEFAULT_MAX_LENGTH)
|
@@ -28,56 +30,82 @@ module Datadog
|
|
28
30
|
end
|
29
31
|
|
30
32
|
def trace_id
|
31
|
-
@
|
33
|
+
@mutex.synchronize do
|
34
|
+
@parent_trace_id
|
35
|
+
end
|
32
36
|
end
|
33
37
|
|
34
38
|
def span_id
|
35
|
-
@
|
39
|
+
@mutex.synchronize do
|
40
|
+
@parent_span_id
|
41
|
+
end
|
36
42
|
end
|
37
43
|
|
38
|
-
|
44
|
+
def sampling_priority
|
45
|
+
@mutex.synchronize do
|
46
|
+
@sampling_priority
|
47
|
+
end
|
48
|
+
end
|
39
49
|
|
40
|
-
|
50
|
+
def sampling_priority=(priority)
|
51
|
+
@mutex.synchronize do
|
52
|
+
@sampling_priority = priority
|
53
|
+
end
|
54
|
+
end
|
41
55
|
|
42
56
|
# Return the last active span that corresponds to the last inserted
|
43
57
|
# item in the trace list. This cannot be considered as the current active
|
44
58
|
# span in asynchronous environments, because some spans can be closed
|
45
59
|
# earlier while child spans still need to finish their traced execution.
|
46
|
-
|
60
|
+
def current_span
|
61
|
+
@mutex.synchronize do
|
62
|
+
return @current_span
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def current_root_span
|
67
|
+
@mutex.synchronize do
|
68
|
+
return @current_root_span
|
69
|
+
end
|
70
|
+
end
|
47
71
|
|
48
72
|
# Add a span to the context trace list, keeping it as the last active span.
|
49
73
|
def add_span(span)
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
74
|
+
@mutex.synchronize do
|
75
|
+
# If hitting the hard limit, just drop spans. This is really a rare case
|
76
|
+
# as it means despite the soft limit, the hard limit is reached, so the trace
|
77
|
+
# by default has 10000 spans, all of which belong to unfinished parts of a
|
78
|
+
# larger trace. This is a catch-all to reduce global memory usage.
|
79
|
+
if @max_length > 0 && @trace.length >= @max_length
|
80
|
+
Datadog::Tracer.log.debug("context full, ignoring span #{span.name}")
|
81
|
+
# Detach the span from any context, it's being dropped and ignored.
|
82
|
+
span.context = nil
|
83
|
+
return
|
84
|
+
end
|
85
|
+
set_current_span(span)
|
86
|
+
@current_root_span = span if @trace.empty?
|
87
|
+
@trace << span
|
88
|
+
span.context = self
|
89
|
+
end
|
64
90
|
end
|
65
91
|
|
66
92
|
# Mark a span as a finished, increasing the internal counter to prevent
|
67
93
|
# cycles inside _trace list.
|
68
94
|
def close_span(span)
|
69
|
-
@
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
95
|
+
@mutex.synchronize do
|
96
|
+
@finished_spans += 1
|
97
|
+
# Current span is only meaningful for linear tree-like traces,
|
98
|
+
# in other cases, this is just broken and one should rely
|
99
|
+
# on per-instrumentation code to retrieve handle parent/child relations.
|
100
|
+
set_current_span(span.parent)
|
101
|
+
return if span.tracer.nil?
|
102
|
+
return unless Datadog::Tracer.debug_logging
|
103
|
+
if span.parent.nil? && !check_finished_spans
|
104
|
+
opened_spans = @trace.length - @finished_spans
|
105
|
+
Datadog::Tracer.log.debug("root span #{span.name} closed but has #{opened_spans} unfinished spans:")
|
106
|
+
@trace.each do |s|
|
107
|
+
Datadog::Tracer.log.debug("unfinished span: #{s}") unless s.finished?
|
108
|
+
end
|
81
109
|
end
|
82
110
|
end
|
83
111
|
end
|
@@ -85,13 +113,17 @@ module Datadog
|
|
85
113
|
# Returns if the trace for the current Context is finished or not. A \Context
|
86
114
|
# is considered finished if all spans in this context are finished.
|
87
115
|
def finished?
|
88
|
-
|
116
|
+
@mutex.synchronize do
|
117
|
+
return check_finished_spans
|
118
|
+
end
|
89
119
|
end
|
90
120
|
|
91
121
|
# Returns true if the context is sampled, that is, if it should be kept
|
92
122
|
# and sent to the trace agent.
|
93
123
|
def sampled?
|
94
|
-
@
|
124
|
+
@mutex.synchronize do
|
125
|
+
return @sampled
|
126
|
+
end
|
95
127
|
end
|
96
128
|
|
97
129
|
# Returns both the trace list generated in the current context and
|
@@ -101,22 +133,26 @@ module Datadog
|
|
101
133
|
#
|
102
134
|
# This operation is thread-safe.
|
103
135
|
def get
|
104
|
-
|
105
|
-
|
136
|
+
@mutex.synchronize do
|
137
|
+
trace = @trace
|
138
|
+
sampled = @sampled
|
106
139
|
|
107
|
-
|
140
|
+
attach_sampling_priority if sampled && @sampling_priority
|
108
141
|
|
109
|
-
|
110
|
-
|
142
|
+
# still return sampled attribute, even if context is not finished
|
143
|
+
return nil, sampled unless check_finished_spans()
|
111
144
|
|
112
|
-
|
113
|
-
|
145
|
+
reset
|
146
|
+
[trace, sampled]
|
147
|
+
end
|
114
148
|
end
|
115
149
|
|
116
150
|
# Return a string representation of the context.
|
117
151
|
def to_s
|
118
|
-
|
119
|
-
|
152
|
+
@mutex.synchronize do
|
153
|
+
# rubocop:disable Metrics/LineLength
|
154
|
+
"Context(trace.length:#{@trace.length},sampled:#{@sampled},finished_spans:#{@finished_spans},current_span:#{@current_span})"
|
155
|
+
end
|
120
156
|
end
|
121
157
|
|
122
158
|
private
|
@@ -158,37 +194,45 @@ module Datadog
|
|
158
194
|
|
159
195
|
# Return the start time of the root span, or nil if there are no spans or this is undefined.
|
160
196
|
def start_time
|
161
|
-
|
162
|
-
|
197
|
+
@mutex.synchronize do
|
198
|
+
return nil if @trace.empty?
|
199
|
+
@trace[0].start_time
|
200
|
+
end
|
163
201
|
end
|
164
202
|
|
165
203
|
# Return the length of the current trace held by this context.
|
166
204
|
def length
|
167
|
-
@
|
205
|
+
@mutex.synchronize do
|
206
|
+
@trace.length
|
207
|
+
end
|
168
208
|
end
|
169
209
|
|
170
210
|
# Iterate on each span within the trace. This is thread safe.
|
171
211
|
def each_span
|
172
|
-
@
|
173
|
-
|
212
|
+
@mutex.synchronize do
|
213
|
+
@trace.each do |span|
|
214
|
+
yield span
|
215
|
+
end
|
174
216
|
end
|
175
217
|
end
|
176
218
|
|
177
219
|
# Delete any span matching the condition. This is thread safe.
|
178
220
|
def delete_span_if
|
179
|
-
@
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
221
|
+
@mutex.synchronize do
|
222
|
+
@trace.delete_if do |span|
|
223
|
+
finished = span.finished?
|
224
|
+
delete_span = yield span
|
225
|
+
if delete_span
|
226
|
+
# We need to detach the span from the context, else, some code
|
227
|
+
# finishing it afterwards would mess up with the number of
|
228
|
+
# finished_spans and possibly cause other side effects.
|
229
|
+
span.context = nil
|
230
|
+
# Acknowledge there's one span less to finish, if needed.
|
231
|
+
# It's very important to keep this balanced.
|
232
|
+
@finished_spans -= 1 if finished
|
233
|
+
end
|
234
|
+
delete_span
|
190
235
|
end
|
191
|
-
delete_span
|
192
236
|
end
|
193
237
|
end
|
194
238
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'ddtrace/contrib/configuration/settings'
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module Contrib
|
5
|
+
module ConcurrentRuby
|
6
|
+
module Configuration
|
7
|
+
class Settings < Contrib::Configuration::Settings
|
8
|
+
# Add any custom ConcurrentRuby configuration or behavior here.
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'concurrent/executor/executor_service'
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module Contrib
|
5
|
+
module ConcurrentRuby
|
6
|
+
# wraps existing executor to carry over trace context
|
7
|
+
class ContextCompositeExecutorService
|
8
|
+
extend Forwardable
|
9
|
+
include Concurrent::ExecutorService
|
10
|
+
|
11
|
+
attr_accessor :composited_executor
|
12
|
+
|
13
|
+
def initialize(composited_executor)
|
14
|
+
@composited_executor = composited_executor
|
15
|
+
end
|
16
|
+
|
17
|
+
# post method runs the task within composited executor - in a different thread
|
18
|
+
def post(*args, &task)
|
19
|
+
context = datadog_configuration.tracer.provider.context
|
20
|
+
|
21
|
+
@composited_executor.post(*args) do
|
22
|
+
datadog_configuration.tracer.provider.context = context
|
23
|
+
yield
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def datadog_configuration
|
28
|
+
Datadog.configuration[:concurrent_ruby]
|
29
|
+
end
|
30
|
+
|
31
|
+
delegate [:can_overflow?, :serialized?] => :composited_executor
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'ddtrace/contrib/concurrent_ruby/context_composite_executor_service'
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module Contrib
|
5
|
+
module ConcurrentRuby
|
6
|
+
# This patches the Future - to wrap executor service using ContextCompositeExecutorService
|
7
|
+
module FuturePatch
|
8
|
+
def self.included(base)
|
9
|
+
base.class_eval do
|
10
|
+
alias_method :ns_initialize_without_datadog, :ns_initialize
|
11
|
+
remove_method(:ns_initialize)
|
12
|
+
|
13
|
+
def ns_initialize(value, opts)
|
14
|
+
ns_initialize_without_datadog(value, opts)
|
15
|
+
|
16
|
+
@executor = ContextCompositeExecutorService.new(@executor)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'ddtrace/contrib/integration'
|
2
|
+
require 'ddtrace/contrib/concurrent_ruby/patcher'
|
3
|
+
require 'ddtrace/contrib/concurrent_ruby/configuration/settings'
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module Contrib
|
7
|
+
module ConcurrentRuby
|
8
|
+
# Propagate Tracing context in Concurrent::Future
|
9
|
+
class Integration
|
10
|
+
include Contrib::Integration
|
11
|
+
|
12
|
+
register_as :concurrent_ruby
|
13
|
+
|
14
|
+
def self.compatible?
|
15
|
+
defined?(::Concurrent::Future)
|
16
|
+
end
|
17
|
+
|
18
|
+
def default_configuration
|
19
|
+
Configuration::Settings.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def patcher
|
23
|
+
Patcher
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'ddtrace/contrib/patcher'
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module Contrib
|
5
|
+
module ConcurrentRuby
|
6
|
+
# Patcher enables patching of 'Future' class.
|
7
|
+
module Patcher
|
8
|
+
include Contrib::Patcher
|
9
|
+
|
10
|
+
module_function
|
11
|
+
|
12
|
+
def patched?
|
13
|
+
done?(:concurrent_ruby)
|
14
|
+
end
|
15
|
+
|
16
|
+
def patch
|
17
|
+
do_once(:concurrent_ruby) do
|
18
|
+
begin
|
19
|
+
require 'ddtrace/contrib/concurrent_ruby/future_patch'
|
20
|
+
|
21
|
+
patch_future
|
22
|
+
rescue StandardError => e
|
23
|
+
Datadog::Tracer.log.error("Unable to apply Future integration: #{e}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def patch_future
|
29
|
+
::Concurrent::Future.send(:include, FuturePatch)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -29,12 +29,20 @@ module Datadog
|
|
29
29
|
tracer = get_option(:tracer)
|
30
30
|
service_name = get_option(:service_name)
|
31
31
|
|
32
|
-
schema.
|
33
|
-
use(
|
32
|
+
if schema.respond_to?(:use)
|
33
|
+
schema.use(
|
34
34
|
::GraphQL::Tracing::DataDogTracing,
|
35
35
|
tracer: tracer,
|
36
36
|
service: service_name
|
37
37
|
)
|
38
|
+
else
|
39
|
+
schema.define do
|
40
|
+
use(
|
41
|
+
::GraphQL::Tracing::DataDogTracing,
|
42
|
+
tracer: tracer,
|
43
|
+
service: service_name
|
44
|
+
)
|
45
|
+
end
|
38
46
|
end
|
39
47
|
end
|
40
48
|
|
@@ -14,27 +14,37 @@ module Datadog
|
|
14
14
|
|
15
15
|
module_function
|
16
16
|
|
17
|
-
def
|
17
|
+
def tracing_transport?(req, address, port, transport)
|
18
18
|
# we don't want to trace our own call to the API (they use net/http)
|
19
19
|
# when we know the host & port (from the URI) we use it, else (most-likely
|
20
20
|
# called with a block) rely on the URL at the end.
|
21
21
|
if req.respond_to?(:uri) && req.uri
|
22
22
|
if req.uri.host.to_s == transport.hostname.to_s &&
|
23
23
|
req.uri.port.to_i == transport.port.to_i
|
24
|
-
|
24
|
+
true
|
25
25
|
end
|
26
26
|
elsif address && port &&
|
27
27
|
address.to_s == transport.hostname.to_s &&
|
28
28
|
port.to_i == transport.port.to_i
|
29
|
-
|
29
|
+
true
|
30
30
|
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def tracing_recursive_request?(tracer)
|
31
34
|
# we don't want a "shotgun" effect with two nested traces for one
|
32
35
|
# logical get, and request is likely to call itself recursively
|
33
|
-
active =
|
36
|
+
active = tracer.active_span
|
34
37
|
return true if active && (active.name == NAME)
|
35
38
|
false
|
36
39
|
end
|
37
40
|
|
41
|
+
# @deprecated This method will be removed in 1.0
|
42
|
+
def should_skip_tracing?(req, address, port, transport, pin)
|
43
|
+
Datadog::Tracer.log.error('should_skip_tracing? will be deprecated in 1.0')
|
44
|
+
|
45
|
+
tracing_transport?(req, address, port, transport) || tracing_recursive_request?(pin.tracer)
|
46
|
+
end
|
47
|
+
|
38
48
|
def should_skip_distributed_tracing?(pin)
|
39
49
|
if pin.config && pin.config.key?(:distributed_tracing)
|
40
50
|
return !pin.config[:distributed_tracing]
|
@@ -99,12 +109,15 @@ module Datadog
|
|
99
109
|
end
|
100
110
|
|
101
111
|
def request(req, body = nil, &block) # :yield: +response+
|
112
|
+
transport = Datadog.configuration[:http][:tracer].writer.transport
|
113
|
+
if Datadog::Contrib::HTTP.tracing_transport?(req, @address, @port, transport)
|
114
|
+
return request_without_datadog(req, body, &block)
|
115
|
+
end
|
116
|
+
|
102
117
|
pin = datadog_pin
|
103
118
|
return request_without_datadog(req, body, &block) unless pin && pin.tracer
|
104
119
|
|
105
|
-
|
106
|
-
|
107
|
-
if Datadog::Contrib::HTTP.should_skip_tracing?(req, @address, @port, transport, pin)
|
120
|
+
if Datadog::Contrib::HTTP.tracing_recursive_request?(pin.tracer)
|
108
121
|
return request_without_datadog(req, body, &block)
|
109
122
|
end
|
110
123
|
|