@@ -149,10 +149,10 @@ def trace(
149
149
context = call_context
150
150
active_trace = context . active_trace
151
151
trace = if continue_from || active_trace . nil?
152
- start_trace ( continue_from : continue_from )
153
- else
154
- active_trace
155
- end
152
+ start_trace ( continue_from : continue_from )
153
+ else
154
+ active_trace
155
+ end
156
156
rescue => e
157
157
logger . debug { "Failed to trace: #{ e } " }
158
158
@@ -241,10 +241,21 @@ def active_correlation(key = nil)
241
241
trace . to_correlation
242
242
end
243
243
244
- # Setup a new trace to continue from where another
244
+ # Setup a new trace execution context to continue from where another
245
245
# trace left off.
246
+ # This is useful to continue distributed or async traces.
247
+ #
248
+ # The first span created in the restored context is a direct child of the
249
+ # active span from when the {Datadog::Tracing::TraceDigest} was created.
250
+ #
251
+ # When no block is given, the trace context is restored in the current thread.
252
+ # It remains active until the first span created in this restored context is finished.
253
+ # After that, if a new span is created, it start a new, unrelated trace.
246
254
#
247
- # Used to continue distributed or async traces.
255
+ # When a block is given, the trace context is restored inside the block execution.
256
+ # It remains active until the block ends, even when the first span created inside
257
+ # the block finishes. This means that multiple spans can be direct children of the
258
+ # active span from when the {Datadog::Tracing::TraceDigest} was created.
248
259
#
249
260
# @param [Datadog::Tracing::TraceDigest] digest continue from the {Datadog::Tracing::TraceDigest}.
250
261
# @param [Thread] key Thread to retrieve trace from. Defaults to current thread. For internal use only.
@@ -260,13 +271,32 @@ def continue_trace!(digest, key = nil, &block)
260
271
# Start a new trace from the digest
261
272
context = call_context ( key )
262
273
original_trace = active_trace ( key )
263
- trace = start_trace ( continue_from : digest )
274
+ # When we want the trace to be bound to a block, we cannot let
275
+ # it auto finish when the local root span finishes. This would
276
+ # create mutiple traces inside the block. Instead, we'll
277
+ # expliclity finish the trace after the block finishes.
278
+ auto_finish = !block
279
+
280
+ trace = start_trace ( continue_from : digest , auto_finish : auto_finish )
264
281
265
282
# If block hasn't been given; we need to manually deactivate
266
283
# this trace. Subscribe to the trace finished event to do this.
267
284
subscribe_trace_deactivation! ( context , trace , original_trace ) unless block
268
285
269
- context . activate! ( trace , &block )
286
+ if block
287
+ # When a block is given, the trace will be active until the block finishes.
288
+ context . activate! ( trace ) do
289
+ yield
290
+ ensure # We have to flush even when an error occurs
291
+ # On block completion, force the trace to finish and flush its finished spans.
292
+ # Unfinished spans are lost as the {TraceOperation} has ended.
293
+ trace . finish!
294
+ flush_trace ( trace )
295
+ end
296
+ else
297
+ # Otherwise, the trace will be bound to the current thread after this point
298
+ context . activate! ( trace )
299
+ end
270
300
end
271
301
272
302
# Sample a span, tagging the trace as appropriate.
@@ -329,15 +359,15 @@ def call_context(key = nil)
329
359
@provider . context ( key )
330
360
end
331
361
332
- def build_trace ( digest = nil )
362
+ def build_trace ( digest , auto_finish )
333
363
# Resolve hostname if configured
334
364
hostname = Core ::Environment ::Socket . hostname if Datadog . configuration . tracing . report_hostname
335
- hostname = ( hostname && !hostname . empty? ) ? hostname : nil
365
+ hostname = hostname && !hostname . empty? ? hostname : nil
336
366
337
367
if digest
338
368
sampling_priority = if propagate_sampling_priority? ( upstream_tags : digest . trace_distributed_tags )
339
- digest . trace_sampling_priority
340
- end
369
+ digest . trace_sampling_priority
370
+ end
341
371
TraceOperation . new (
342
372
logger : logger ,
343
373
hostname : hostname ,
@@ -353,7 +383,8 @@ def build_trace(digest = nil)
353
383
trace_state_unknown_fields : digest . trace_state_unknown_fields ,
354
384
remote_parent : digest . span_remote ,
355
385
tracer : self ,
356
- baggage : digest . baggage
386
+ baggage : digest . baggage ,
387
+ auto_finish : auto_finish
357
388
)
358
389
else
359
390
TraceOperation . new (
@@ -362,13 +393,13 @@ def build_trace(digest = nil)
362
393
profiling_enabled : profiling_enabled ,
363
394
apm_tracing_enabled : apm_tracing_enabled ,
364
395
remote_parent : false ,
365
- tracer : self
396
+ tracer : self ,
397
+ auto_finish : auto_finish
366
398
)
367
399
end
368
400
end
369
401
# rubocop:enable Metrics/MethodLength
370
402
371
- # rubocop:disable Metrics/MethodLength
372
403
def bind_trace_events! ( trace_op )
373
404
events = trace_op . send ( :events )
374
405
@@ -387,13 +418,12 @@ def bind_trace_events!(trace_op)
387
418
flush_trace ( event_trace_op )
388
419
end
389
420
end
390
- # rubocop:enable Metrics/MethodLength
391
421
392
422
# Creates a new TraceOperation, with events bounds to this Tracer instance.
393
423
# @return [TraceOperation]
394
- def start_trace ( continue_from : nil )
424
+ def start_trace ( continue_from : nil , auto_finish : true )
395
425
# Build a new trace using digest if provided.
396
- trace = build_trace ( continue_from )
426
+ trace = build_trace ( continue_from , auto_finish )
397
427
398
428
# Bind trace events: sample trace, set default service, flush spans.
399
429
bind_trace_events! ( trace )
@@ -402,7 +432,6 @@ def start_trace(continue_from: nil)
402
432
end
403
433
404
434
# rubocop:disable Lint/UnderscorePrefixedVariableName
405
- # rubocop:disable Metrics/MethodLength
406
435
def start_span (
407
436
name ,
408
437
continue_from : nil ,
@@ -454,18 +483,17 @@ def start_span(
454
483
span
455
484
end
456
485
end
457
- # rubocop:enable Lint/UnderscorePrefixedVariableName
458
- # rubocop:enable Metrics/MethodLength
459
486
487
+ # rubocop:enable Lint/UnderscorePrefixedVariableName
460
488
def resolve_tags ( tags , service )
461
489
merged_tags = if @tags . any? && tags
462
- # Combine default tags with provided tags,
463
- # preferring provided tags.
464
- @tags . merge ( tags )
465
- else
466
- # Use provided tags or default tags if none.
467
- tags || @tags . dup
468
- end
490
+ # Combine default tags with provided tags,
491
+ # preferring provided tags.
492
+ @tags . merge ( tags )
493
+ else
494
+ # Use provided tags or default tags if none.
495
+ tags || @tags . dup
496
+ end
469
497
# Remove version tag if service is not the default service
470
498
if merged_tags . key? ( Core ::Environment ::Ext ::TAG_VERSION ) && service && service != @default_service
471
499
merged_tags . delete ( Core ::Environment ::Ext ::TAG_VERSION )
0 commit comments