ddtrace 0.15.0.beta1 → 0.15.0.internaltracinfeature1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|