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.
@@ -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