ddtrace 0.30.1 → 0.31.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +28 -0
  3. data/.circleci/images/primary/Dockerfile-2.7.0 +73 -0
  4. data/Appraisals +111 -2
  5. data/CHANGELOG.md +33 -1
  6. data/Rakefile +69 -1
  7. data/ddtrace.gemspec +1 -0
  8. data/docker-compose.yml +30 -0
  9. data/docs/GettingStarted.md +28 -3
  10. data/lib/ddtrace.rb +6 -0
  11. data/lib/ddtrace/buffer.rb +3 -3
  12. data/lib/ddtrace/configuration/base.rb +2 -1
  13. data/lib/ddtrace/configuration/settings.rb +15 -2
  14. data/lib/ddtrace/context.rb +62 -57
  15. data/lib/ddtrace/context_flush.rb +51 -114
  16. data/lib/ddtrace/context_provider.rb +45 -0
  17. data/lib/ddtrace/contrib/action_cable/configuration/settings.rb +25 -0
  18. data/lib/ddtrace/contrib/action_cable/event.rb +65 -0
  19. data/lib/ddtrace/contrib/action_cable/events.rb +33 -0
  20. data/lib/ddtrace/contrib/action_cable/events/broadcast.rb +49 -0
  21. data/lib/ddtrace/contrib/action_cable/events/perform_action.rb +52 -0
  22. data/lib/ddtrace/contrib/action_cable/events/transmit.rb +50 -0
  23. data/lib/ddtrace/contrib/action_cable/ext.rb +23 -0
  24. data/lib/ddtrace/contrib/action_cable/instrumentation.rb +31 -0
  25. data/lib/ddtrace/contrib/action_cable/integration.rb +36 -0
  26. data/lib/ddtrace/contrib/action_cable/patcher.rb +27 -0
  27. data/lib/ddtrace/contrib/action_pack/action_controller/instrumentation.rb +2 -2
  28. data/lib/ddtrace/contrib/action_view/events/render_partial.rb +5 -3
  29. data/lib/ddtrace/contrib/action_view/events/render_template.rb +5 -3
  30. data/lib/ddtrace/contrib/action_view/instrumentation/partial_renderer.rb +2 -1
  31. data/lib/ddtrace/contrib/action_view/instrumentation/template_renderer.rb +4 -2
  32. data/lib/ddtrace/contrib/action_view/patcher.rb +1 -1
  33. data/lib/ddtrace/contrib/active_record/events/instantiation.rb +1 -1
  34. data/lib/ddtrace/contrib/active_record/events/sql.rb +1 -1
  35. data/lib/ddtrace/contrib/active_support/cache/instrumentation.rb +2 -2
  36. data/lib/ddtrace/contrib/active_support/notifications/subscription.rb +2 -2
  37. data/lib/ddtrace/contrib/dalli/patcher.rb +1 -1
  38. data/lib/ddtrace/contrib/dalli/quantize.rb +1 -1
  39. data/lib/ddtrace/contrib/elasticsearch/patcher.rb +1 -1
  40. data/lib/ddtrace/contrib/excon/middleware.rb +3 -3
  41. data/lib/ddtrace/contrib/faraday/connection.rb +18 -0
  42. data/lib/ddtrace/contrib/faraday/integration.rb +1 -1
  43. data/lib/ddtrace/contrib/faraday/patcher.rb +3 -3
  44. data/lib/ddtrace/contrib/grape/endpoint.rb +5 -5
  45. data/lib/ddtrace/contrib/grape/patcher.rb +1 -1
  46. data/lib/ddtrace/contrib/grpc/datadog_interceptor/client.rb +1 -1
  47. data/lib/ddtrace/contrib/grpc/datadog_interceptor/server.rb +2 -2
  48. data/lib/ddtrace/contrib/grpc/patcher.rb +1 -1
  49. data/lib/ddtrace/contrib/http/instrumentation.rb +1 -1
  50. data/lib/ddtrace/contrib/mongodb/subscribers.rb +2 -2
  51. data/lib/ddtrace/contrib/patchable.rb +1 -1
  52. data/lib/ddtrace/contrib/patcher.rb +1 -1
  53. data/lib/ddtrace/contrib/rack/middlewares.rb +2 -2
  54. data/lib/ddtrace/contrib/rack/patcher.rb +2 -2
  55. data/lib/ddtrace/contrib/rack/request_queue.rb +1 -1
  56. data/lib/ddtrace/contrib/rails/configuration/settings.rb +1 -0
  57. data/lib/ddtrace/contrib/rails/framework.rb +12 -0
  58. data/lib/ddtrace/contrib/rake/instrumentation.rb +2 -2
  59. data/lib/ddtrace/contrib/redis/quantize.rb +1 -1
  60. data/lib/ddtrace/contrib/sinatra/tracer.rb +1 -1
  61. data/lib/ddtrace/ext/forced_tracing.rb +1 -1
  62. data/lib/ddtrace/ext/sampling.rb +3 -0
  63. data/lib/ddtrace/logger.rb +42 -0
  64. data/lib/ddtrace/metrics.rb +5 -5
  65. data/lib/ddtrace/monkey.rb +1 -1
  66. data/lib/ddtrace/pin.rb +1 -1
  67. data/lib/ddtrace/pipeline.rb +1 -1
  68. data/lib/ddtrace/propagation/http_propagator.rb +2 -2
  69. data/lib/ddtrace/runtime/cgroup.rb +1 -1
  70. data/lib/ddtrace/runtime/container.rb +1 -1
  71. data/lib/ddtrace/runtime/metrics.rb +1 -1
  72. data/lib/ddtrace/sampler.rb +1 -1
  73. data/lib/ddtrace/sampling/rule.rb +1 -1
  74. data/lib/ddtrace/sampling/rule_sampler.rb +4 -4
  75. data/lib/ddtrace/span.rb +24 -6
  76. data/lib/ddtrace/sync_writer.rb +4 -3
  77. data/lib/ddtrace/tracer.rb +37 -77
  78. data/lib/ddtrace/transport/http/client.rb +2 -2
  79. data/lib/ddtrace/utils.rb +1 -1
  80. data/lib/ddtrace/version.rb +2 -2
  81. data/lib/ddtrace/workers.rb +3 -3
  82. data/lib/ddtrace/writer.rb +3 -2
  83. metadata +44 -6
  84. data/lib/ddtrace/contrib/faraday/rack_builder.rb +0 -18
  85. data/lib/ddtrace/provider.rb +0 -21
@@ -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.6 | Full | Latest |
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 | 5.0 - 6.0 |
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::Tracer.log.info { "this is typically called by tracing code" }
1478
+ Datadog::Logger.log.info { "this is typically called by tracing code" }
1454
1479
  ```
1455
1480
 
1456
1481
  ### Environment and tags
@@ -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'
@@ -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::Tracer.log.debug("Failed to measure queue accept. Cause: #{e.message} Source: #{e.backtrace.first}")
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::Tracer.log.debug("Failed to measure queue drop. Cause: #{e.message} Source: #{e.backtrace.first}")
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::Tracer.log.debug("Failed to measure queue. Cause: #{e.message} Source: #{e.backtrace.first}")
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
@@ -23,7 +23,8 @@ module Datadog
23
23
  settings_class = new_settings_class(&block)
24
24
 
25
25
  option(name) do |o|
26
- o.default settings_class.new
26
+ o.default -> { settings_class.new }
27
+ o.lazy
27
28
  o.resetter do |value|
28
29
  value.reset! if value.respond_to?(:reset!)
29
30
  value
@@ -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
- t.class.log = options[:log] if options[:log]
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
- t.class.debug_logging = options.fetch(:debug, false)
104
+ Datadog::Logger.debug_logging = options.fetch(:debug, false)
92
105
  end
93
106
  end
94
107
  end
@@ -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::Tracer.log.debug("context full, ignoring span #{span.name}")
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::Tracer.debug_logging
127
+ if Datadog::Logger.debug_logging
125
128
  opened_spans = @trace.length - @finished_spans
126
- Datadog::Tracer.log.debug("root span #{span.name} closed but has #{opened_spans} unfinished spans:")
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::Tracer.log.debug("unfinished span: #{unfinished_spans.first}") if Datadog::Tracer.debug_logging
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. It returns nil, nil if the ``Context`` is
158
- # not finished. If a trace is returned, the \Context will be reset so that it
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
- @current_root_span.set_metric(
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
- @current_root_span.set_tag(
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
- # \ContextFlush is used to cap context size and avoid it using too much memory.
7
- # It performs memory flushes when required.
8
- class ContextFlush
9
- # by default, soft and hard limits are the same
10
- DEFAULT_MAX_SPANS_BEFORE_PARTIAL_FLUSH = Datadog::Context::DEFAULT_MAX_LENGTH
11
- # by default, never do a partial flush
12
- DEFAULT_MIN_SPANS_BEFORE_PARTIAL_FLUSH = Datadog::Context::DEFAULT_MAX_LENGTH
13
- # timeout should be lower than the trace agent window
14
- DEFAULT_PARTIAL_FLUSH_TIMEOUT = 10
15
-
16
- private_constant :DEFAULT_MAX_SPANS_BEFORE_PARTIAL_FLUSH
17
- private_constant :DEFAULT_MIN_SPANS_BEFORE_PARTIAL_FLUSH
18
- private_constant :DEFAULT_PARTIAL_FLUSH_TIMEOUT
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
- def add_children(m, spans, ids, leaf)
41
- spans << leaf
42
- ids.add(leaf.span_id)
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
- if m[leaf.span_id]
45
- m[leaf.span_id].each do |sub|
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
- def partial_traces(context)
52
- # 1st step, taint all parents of an unfinished span as unflushable
53
- unflushable_ids = Set.new
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
- context.send(:each_span) do |span|
56
- next if span.finished? || unflushable_ids.include?(span.span_id)
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
- # 2nd step, find all spans which are at the border between flushable and unflushable
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
- # 3rd step, find all children, as this can be costly, only perform it for partial roots
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
- return [nil, nil] if partial_traces.empty?
95
- [partial_traces, all_ids]
96
- end
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
- def partial_flush(context)
99
- traces, flushed_ids = partial_traces(context)
100
- return nil unless traces && flushed_ids
53
+ def finished_spans(context)
54
+ trace = context.delete_span_if(&:finished?)
101
55
 
102
- # We need to reject by span ID and not by value, because a span
103
- # value may be altered (typical example: it's finished by some other thread)
104
- # since we lock only the context, not all the spans which belong to it.
105
- context.send(:delete_span_if) { |span| flushed_ids.include? span.span_id }
106
- traces
107
- end
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
- # Performs an operation which each partial trace it can get from the context.
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