/
Tracer.java
652 lines (605 loc) · 25.7 KB
/
Tracer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
/*
* Copyright 2013-2020 The OpenZipkin Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package brave;
import brave.handler.MutableSpan;
import brave.handler.SpanHandler;
import brave.internal.InternalPropagation;
import brave.internal.Nullable;
import brave.internal.Platform;
import brave.internal.recorder.PendingSpan;
import brave.internal.recorder.PendingSpans;
import brave.propagation.CurrentTraceContext;
import brave.propagation.CurrentTraceContext.Scope;
import brave.propagation.Propagation;
import brave.propagation.SamplingFlags;
import brave.propagation.TraceContext;
import brave.propagation.TraceContext.Extractor;
import brave.propagation.TraceContextOrSamplingFlags;
import brave.propagation.TraceIdContext;
import brave.sampler.Sampler;
import brave.sampler.SamplerFunction;
import java.io.Closeable;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import static brave.internal.InternalPropagation.FLAG_LOCAL_ROOT;
import static brave.internal.InternalPropagation.FLAG_SAMPLED;
import static brave.internal.InternalPropagation.FLAG_SAMPLED_LOCAL;
import static brave.internal.InternalPropagation.FLAG_SAMPLED_SET;
import static brave.internal.InternalPropagation.FLAG_SHARED;
import static brave.internal.collect.Lists.concat;
import static brave.propagation.SamplingFlags.EMPTY;
import static brave.propagation.SamplingFlags.NOT_SAMPLED;
import static brave.propagation.SamplingFlags.SAMPLED;
/**
* Using a tracer, you can create a root span capturing the critical path of a request. Child spans
* can be created to allocate latency relating to outgoing requests.
*
* When tracing single-threaded code, just run it inside a scoped span:
* <pre>{@code
* // Start a new trace or a span within an existing trace representing an operation
* ScopedSpan span = tracer.startScopedSpan("encode");
* try {
* // The span is in "scope" so that downstream code such as loggers can see trace IDs
* return encoder.encode();
* } catch (RuntimeException | Error e) {
* span.error(e); // Unless you handle exceptions, you might not know the operation failed!
* throw e;
* } finally {
* span.finish();
* }
* }</pre>
*
* <p>When you need more features, or finer control, use the {@linkplain Span} type:
* <pre>{@code
* // Start a new trace or a span within an existing trace representing an operation
* Span span = tracer.nextSpan().name("encode").start();
* // Put the span in "scope" so that downstream code such as loggers can see trace IDs
* try (SpanInScope ws = tracer.withSpanInScope(span)) {
* return encoder.encode();
* } catch (RuntimeException | Error e) {
* span.error(e); // Unless you handle exceptions, you might not know the operation failed!
* throw e;
* } finally {
* span.finish(); // note the scope is independent of the span. Always finish a span.
* }
* }</pre>
*
* <p>Both of the above examples report the exact same span on finish!
*
* @see Span
* @see ScopedSpan
* @see Propagation
*/
public class Tracer {
final Clock clock;
final Propagation.Factory propagationFactory;
final SpanHandler spanHandler; // only for toString
final PendingSpans pendingSpans;
final Sampler sampler;
final CurrentTraceContext currentTraceContext;
final boolean traceId128Bit, supportsJoin, alwaysSampleLocal;
final AtomicBoolean noop;
Tracer(
Clock clock,
Propagation.Factory propagationFactory,
SpanHandler spanHandler,
PendingSpans pendingSpans,
Sampler sampler,
CurrentTraceContext currentTraceContext,
boolean traceId128Bit,
boolean supportsJoin,
boolean alwaysSampleLocal,
AtomicBoolean noop
) {
this.clock = clock;
this.propagationFactory = propagationFactory;
this.spanHandler = spanHandler;
this.pendingSpans = pendingSpans;
this.sampler = sampler;
this.currentTraceContext = currentTraceContext;
this.traceId128Bit = traceId128Bit;
this.supportsJoin = supportsJoin;
this.alwaysSampleLocal = alwaysSampleLocal;
this.noop = noop;
}
/**
* @since 4.19
* @deprecated Since 5.8, use {@link #nextSpan(SamplerFunction, Object)} or {@link
* #startScopedSpan(String, SamplerFunction, Object)}
*/
@Deprecated public Tracer withSampler(Sampler sampler) {
if (sampler == null) throw new NullPointerException("sampler == null");
return new Tracer(
clock,
propagationFactory,
spanHandler,
pendingSpans,
sampler,
currentTraceContext,
traceId128Bit,
supportsJoin,
alwaysSampleLocal,
noop
);
}
/**
* Explicitly creates a new trace. The result will be a root span (no parent span ID).
*
* <p>To implicitly create a new trace, or a span within an existing one, use {@link
* #nextSpan()}.
*/
public Span newTrace() {
return _toSpan(null, newRootContext(0));
}
/**
* Joining is re-using the same trace and span ids extracted from an incoming RPC request. This
* should not be used for messaging operations, as {@link #nextSpan(TraceContextOrSamplingFlags)}
* is a better choice.
*
* <p>When this incoming context is sampled, we assume this is a shared span, one where the
* caller and the current tracer report to the same span IDs. If no sampling decision occurred
* yet, we have exclusive access to this span ID.
*
* <p>Here's an example of conditionally joining a span, depending on if a trace context was
* extracted from an incoming request.
*
* <pre>{@code
* extracted = extractor.extract(request);
* span = contextOrFlags.context() != null
* ? tracer.joinSpan(contextOrFlags.context())
* : tracer.nextSpan(extracted);
* }</pre>
*
* <p><em>Note:</em> When {@link Propagation.Factory#supportsJoin()} is false, this will always
* fork a new child via {@link #newChild(TraceContext)}.
*
* @see Propagation
* @see Extractor#extract(Object)
* @see TraceContextOrSamplingFlags#context()
* @see #nextSpan(TraceContextOrSamplingFlags)
*/
public final Span joinSpan(TraceContext context) {
if (context == null) throw new NullPointerException("context == null");
if (!supportsJoin) return newChild(context);
// set shared flag if not already done
int flags = InternalPropagation.instance.flags(context);
if (!context.shared()) {
flags |= FLAG_SHARED;
return toSpan(context, InternalPropagation.instance.withFlags(context, flags));
} else {
flags &= ~FLAG_SHARED;
return toSpan(InternalPropagation.instance.withFlags(context, flags), context);
}
}
/** Returns an equivalent context if exists in the pending map */
TraceContext swapForPendingContext(TraceContext context) {
PendingSpan pendingSpan = pendingSpans.get(context);
return pendingSpan != null ? pendingSpan.context() : null;
}
/**
* Explicitly creates a child within an existing trace. The result will be have its parent ID set
* to the input's span ID. If a sampling decision has not yet been made, one will happen here.
*
* <p>To implicitly create a new trace, or a span within an existing one, use {@link
* #nextSpan()}.
*/
public Span newChild(TraceContext parent) {
if (parent == null) throw new NullPointerException("parent == null");
return _toSpan(parent, decorateContext(parent, parent.spanId()));
}
TraceContext newRootContext(int flags) {
flags &= ~FLAG_SHARED; // cannot be shared if we aren't reusing the span ID
return decorateContext(flags, 0L, 0L, 0L, 0L, 0L, Collections.emptyList());
}
/**
* Decorates a context after backfilling any missing data such as span IDs or sampling state.
*
* <p>Called by methods which can accept externally supplied parent trace contexts: Ex. {@link
* #newChild(TraceContext)} and {@link #startScopedSpanWithParent(String, TraceContext)}. This
* implies the {@link TraceContext#localRootId()} could be zero, if the context was manually
* created.
*/
TraceContext decorateContext(TraceContext parent, long parentId) {
int flags = InternalPropagation.instance.flags(parent);
flags &= ~FLAG_SHARED; // cannot be shared if we aren't reusing the span ID
return decorateContext(
flags,
parent.traceIdHigh(),
parent.traceId(),
parent.localRootId(),
parentId,
0L,
parent.extra()
);
}
/**
* Creates a trace context object holding the below fields. When fields such as span ID are
* absent, they will be backfilled. Then, any missing state managed by the tracer are applied,
* such as the "local root". Finally, decoration hooks apply to ensure any propagation state are
* added to the "extra" section of the result. This supports functionality like baggage
* propagation.
*
* <p>All parameters except span ID can be empty in the case of a new root span.
*
* @param flags any incoming flags from a parent context.
* @param traceIdHigh See {@link TraceContext#traceIdHigh()}
* @param traceId Zero is a new trace. Otherwise, {@link TraceContext#traceId()}.
* @param localRootId Zero is a new local root. Otherwise, {@link TraceContext#localRootId()}.
* @param parentId Same as {@link TraceContext#parentIdAsLong()}.
* @param spanId When non-zero this is a shared span. See {@link TraceContext#spanId()}.
* @param extra Any additional {@link TraceContext#extra() propagated state}.
* @return a decorated, sampled context with local root information applied.
*/
TraceContext decorateContext(
int flags,
long traceIdHigh,
long traceId,
long localRootId,
long parentId,
long spanId,
List<Object> extra
) {
if (alwaysSampleLocal && (flags & FLAG_SAMPLED_LOCAL) != FLAG_SAMPLED_LOCAL) {
flags |= FLAG_SAMPLED_LOCAL;
}
if (spanId == 0L) spanId = nextId();
if (traceId == 0L) { // make a new trace ID
traceIdHigh = traceId128Bit ? Platform.get().nextTraceIdHigh() : 0L;
traceId = spanId;
}
if ((flags & FLAG_SAMPLED_SET) != FLAG_SAMPLED_SET) { // cheap check for not yet sampled
flags = InternalPropagation.sampled(sampler.isSampled(traceId), flags);
flags &= ~FLAG_SHARED; // cannot be shared if not yet sampled
}
// Zero when root or an externally managed context was passed to newChild or scopedWithParent
if (localRootId == 0L) {
localRootId = spanId;
flags |= FLAG_LOCAL_ROOT;
} else {
flags &= ~FLAG_LOCAL_ROOT;
}
return propagationFactory.decorate(InternalPropagation.instance.newTraceContext(
flags,
traceIdHigh,
traceId,
localRootId,
parentId,
spanId,
extra
));
}
/**
* This creates a new span based on parameters extracted from an incoming request. This will
* always result in a new span. If no trace identifiers were extracted, a span will be created
* based on the implicit context in the same manner as {@link #nextSpan()}. If a sampling decision
* has not yet been made, one will happen here.
*
* <p>Ex.
* <pre>{@code
* extracted = extractor.extract(request);
* span = tracer.nextSpan(extracted);
* }</pre>
*
* <p><em>Note:</em> Unlike {@link #joinSpan(TraceContext)}, this does not attempt to re-use
* extracted span IDs. This means the extracted context (if any) is the parent of the span
* returned.
*
* <p><em>Note:</em> If a context could be extracted from the input, that trace is resumed, not
* whatever the {@link #currentSpan()} was. Make sure you re-apply {@link #withSpanInScope(Span)}
* so that data is written to the correct trace.
*
* @see Propagation
* @see Extractor#extract(Object)
* @see #nextSpan(SamplerFunction, Object)
*/
// TODO: BRAVE6 a MutableTraceContext object is cleaner especially here, as we can represent a
// partial result, such as trace id without span ID without declaring a special type. Also, the
// the code is a bit easier to work with especially if we want to avoid excess allocations. Here,
// we manually code some things to keep the cpu and allocations low, at the cost of readability.
public Span nextSpan(TraceContextOrSamplingFlags extracted) {
if (extracted == null) throw new NullPointerException("extracted == null");
TraceContext context = extracted.context();
if (context != null) return newChild(context);
TraceIdContext traceIdContext = extracted.traceIdContext();
if (traceIdContext != null) {
return _toSpan(null, decorateContext(
InternalPropagation.instance.flags(extracted.traceIdContext()),
traceIdContext.traceIdHigh(),
traceIdContext.traceId(),
0L,
0L,
0L,
extracted.extra()
));
}
SamplingFlags samplingFlags = extracted.samplingFlags();
List<Object> extra = extracted.extra();
TraceContext parent = currentTraceContext.get();
int flags;
long traceIdHigh = 0L, traceId = 0L, localRootId = 0L, spanId = 0L;
if (parent != null) {
// At this point, we didn't extract trace IDs, but do have a trace in progress. Since typical
// trace sampling is up front, we retain the decision from the parent.
flags = InternalPropagation.instance.flags(parent);
traceIdHigh = parent.traceIdHigh();
traceId = parent.traceId();
localRootId = parent.localRootId();
spanId = parent.spanId();
extra = concat(extra, parent.extra());
} else {
flags = InternalPropagation.instance.flags(samplingFlags);
}
return _toSpan(parent,
decorateContext(flags, traceIdHigh, traceId, localRootId, spanId, 0L, extra));
}
/**
* Converts the context to a Span object after decorating it for propagation.
*
* <p>This api is not advised for routine use. It is better to hold a reference to a span created
* elsewhere vs rely on implicit lookups.
*/
public Span toSpan(TraceContext context) {
return toSpan(null, context);
}
Span toSpan(@Nullable TraceContext parent, TraceContext context) {
// Re-use a pending context if present: This ensures reference consistency on Span.context()
TraceContext pendingContext = swapForPendingContext(context);
if (pendingContext != null) return _toSpan(parent, pendingContext);
// There are a few known scenarios for the context to be absent from the pending map:
// * Created by a separate tracer (localRootId set)
// * Recreating the same trace context after it was garbage collected (localRootId set)
// * Ad-hoc usage of TraceContext.Builder (localRootId not set, as only settable internally)
//
// The first two scenarios are currently indistinguishable from each other. If we had a way to
// tell if the current tracer already decorated the context, we could avoid re-decorating it
// in the case of recreation. This is an edge case anyway and decoration should be idempotent.
// Hence, we decorate unconditionally here.
TraceContext decorated = decorateContext(
InternalPropagation.instance.flags(context),
context.traceIdHigh(),
context.traceId(),
parent != null ? context.localRootId() : 0L, // we are now a local root
context.parentIdAsLong(),
context.spanId(),
context.extra()
);
return _toSpan(parent, decorated);
}
Span _toSpan(@Nullable TraceContext parent, TraceContext context) {
if (isNoop(context)) return new NoopSpan(context);
// allocate a mutable span in case multiple threads call this method.. they'll use the same data
PendingSpan pendingSpan = pendingSpans.getOrCreate(parent, context, false);
TraceContext pendingContext = pendingSpan.context();
// A lost race of Tracer.toSpan(context) is the only known situation where "context" won't be
// the same as pendingSpan.context()
if (pendingContext != null) context = pendingContext;
return new RealSpan(context, pendingSpans, pendingSpan.state(), pendingSpan.clock());
}
/**
* Makes the given span the "current span" and returns an object that exits that scope on close.
* Calls to {@link #currentSpan()} and {@link #currentSpanCustomizer()} will affect this span
* until the return value is closed.
*
* <p>The most convenient way to use this method is via the try-with-resources idiom.
*
* Ex.
* <pre>{@code
* // Assume a framework interceptor uses this method to set the inbound span as current
* try (SpanInScope ws = tracer.withSpanInScope(span)) {
* return inboundRequest.invoke();
* // note: try-with-resources closes the scope *before* the catch block
* } catch (RuntimeException | Error e) {
* span.error(e);
* throw e;
* } finally {
* span.finish();
* }
*
* // An unrelated framework interceptor can now lookup the correct parent for outbound requests
* Span parent = tracer.currentSpan()
* Span span = tracer.nextSpan().name("outbound").start(); // parent is implicitly looked up
* try (SpanInScope ws = tracer.withSpanInScope(span)) {
* return outboundRequest.invoke();
* // note: try-with-resources closes the scope *before* the catch block
* } catch (RuntimeException | Error e) {
* span.error(e);
* throw e;
* } finally {
* span.finish();
* }
* }</pre>
*
* <p>When tracing in-process commands, prefer {@link #startScopedSpan(String)} which scopes by
* default.
*
* <p>Note: While downstream code might affect the span, calling this method, and calling close
* on the result have no effect on the input. For example, calling close on the result does not
* finish the span. Not only is it safe to call close, you must call close to end the scope, or
* risk leaking resources associated with the scope.
*
* @param span span to place into scope or null to clear the scope
*/
public SpanInScope withSpanInScope(@Nullable Span span) {
return new SpanInScope(currentTraceContext.newScope(span != null ? span.context() : null));
}
/**
* Returns a customizer for current span in scope or noop if there isn't one.
*
* <p>Unlike {@link CurrentSpanCustomizer}, this represents a single span. Accordingly, this
* reference should not be saved as a field. That said, it is more efficient to save this result
* as a method-local variable vs repeated calls.
*/
public SpanCustomizer currentSpanCustomizer() {
// note: we don't need to decorate the context for propagation as it is only used for toString
TraceContext context = currentTraceContext.get();
if (context == null || isNoop(context)) return NoopSpanCustomizer.INSTANCE;
return new SpanCustomizerShield(toSpan(context));
}
/**
* Returns the current span in scope or null if there isn't one.
*
* <p>When entering user code, prefer {@link #currentSpanCustomizer()} as it is a stable type and
* will never return null.
*/
@Nullable public Span currentSpan() {
TraceContext context = currentTraceContext.get();
if (context == null) return null;
// Returns a lazy span to reduce overhead when tracer.currentSpan() is invoked just to see if
// one exists, or when the result is never used.
return new LazySpan(this, context);
}
/**
* Returns a new child span if there's a {@link #currentSpan()} or a new trace if there isn't.
*
* <p>Prefer {@link #startScopedSpan(String)} if you are tracing a synchronous function or code
* block.
*/
public Span nextSpan() {
TraceContext parent = currentTraceContext.get();
return parent != null ? newChild(parent) : newTrace();
}
/**
* Returns a new child span if there's a {@link #currentSpan()} or a new trace if there isn't. The
* result is the "current span" until {@link ScopedSpan#finish()} is called.
*
* Here's an example:
* <pre>{@code
* ScopedSpan span = tracer.startScopedSpan("encode");
* try {
* // The span is in "scope" so that downstream code such as loggers can see trace IDs
* return encoder.encode();
* } catch (RuntimeException | Error e) {
* span.error(e); // Unless you handle exceptions, you might not know the operation failed!
* throw e;
* } finally {
* span.finish();
* }
* }</pre>
*/
public ScopedSpan startScopedSpan(String name) {
return startScopedSpanWithParent(name, currentTraceContext.get());
}
/**
* Like {@link #startScopedSpan(String)} except when there is no trace in process, the sampler
* {@link SamplerFunction#trySample(Object) triggers} against the supplied argument.
*
* @param name the {@link Span#name(String) span name}
* @param samplerFunction invoked if there's no {@link CurrentTraceContext#get() current trace}
* @param arg parameter to {@link SamplerFunction#trySample(Object)}
* @see #nextSpan(SamplerFunction, Object)
* @since 5.8
*/
public <T> ScopedSpan startScopedSpan(String name, SamplerFunction<T> samplerFunction, T arg) {
if (name == null) throw new NullPointerException("name == null");
TraceContext parent = currentTraceContext.get();
return newScopedSpan(parent, nextContext(samplerFunction, arg, parent), name);
}
/**
* Like {@link #nextSpan()} except when there is no trace in process, the sampler {@link
* SamplerFunction#trySample(Object) triggers} against the supplied argument.
*
* @param samplerFunction invoked if there's no {@link CurrentTraceContext#get() current trace}
* @param arg parameter to {@link SamplerFunction#trySample(Object)}
* @see #startScopedSpan(String, SamplerFunction, Object)
* @see #nextSpan(TraceContextOrSamplingFlags)
* @since 5.8
*/
public <T> Span nextSpan(SamplerFunction<T> samplerFunction, T arg) {
TraceContext parent = currentTraceContext.get();
return _toSpan(parent, nextContext(samplerFunction, arg, parent));
}
/**
* Like {@link #nextSpan(SamplerFunction, Object)} except this controls the parent context
* explicitly. This is useful when an invocation context is propagated manually, commonly the case
* with asynchronous client frameworks.
*
* @param samplerFunction invoked if there's no {@link CurrentTraceContext#get() current trace}
* @param arg parameter to {@link SamplerFunction#trySample(Object)}
* @param parent of the new span, or {@code null} if it should have no parent
* @see #nextSpan(SamplerFunction, Object)
* @since 5.10
*/
public <T> Span nextSpanWithParent(SamplerFunction<T> samplerFunction, T arg,
@Nullable TraceContext parent) {
return _toSpan(parent, nextContext(samplerFunction, arg, parent));
}
<T> TraceContext nextContext(SamplerFunction<T> samplerFunction, T arg, TraceContext parent) {
if (samplerFunction == null) throw new NullPointerException("samplerFunction == null");
if (arg == null) throw new NullPointerException("arg == null");
if (parent != null) return decorateContext(parent, parent.spanId());
Boolean sampled = samplerFunction.trySample(arg);
SamplingFlags flags = sampled != null ? (sampled ? SAMPLED : NOT_SAMPLED) : EMPTY;
return newRootContext(InternalPropagation.instance.flags(flags));
}
/**
* Same as {@link #startScopedSpan(String)}, except ignores the current trace context.
*
* <p>Use this when you are creating a scoped span in a method block where the parent was
* created. You can also use this to force a new trace by passing null parent.
*/
// this api is needed to make tools such as executors which need to carry the invocation context
public ScopedSpan startScopedSpanWithParent(String name, @Nullable TraceContext parent) {
if (name == null) throw new NullPointerException("name == null");
TraceContext context =
parent != null ? decorateContext(parent, parent.spanId()) : newRootContext(0);
return newScopedSpan(parent, context, name);
}
ScopedSpan newScopedSpan(@Nullable TraceContext parent, TraceContext context, String name) {
Scope scope = currentTraceContext.newScope(context);
if (isNoop(context)) return new NoopScopedSpan(context, scope);
PendingSpan pendingSpan = pendingSpans.getOrCreate(parent, context, true);
Clock clock = pendingSpan.clock();
MutableSpan state = pendingSpan.state();
state.name(name);
return new RealScopedSpan(context, scope, state, clock, pendingSpans);
}
/** A span remains in the scope it was bound to until close is called. */
public static final class SpanInScope implements Closeable {
final Scope scope;
// This type hides the SPI type and allows us to double-check the SPI didn't return null.
SpanInScope(Scope scope) {
if (scope == null) throw new NullPointerException("scope == null");
this.scope = scope;
}
/** No exceptions are thrown when unbinding a span scope. */
@Override public void close() {
scope.close();
}
@Override public String toString() {
return scope.toString();
}
}
@Override public String toString() {
TraceContext currentSpan = currentTraceContext.get();
return "Tracer{"
+ (currentSpan != null ? ("currentSpan=" + currentSpan + ", ") : "")
+ (noop.get() ? "noop=true, " : "")
+ "spanHandler=" + spanHandler + "}";
}
boolean isNoop(TraceContext context) {
if (noop.get()) return true;
int flags = InternalPropagation.instance.flags(context);
if ((flags & FLAG_SAMPLED_LOCAL) == FLAG_SAMPLED_LOCAL) return false;
return (flags & FLAG_SAMPLED) != FLAG_SAMPLED;
}
/** Generates a new 64-bit ID, taking care to dodge zero which can be confused with absent */
long nextId() {
long nextId = Platform.get().randomLong();
while (nextId == 0L) {
nextId = Platform.get().randomLong();
}
return nextId;
}
}