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.
@@ -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
- @parent_trace_id
33
+ @mutex.synchronize do
34
+ @parent_trace_id
35
+ end
32
36
  end
33
37
 
34
38
  def span_id
35
- @parent_span_id
39
+ @mutex.synchronize do
40
+ @parent_span_id
41
+ end
36
42
  end
37
43
 
38
- attr_reader :sampling_priority
44
+ def sampling_priority
45
+ @mutex.synchronize do
46
+ @sampling_priority
47
+ end
48
+ end
39
49
 
40
- attr_writer :sampling_priority
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
- attr_reader :current_span, :current_root_span
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
- # If hitting the hard limit, just drop spans. This is really a rare case
51
- # as it means despite the soft limit, the hard limit is reached, so the trace
52
- # by default has 10000 spans, all of which belong to unfinished parts of a
53
- # larger trace. This is a catch-all to reduce global memory usage.
54
- if @max_length > 0 && @trace.length >= @max_length
55
- Datadog::Tracer.log.debug("context full, ignoring span #{span.name}")
56
- # Detach the span from any context, it's being dropped and ignored.
57
- span.context = nil
58
- return
59
- end
60
- set_current_span(span)
61
- @current_root_span = span if @trace.empty?
62
- @trace << span
63
- span.context = self
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
- @finished_spans += 1
70
- # Current span is only meaningful for linear tree-like traces,
71
- # in other cases, this is just broken and one should rely
72
- # on per-instrumentation code to retrieve handle parent/child relations.
73
- set_current_span(span.parent)
74
- return if span.tracer.nil?
75
- return unless Datadog::Tracer.debug_logging
76
- if span.parent.nil? && !check_finished_spans
77
- opened_spans = @trace.length - @finished_spans
78
- Datadog::Tracer.log.debug("root span #{span.name} closed but has #{opened_spans} unfinished spans:")
79
- @trace.each do |s|
80
- Datadog::Tracer.log.debug("unfinished span: #{s}") unless s.finished?
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
- check_finished_spans
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
- @sampled
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
- trace = @trace
105
- sampled = @sampled
136
+ @mutex.synchronize do
137
+ trace = @trace
138
+ sampled = @sampled
106
139
 
107
- attach_sampling_priority if sampled && @sampling_priority
140
+ attach_sampling_priority if sampled && @sampling_priority
108
141
 
109
- # still return sampled attribute, even if context is not finished
110
- return nil, sampled unless check_finished_spans()
142
+ # still return sampled attribute, even if context is not finished
143
+ return nil, sampled unless check_finished_spans()
111
144
 
112
- reset
113
- [trace, sampled]
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
- # rubocop:disable Metrics/LineLength
119
- "Context(trace.length:#{@trace.length},sampled:#{@sampled},finished_spans:#{@finished_spans},current_span:#{@current_span})"
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
- return nil if @trace.empty?
162
- @trace[0].start_time
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
- @trace.length
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
- @trace.each do |span|
173
- yield span
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
- @trace.delete_if do |span|
180
- finished = span.finished?
181
- delete_span = yield span
182
- if delete_span
183
- # We need to detach the span from the context, else, some code
184
- # finishing it afterwards would mess up with the number of
185
- # finished_spans and possibly cause other side effects.
186
- span.context = nil
187
- # Acknowledge there's one span less to finish, if needed.
188
- # It's very important to keep this balanced.
189
- @finished_spans -= 1 if finished
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
@@ -1,5 +1,6 @@
1
1
  require 'ddtrace/ext/http'
2
2
  require 'ddtrace/ext/errors'
3
+ require 'ddtrace/contrib/rack/middlewares'
3
4
 
4
5
  module Datadog
5
6
  module Contrib
@@ -29,12 +29,20 @@ module Datadog
29
29
  tracer = get_option(:tracer)
30
30
  service_name = get_option(:service_name)
31
31
 
32
- schema.define do
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 should_skip_tracing?(req, address, port, transport, pin)
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
- return true
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
- return true
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 = pin.tracer.active_span()
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
- transport = pin.tracer.writer.transport
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