ddtrace 0.30.1 → 0.31.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 +5 -5
- data/.circleci/config.yml +28 -0
- data/.circleci/images/primary/Dockerfile-2.7.0 +73 -0
- data/Appraisals +111 -2
- data/CHANGELOG.md +33 -1
- data/Rakefile +69 -1
- data/ddtrace.gemspec +1 -0
- data/docker-compose.yml +30 -0
- data/docs/GettingStarted.md +28 -3
- data/lib/ddtrace.rb +6 -0
- data/lib/ddtrace/buffer.rb +3 -3
- data/lib/ddtrace/configuration/base.rb +2 -1
- data/lib/ddtrace/configuration/settings.rb +15 -2
- data/lib/ddtrace/context.rb +62 -57
- data/lib/ddtrace/context_flush.rb +51 -114
- data/lib/ddtrace/context_provider.rb +45 -0
- data/lib/ddtrace/contrib/action_cable/configuration/settings.rb +25 -0
- data/lib/ddtrace/contrib/action_cable/event.rb +65 -0
- data/lib/ddtrace/contrib/action_cable/events.rb +33 -0
- data/lib/ddtrace/contrib/action_cable/events/broadcast.rb +49 -0
- data/lib/ddtrace/contrib/action_cable/events/perform_action.rb +52 -0
- data/lib/ddtrace/contrib/action_cable/events/transmit.rb +50 -0
- data/lib/ddtrace/contrib/action_cable/ext.rb +23 -0
- data/lib/ddtrace/contrib/action_cable/instrumentation.rb +31 -0
- data/lib/ddtrace/contrib/action_cable/integration.rb +36 -0
- data/lib/ddtrace/contrib/action_cable/patcher.rb +27 -0
- data/lib/ddtrace/contrib/action_pack/action_controller/instrumentation.rb +2 -2
- data/lib/ddtrace/contrib/action_view/events/render_partial.rb +5 -3
- data/lib/ddtrace/contrib/action_view/events/render_template.rb +5 -3
- data/lib/ddtrace/contrib/action_view/instrumentation/partial_renderer.rb +2 -1
- data/lib/ddtrace/contrib/action_view/instrumentation/template_renderer.rb +4 -2
- data/lib/ddtrace/contrib/action_view/patcher.rb +1 -1
- data/lib/ddtrace/contrib/active_record/events/instantiation.rb +1 -1
- data/lib/ddtrace/contrib/active_record/events/sql.rb +1 -1
- data/lib/ddtrace/contrib/active_support/cache/instrumentation.rb +2 -2
- data/lib/ddtrace/contrib/active_support/notifications/subscription.rb +2 -2
- data/lib/ddtrace/contrib/dalli/patcher.rb +1 -1
- data/lib/ddtrace/contrib/dalli/quantize.rb +1 -1
- data/lib/ddtrace/contrib/elasticsearch/patcher.rb +1 -1
- data/lib/ddtrace/contrib/excon/middleware.rb +3 -3
- data/lib/ddtrace/contrib/faraday/connection.rb +18 -0
- data/lib/ddtrace/contrib/faraday/integration.rb +1 -1
- data/lib/ddtrace/contrib/faraday/patcher.rb +3 -3
- data/lib/ddtrace/contrib/grape/endpoint.rb +5 -5
- data/lib/ddtrace/contrib/grape/patcher.rb +1 -1
- data/lib/ddtrace/contrib/grpc/datadog_interceptor/client.rb +1 -1
- data/lib/ddtrace/contrib/grpc/datadog_interceptor/server.rb +2 -2
- data/lib/ddtrace/contrib/grpc/patcher.rb +1 -1
- data/lib/ddtrace/contrib/http/instrumentation.rb +1 -1
- data/lib/ddtrace/contrib/mongodb/subscribers.rb +2 -2
- data/lib/ddtrace/contrib/patchable.rb +1 -1
- data/lib/ddtrace/contrib/patcher.rb +1 -1
- data/lib/ddtrace/contrib/rack/middlewares.rb +2 -2
- data/lib/ddtrace/contrib/rack/patcher.rb +2 -2
- data/lib/ddtrace/contrib/rack/request_queue.rb +1 -1
- data/lib/ddtrace/contrib/rails/configuration/settings.rb +1 -0
- data/lib/ddtrace/contrib/rails/framework.rb +12 -0
- data/lib/ddtrace/contrib/rake/instrumentation.rb +2 -2
- data/lib/ddtrace/contrib/redis/quantize.rb +1 -1
- data/lib/ddtrace/contrib/sinatra/tracer.rb +1 -1
- data/lib/ddtrace/ext/forced_tracing.rb +1 -1
- data/lib/ddtrace/ext/sampling.rb +3 -0
- data/lib/ddtrace/logger.rb +42 -0
- data/lib/ddtrace/metrics.rb +5 -5
- data/lib/ddtrace/monkey.rb +1 -1
- data/lib/ddtrace/pin.rb +1 -1
- data/lib/ddtrace/pipeline.rb +1 -1
- data/lib/ddtrace/propagation/http_propagator.rb +2 -2
- data/lib/ddtrace/runtime/cgroup.rb +1 -1
- data/lib/ddtrace/runtime/container.rb +1 -1
- data/lib/ddtrace/runtime/metrics.rb +1 -1
- data/lib/ddtrace/sampler.rb +1 -1
- data/lib/ddtrace/sampling/rule.rb +1 -1
- data/lib/ddtrace/sampling/rule_sampler.rb +4 -4
- data/lib/ddtrace/span.rb +24 -6
- data/lib/ddtrace/sync_writer.rb +4 -3
- data/lib/ddtrace/tracer.rb +37 -77
- data/lib/ddtrace/transport/http/client.rb +2 -2
- data/lib/ddtrace/utils.rb +1 -1
- data/lib/ddtrace/version.rb +2 -2
- data/lib/ddtrace/workers.rb +3 -3
- data/lib/ddtrace/writer.rb +3 -2
- metadata +44 -6
- data/lib/ddtrace/contrib/faraday/rack_builder.rb +0 -18
- data/lib/ddtrace/provider.rb +0 -21
data/docs/GettingStarted.md
CHANGED
@@ -26,6 +26,7 @@ To contribute, check out the [contribution guidelines][contribution docs] and [d
|
|
26
26
|
- [Quickstart for OpenTracing](#quickstart-for-opentracing)
|
27
27
|
- [Manual instrumentation](#manual-instrumentation)
|
28
28
|
- [Integration instrumentation](#integration-instrumentation)
|
29
|
+
- [Action Cable](#action-cable)
|
29
30
|
- [Action View](#action-view)
|
30
31
|
- [Active Model Serializers](#active-model-serializers)
|
31
32
|
- [Action Pack](#action-pack)
|
@@ -80,7 +81,8 @@ To contribute, check out the [contribution guidelines][contribution docs] and [d
|
|
80
81
|
|
81
82
|
| Type | Documentation | Version | Support type | Gem version support |
|
82
83
|
| ----- | -------------------------- | ----- | ------------------------------------ | ------------------- |
|
83
|
-
| MRI | https://www.ruby-lang.org/ | 2.
|
84
|
+
| MRI | https://www.ruby-lang.org/ | 2.7 | Full | Latest |
|
85
|
+
| | | 2.6 | Full | Latest |
|
84
86
|
| | | 2.5 | Full | Latest |
|
85
87
|
| | | 2.4 | Full | Latest |
|
86
88
|
| | | 2.3 | Full | Latest |
|
@@ -324,6 +326,7 @@ For a list of available integrations, and their configuration options, please re
|
|
324
326
|
|
325
327
|
| Name | Key | Versions Supported | How to configure | Gem source |
|
326
328
|
| ------------------------ | -------------------------- | ------------------------ | ----------------------------------- | ------------------------------------------------------------------------------ |
|
329
|
+
| Action Cable | `action_cable` | `>= 5.0` | *[Link](#action-cable)* | *[Link](https://github.com/rails/rails/tree/master/actioncable)* |
|
327
330
|
| Action View | `action_view` | `>= 3.2` | *[Link](#action-view)* | *[Link](https://github.com/rails/rails/tree/master/actionview)* |
|
328
331
|
| Active Model Serializers | `active_model_serializers` | `>= 0.9` | *[Link](#active-model-serializers)* | *[Link](https://github.com/rails-api/active_model_serializers)* |
|
329
332
|
| Action Pack | `action_pack` | `>= 3.2` | *[Link](#action-pack)* | *[Link](https://github.com/rails/rails/tree/master/actionpack)* |
|
@@ -356,6 +359,28 @@ For a list of available integrations, and their configuration options, please re
|
|
356
359
|
| Sinatra | `sinatra` | `>= 1.4.5` | *[Link](#sinatra)* | *[Link](https://github.com/sinatra/sinatra)* |
|
357
360
|
| Sucker Punch | `sucker_punch` | `>= 2.0` | *[Link](#sucker-punch)* | *[Link](https://github.com/brandonhilkert/sucker_punch)* |
|
358
361
|
|
362
|
+
### Action Cable
|
363
|
+
|
364
|
+
The Action Cable integration traces broadcast messages and channel actions.
|
365
|
+
|
366
|
+
You can enable it through `Datadog.configure`:
|
367
|
+
|
368
|
+
```ruby
|
369
|
+
require 'ddtrace'
|
370
|
+
|
371
|
+
Datadog.configure do |c|
|
372
|
+
c.use :action_cable, options
|
373
|
+
end
|
374
|
+
```
|
375
|
+
|
376
|
+
Where `options` is an optional `Hash` that accepts the following parameters:
|
377
|
+
|
378
|
+
| Key | Description | Default |
|
379
|
+
| --- | ----------- | ------- |
|
380
|
+
| `analytics_enabled` | Enable analytics for spans produced by this integration. `true` for on, `nil` to defer to global setting, `false` for off. | `false` |
|
381
|
+
| `service_name` | Service name used for `action_cable` instrumentation | `'action_cable'` |
|
382
|
+
| `tracer` | `Datadog::Tracer` used to perform instrumentation. Usually you don't need to set this. | `Datadog.tracer` |
|
383
|
+
|
359
384
|
### Action View
|
360
385
|
|
361
386
|
Most of the time, Active Support is set up as part of Rails, but it can be activated separately:
|
@@ -1105,7 +1130,7 @@ Where `options` is an optional `Hash` that accepts the following parameters:
|
|
1105
1130
|
| 2.2 - 2.3 | 3.0 - 5.2 |
|
1106
1131
|
| 2.4 | 4.2.8 - 5.2 |
|
1107
1132
|
| 2.5 | 4.2.8 - 6.0 |
|
1108
|
-
| 2.6
|
1133
|
+
| 2.6 - 2.7 | 5.0 - 6.0 |
|
1109
1134
|
|
1110
1135
|
### Rake
|
1111
1136
|
|
@@ -1450,7 +1475,7 @@ Datadog.configure do |c|
|
|
1450
1475
|
c.tracer log: Logger.new(f) # Overriding the default tracer
|
1451
1476
|
end
|
1452
1477
|
|
1453
|
-
Datadog::
|
1478
|
+
Datadog::Logger.log.info { "this is typically called by tracing code" }
|
1454
1479
|
```
|
1455
1480
|
|
1456
1481
|
### Environment and tags
|
data/lib/ddtrace.rb
CHANGED
@@ -26,8 +26,14 @@ module Datadog
|
|
26
26
|
# Load and extend Contrib by default
|
27
27
|
require 'ddtrace/contrib/extensions'
|
28
28
|
extend Contrib::Extensions
|
29
|
+
|
30
|
+
# Add shutdown hook:
|
31
|
+
# Ensures the tracer has an opportunity to flush traces
|
32
|
+
# and cleanup before terminating the process.
|
33
|
+
at_exit { Datadog.tracer.shutdown! }
|
29
34
|
end
|
30
35
|
|
36
|
+
require 'ddtrace/contrib/action_cable/integration'
|
31
37
|
require 'ddtrace/contrib/action_pack/integration'
|
32
38
|
require 'ddtrace/contrib/action_view/integration'
|
33
39
|
require 'ddtrace/contrib/active_model_serializers/integration'
|
data/lib/ddtrace/buffer.rb
CHANGED
@@ -83,7 +83,7 @@ module Datadog
|
|
83
83
|
@buffer_accepted += 1
|
84
84
|
@buffer_accepted_lengths += trace.length
|
85
85
|
rescue StandardError => e
|
86
|
-
Datadog::
|
86
|
+
Datadog::Logger.log.debug("Failed to measure queue accept. Cause: #{e.message} Source: #{e.backtrace.first}")
|
87
87
|
end
|
88
88
|
|
89
89
|
def measure_drop(trace)
|
@@ -91,7 +91,7 @@ module Datadog
|
|
91
91
|
@buffer_spans -= trace.length
|
92
92
|
@buffer_accepted_lengths -= trace.length
|
93
93
|
rescue StandardError => e
|
94
|
-
Datadog::
|
94
|
+
Datadog::Logger.log.debug("Failed to measure queue drop. Cause: #{e.message} Source: #{e.backtrace.first}")
|
95
95
|
end
|
96
96
|
|
97
97
|
def measure_pop(traces)
|
@@ -113,7 +113,7 @@ module Datadog
|
|
113
113
|
@buffer_dropped = 0
|
114
114
|
@buffer_spans = 0
|
115
115
|
rescue StandardError => e
|
116
|
-
Datadog::
|
116
|
+
Datadog::Logger.log.debug("Failed to measure queue. Cause: #{e.message} Source: #{e.backtrace.first}")
|
117
117
|
end
|
118
118
|
end
|
119
119
|
end
|
@@ -3,6 +3,7 @@ require 'ddtrace/configuration/base'
|
|
3
3
|
require 'ddtrace/ext/analytics'
|
4
4
|
require 'ddtrace/ext/distributed'
|
5
5
|
require 'ddtrace/ext/runtime'
|
6
|
+
require 'ddtrace/ext/sampling'
|
6
7
|
|
7
8
|
require 'ddtrace/tracer'
|
8
9
|
require 'ddtrace/metrics'
|
@@ -56,6 +57,18 @@ module Datadog
|
|
56
57
|
end
|
57
58
|
end
|
58
59
|
|
60
|
+
settings :sampling do
|
61
|
+
option :default_rate do |o|
|
62
|
+
o.default { env_to_float(Ext::Sampling::ENV_SAMPLE_RATE, nil) }
|
63
|
+
o.lazy
|
64
|
+
end
|
65
|
+
|
66
|
+
option :rate_limit do |o|
|
67
|
+
o.default { env_to_float(Ext::Sampling::ENV_RATE_LIMIT, 100) }
|
68
|
+
o.lazy
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
59
72
|
settings :diagnostics do
|
60
73
|
option :health_metrics do |o|
|
61
74
|
o.default do
|
@@ -85,10 +98,10 @@ module Datadog
|
|
85
98
|
tracer.tap do |t|
|
86
99
|
unless options.nil?
|
87
100
|
t.configure(options)
|
88
|
-
|
101
|
+
Datadog::Logger.log = options[:log] if options[:log]
|
89
102
|
t.set_tags(options[:tags]) if options[:tags]
|
90
103
|
t.set_tags(env: options[:env]) if options[:env]
|
91
|
-
|
104
|
+
Datadog::Logger.debug_logging = options.fetch(:debug, false)
|
92
105
|
end
|
93
106
|
end
|
94
107
|
end
|
data/lib/ddtrace/context.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'thread'
|
2
2
|
require 'ddtrace/diagnostics/health'
|
3
3
|
|
4
|
+
require 'ddtrace/context_flush'
|
5
|
+
require 'ddtrace/context_provider'
|
6
|
+
|
4
7
|
module Datadog
|
5
8
|
# \Context is used to keep track of a hierarchy of spans for the current
|
6
9
|
# execution flow. During each logical execution, the same \Context is
|
@@ -92,7 +95,7 @@ module Datadog
|
|
92
95
|
if @max_length > 0 && @trace.length >= @max_length
|
93
96
|
# Detach the span from any context, it's being dropped and ignored.
|
94
97
|
span.context = nil
|
95
|
-
Datadog::
|
98
|
+
Datadog::Logger.log.debug("context full, ignoring span #{span.name}")
|
96
99
|
|
97
100
|
# If overflow has already occurred, don't send this metric.
|
98
101
|
# Prevents metrics spam if buffer repeatedly overflows for the same trace.
|
@@ -121,13 +124,13 @@ module Datadog
|
|
121
124
|
set_current_span(span.parent)
|
122
125
|
return if span.tracer.nil?
|
123
126
|
if span.parent.nil? && !all_spans_finished?
|
124
|
-
if Datadog::
|
127
|
+
if Datadog::Logger.debug_logging
|
125
128
|
opened_spans = @trace.length - @finished_spans
|
126
|
-
Datadog::
|
129
|
+
Datadog::Logger.log.debug("root span #{span.name} closed but has #{opened_spans} unfinished spans:")
|
127
130
|
end
|
128
131
|
|
129
132
|
@trace.reject(&:finished?).group_by(&:name).each do |unfinished_span_name, unfinished_spans|
|
130
|
-
Datadog::
|
133
|
+
Datadog::Logger.log.debug("unfinished span: #{unfinished_spans.first}") if Datadog::Logger.debug_logging
|
131
134
|
Diagnostics::Health.metrics.error_unfinished_spans(
|
132
135
|
unfinished_spans.length,
|
133
136
|
tags: ["name:#{unfinished_span_name}"]
|
@@ -145,6 +148,13 @@ module Datadog
|
|
145
148
|
end
|
146
149
|
end
|
147
150
|
|
151
|
+
# @@return [Numeric] numbers of finished spans
|
152
|
+
def finished_span_count
|
153
|
+
@mutex.synchronize do
|
154
|
+
@finished_spans
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
148
158
|
# Returns true if the context is sampled, that is, if it should be kept
|
149
159
|
# and sent to the trace agent.
|
150
160
|
def sampled?
|
@@ -154,27 +164,66 @@ module Datadog
|
|
154
164
|
end
|
155
165
|
|
156
166
|
# Returns both the trace list generated in the current context and
|
157
|
-
# if the context is sampled or not.
|
158
|
-
#
|
167
|
+
# if the context is sampled or not.
|
168
|
+
#
|
169
|
+
# It returns +[nil,@sampled]+ if the \Context is
|
170
|
+
# not finished.
|
171
|
+
#
|
172
|
+
# If a trace is returned, the \Context will be reset so that it
|
159
173
|
# can be re-used immediately.
|
160
174
|
#
|
161
175
|
# This operation is thread-safe.
|
176
|
+
#
|
177
|
+
# @return [Array<Array<Span>, Boolean>] finished trace and sampled flag
|
162
178
|
def get
|
163
179
|
@mutex.synchronize do
|
164
180
|
trace = @trace
|
165
181
|
sampled = @sampled
|
166
182
|
|
167
|
-
attach_sampling_priority if sampled && @sampling_priority
|
168
|
-
attach_origin if @origin
|
169
|
-
|
170
183
|
# still return sampled attribute, even if context is not finished
|
171
184
|
return nil, sampled unless all_spans_finished?
|
172
185
|
|
186
|
+
# Root span is finished at this point, we can configure it
|
187
|
+
annotate_for_flush!(@current_root_span)
|
188
|
+
|
173
189
|
reset
|
174
190
|
[trace, sampled]
|
175
191
|
end
|
176
192
|
end
|
177
193
|
|
194
|
+
# Delete any span matching the condition. This is thread safe.
|
195
|
+
#
|
196
|
+
# @return [Array<Span>] deleted spans
|
197
|
+
def delete_span_if
|
198
|
+
@mutex.synchronize do
|
199
|
+
[].tap do |deleted_spans|
|
200
|
+
@trace.delete_if do |span|
|
201
|
+
finished = span.finished?
|
202
|
+
|
203
|
+
next unless yield span
|
204
|
+
|
205
|
+
deleted_spans << span
|
206
|
+
|
207
|
+
# We need to detach the span from the context, else, some code
|
208
|
+
# finishing it afterwards would mess up with the number of
|
209
|
+
# finished_spans and possibly cause other side effects.
|
210
|
+
span.context = nil
|
211
|
+
# Acknowledge there's one span less to finish, if needed.
|
212
|
+
# It's very important to keep this balanced.
|
213
|
+
@finished_spans -= 1 if finished
|
214
|
+
|
215
|
+
true
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# Set tags to root span required for flush
|
222
|
+
def annotate_for_flush!(span)
|
223
|
+
attach_sampling_priority(span) if @sampled && @sampling_priority
|
224
|
+
attach_origin(span) if @origin
|
225
|
+
end
|
226
|
+
|
178
227
|
# Return a string representation of the context.
|
179
228
|
def to_s
|
180
229
|
@mutex.synchronize do
|
@@ -215,15 +264,15 @@ module Datadog
|
|
215
264
|
@finished_spans > 0 && @trace.length == @finished_spans
|
216
265
|
end
|
217
266
|
|
218
|
-
def attach_sampling_priority
|
219
|
-
|
267
|
+
def attach_sampling_priority(span)
|
268
|
+
span.set_metric(
|
220
269
|
Ext::DistributedTracing::SAMPLING_PRIORITY_KEY,
|
221
270
|
@sampling_priority
|
222
271
|
)
|
223
272
|
end
|
224
273
|
|
225
|
-
def attach_origin
|
226
|
-
|
274
|
+
def attach_origin(span)
|
275
|
+
span.set_tag(
|
227
276
|
Ext::DistributedTracing::ORIGIN_KEY,
|
228
277
|
@origin
|
229
278
|
)
|
@@ -252,49 +301,5 @@ module Datadog
|
|
252
301
|
end
|
253
302
|
end
|
254
303
|
end
|
255
|
-
|
256
|
-
# Delete any span matching the condition. This is thread safe.
|
257
|
-
def delete_span_if
|
258
|
-
@mutex.synchronize do
|
259
|
-
@trace.delete_if do |span|
|
260
|
-
finished = span.finished?
|
261
|
-
delete_span = yield span
|
262
|
-
if delete_span
|
263
|
-
# We need to detach the span from the context, else, some code
|
264
|
-
# finishing it afterwards would mess up with the number of
|
265
|
-
# finished_spans and possibly cause other side effects.
|
266
|
-
span.context = nil
|
267
|
-
# Acknowledge there's one span less to finish, if needed.
|
268
|
-
# It's very important to keep this balanced.
|
269
|
-
@finished_spans -= 1 if finished
|
270
|
-
end
|
271
|
-
delete_span
|
272
|
-
end
|
273
|
-
end
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
# ThreadLocalContext can be used as a tracer global reference to create
|
278
|
-
# a different \Context for each thread. In synchronous tracer, this
|
279
|
-
# is required to prevent multiple threads sharing the same \Context
|
280
|
-
# in different executions.
|
281
|
-
class ThreadLocalContext
|
282
|
-
# ThreadLocalContext can be used as a tracer global reference to create
|
283
|
-
# a different \Context for each thread. In synchronous tracer, this
|
284
|
-
# is required to prevent multiple threads sharing the same \Context
|
285
|
-
# in different executions.
|
286
|
-
def initialize
|
287
|
-
self.local = Datadog::Context.new
|
288
|
-
end
|
289
|
-
|
290
|
-
# Override the thread-local context with a new context.
|
291
|
-
def local=(ctx)
|
292
|
-
Thread.current[:datadog_context] = ctx
|
293
|
-
end
|
294
|
-
|
295
|
-
# Return the thread-local context.
|
296
|
-
def local
|
297
|
-
Thread.current[:datadog_context] ||= Datadog::Context.new
|
298
|
-
end
|
299
304
|
end
|
300
305
|
end
|
@@ -1,132 +1,69 @@
|
|
1
|
-
require 'set'
|
2
|
-
|
3
|
-
require 'ddtrace/context'
|
4
|
-
|
5
1
|
module Datadog
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
def initialize(options = {})
|
21
|
-
# max_spans_before_partial_flush is the amount of spans collected before
|
22
|
-
# the context starts to partially flush parts of traces. With a setting of 10k,
|
23
|
-
# the memory overhead is about 10Mb per thread/context (depends on spans metadata,
|
24
|
-
# this is just an order of magnitude).
|
25
|
-
@max_spans_before_partial_flush = options.fetch(:max_spans_before_partial_flush,
|
26
|
-
DEFAULT_MAX_SPANS_BEFORE_PARTIAL_FLUSH)
|
27
|
-
# min_spans_before_partial_flush is the minimum number of spans required
|
28
|
-
# for a partial flush to happen on a timeout. This is to prevent partial flush
|
29
|
-
# of traces which last a very long time but yet have few spans.
|
30
|
-
@min_spans_before_partial_flush = options.fetch(:min_spans_before_partial_flush,
|
31
|
-
DEFAULT_MIN_SPANS_BEFORE_PARTIAL_FLUSH)
|
32
|
-
# partial_flush_timeout is the limit (in seconds) above which the context
|
33
|
-
# considers flushing parts of the trace. Partial flushes should not be done too
|
34
|
-
# late else the agent rejects them with a "too far in the past" error.
|
35
|
-
@partial_flush_timeout = options.fetch(:partial_flush_timeout,
|
36
|
-
DEFAULT_PARTIAL_FLUSH_TIMEOUT)
|
37
|
-
@partial_traces = []
|
2
|
+
module ContextFlush
|
3
|
+
# Consumes only completed traces (where all spans have finished)
|
4
|
+
class Finished
|
5
|
+
# Consumes and returns completed traces (where all spans have finished)
|
6
|
+
# from the provided +context+, if any.
|
7
|
+
#
|
8
|
+
# Any traces consumed are removed from +context+ as a side effect.
|
9
|
+
#
|
10
|
+
# @return [Array<Span>] trace to be flushed, or +nil+ if the trace is not finished
|
11
|
+
def consume!(context)
|
12
|
+
trace, sampled = context.get
|
13
|
+
trace if sampled
|
14
|
+
end
|
38
15
|
end
|
39
16
|
|
40
|
-
|
41
|
-
|
42
|
-
|
17
|
+
# Performs partial trace flushing to avoid large traces residing in memory for too long
|
18
|
+
class Partial
|
19
|
+
# Start flushing partial trace after this many active spans in one trace
|
20
|
+
DEFAULT_MIN_SPANS_FOR_PARTIAL_FLUSH = 500
|
43
21
|
|
44
|
-
|
45
|
-
|
46
|
-
add_children(m, spans, ids, sub)
|
47
|
-
end
|
22
|
+
def initialize(options = {})
|
23
|
+
@min_spans_for_partial = options.fetch(:min_spans_before_partial_flush, DEFAULT_MIN_SPANS_FOR_PARTIAL_FLUSH)
|
48
24
|
end
|
49
|
-
end
|
50
25
|
|
51
|
-
|
52
|
-
#
|
53
|
-
|
26
|
+
# Consumes and returns completed or partially completed
|
27
|
+
# traces from the provided +context+, if any.
|
28
|
+
#
|
29
|
+
# Partially completed traces, where not all spans have finished,
|
30
|
+
# will only be returned if there are at least
|
31
|
+
# +@min_spans_for_partial+ finished spans.
|
32
|
+
#
|
33
|
+
# Any spans consumed are removed from +context+ as a side effect.
|
34
|
+
#
|
35
|
+
# @return [Array<Span>] partial or complete trace to be flushed, or +nil+ if no spans are finished
|
36
|
+
def consume!(context)
|
37
|
+
trace, sampled = context.get
|
54
38
|
|
55
|
-
|
56
|
-
|
57
|
-
unflushable_ids.add span.span_id
|
58
|
-
while span.parent
|
59
|
-
span = span.parent
|
60
|
-
unflushable_ids.add span.span_id
|
61
|
-
end
|
62
|
-
end
|
39
|
+
return nil unless sampled
|
40
|
+
return trace if trace && !trace.empty?
|
63
41
|
|
64
|
-
|
65
|
-
# Along the road, collect a reverse-tree which allows direct walking from parents to
|
66
|
-
# children but only for the ones we're interested it.
|
67
|
-
roots = []
|
68
|
-
children_map = {}
|
69
|
-
context.send(:each_span) do |span|
|
70
|
-
# There's no point in trying to put the real root in those partial roots, if
|
71
|
-
# it's flushable, the default algorithm would figure way more quickly.
|
72
|
-
if span.parent && !unflushable_ids.include?(span.span_id)
|
73
|
-
if unflushable_ids.include?(span.parent.span_id)
|
74
|
-
# span is flushable but is parent is not
|
75
|
-
roots << span
|
76
|
-
else
|
77
|
-
# span is flushable and its parent is too, build the reverse
|
78
|
-
# parent to child map for this one, it will be useful
|
79
|
-
children_map[span.parent.span_id] ||= []
|
80
|
-
children_map[span.parent.span_id] << span
|
81
|
-
end
|
82
|
-
end
|
42
|
+
partial_trace(context)
|
83
43
|
end
|
84
44
|
|
85
|
-
|
86
|
-
partial_traces = []
|
87
|
-
all_ids = Set.new
|
88
|
-
roots.each do |root|
|
89
|
-
spans = []
|
90
|
-
add_children(children_map, spans, all_ids, root)
|
91
|
-
partial_traces << spans
|
92
|
-
end
|
45
|
+
private
|
93
46
|
|
94
|
-
|
95
|
-
|
96
|
-
|
47
|
+
def partial_trace(context)
|
48
|
+
return nil if context.finished_span_count < @min_spans_for_partial
|
49
|
+
|
50
|
+
finished_spans(context)
|
51
|
+
end
|
97
52
|
|
98
|
-
|
99
|
-
|
100
|
-
return nil unless traces && flushed_ids
|
53
|
+
def finished_spans(context)
|
54
|
+
trace = context.delete_span_if(&:finished?)
|
101
55
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
56
|
+
# Ensure that the first span in a partial trace has
|
57
|
+
# sampling and origin information.
|
58
|
+
if trace[0]
|
59
|
+
context.annotate_for_flush!(trace[0])
|
60
|
+
else
|
61
|
+
Datadog::Tracer.log.debug('Tried to retrieve trace from context, but got nothing. ' \
|
62
|
+
"Is there another consumer for this context? #{context.trace_id}")
|
63
|
+
end
|
108
64
|
|
109
|
-
|
110
|
-
def each_partial_trace(context)
|
111
|
-
start_time = context.send(:start_time)
|
112
|
-
length = context.send(:length)
|
113
|
-
# Stop and do not flush anything if there are not enough spans.
|
114
|
-
return if length <= @min_spans_before_partial_flush
|
115
|
-
# If there are enough spans, but not too many, check for start time.
|
116
|
-
# If timeout is not given or 0, then wait
|
117
|
-
return if length <= @max_spans_before_partial_flush &&
|
118
|
-
(@partial_flush_timeout.nil? || @partial_flush_timeout <= 0 ||
|
119
|
-
(start_time && start_time > Time.now.utc - @partial_flush_timeout))
|
120
|
-
# Here, either the trace is old or we have too many spans, flush it.
|
121
|
-
traces = partial_flush(context)
|
122
|
-
return unless traces
|
123
|
-
traces.each do |trace|
|
124
|
-
yield trace
|
65
|
+
trace unless trace.empty?
|
125
66
|
end
|
126
67
|
end
|
127
|
-
|
128
|
-
private :add_children
|
129
|
-
private :partial_traces
|
130
|
-
private :partial_flush
|
131
68
|
end
|
132
69
|
end
|