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
|
<?php
namespace Wikimedia\Telemetry;
use Wikimedia\Assert\Assert;
/**
* @since 1.43
* @internal
*/
class Tracer implements TracerInterface {
/**
* The length of a trace ID in bytes, as specified by the OTEL specification.
*/
private const TRACE_ID_BYTES_LENGTH = 16;
/**
* The length of a span ID in bytes, as specified by the OTEL specification.
*/
private const SPAN_ID_BYTES_LENGTH = 8;
private Clock $clock;
private SamplerInterface $sampler;
private ExporterInterface $exporter;
private ContextPropagatorInterface $contextPropagator;
private TracerState $tracerState;
/**
* Whether tracing has been explicitly ended by calling shutdown() on this instance.
* @var bool
*/
private bool $wasShutdown = false;
public function __construct(
Clock $clock,
SamplerInterface $sampler,
ExporterInterface $exporter,
TracerState $tracerState,
ContextPropagatorInterface $contextPropagator
) {
$this->clock = $clock;
$this->sampler = $sampler;
$this->exporter = $exporter;
$this->tracerState = $tracerState;
$this->contextPropagator = $contextPropagator;
}
/** @inheritDoc */
public function createSpan( string $spanName ): SpanInterface {
$activeSpanContext = $this->tracerState->getActiveSpanContext();
// Gracefully handle attempts to instrument code after shutdown() was called.
if ( !$this->wasShutdown ) {
Assert::precondition(
$activeSpanContext !== null,
'Attempted to create a span with the currently active span as the implicit parent, ' .
'but no span was active. Use createRootSpan() to create a span with no parent (i.e. a root span).'
);
}
return $this->newSpan( $spanName, $activeSpanContext );
}
/** @inheritDoc */
public function createRootSpan( string $spanName ): SpanInterface {
return $this->newSpan( $spanName, null );
}
/** @inheritDoc */
public function createSpanWithParent( string $spanName, SpanContext $parentSpanContext ): SpanInterface {
return $this->newSpan( $spanName, $parentSpanContext );
}
/** @inheritDoc */
public function createRootSpanFromCarrier( string $spanName, array $carrier ): SpanInterface {
$spanContext = $this->contextPropagator->extract( $carrier );
return $this->newSpan( $spanName, $spanContext );
}
/** @inheritDoc */
public function getRequestHeaders(): array {
$activeSpanContext = $this->tracerState->getActiveSpanContext();
return $this->contextPropagator->inject( $activeSpanContext, [] );
}
private function newSpan( string $spanName, ?SpanContext $parentSpanContext ): SpanInterface {
$traceId = $parentSpanContext !== null ?
$parentSpanContext->getTraceId() : $this->generateId( self::TRACE_ID_BYTES_LENGTH );
$spanId = $this->generateId( self::SPAN_ID_BYTES_LENGTH );
$sampled = $this->sampler->shouldSample( $parentSpanContext );
$spanContext = new SpanContext(
$traceId,
$spanId,
$parentSpanContext !== null ? $parentSpanContext->getSpanId() : null,
$spanName,
$sampled
);
if ( $this->wasShutdown || !$sampled ) {
return new NoopSpan( $this->tracerState, $spanContext );
}
return new Span(
$this->clock,
$this->tracerState,
$spanContext
);
}
/** @inheritDoc */
public function shutdown(): void {
$this->wasShutdown = true;
$this->exporter->export( $this->tracerState );
}
/**
* Generate a valid hexadecimal string for use as a trace or span ID, with the given length in bytes.
*
* @param int $bytesLength The byte length of the ID
* @return string The ID as a hexadecimal string
*/
private function generateId( int $bytesLength ): string {
return bin2hex( random_bytes( $bytesLength ) );
}
}
|