26
26
use OpenTelemetry \SemConv \ResourceAttributes ;
27
27
use OpenTelemetry \SemConv \TraceAttributes ;
28
28
use Spryker \Glue \GlueApplication \Rest \ResourceRouter ;
29
+ use Spryker \Service \Kernel \ClassResolver \AbstractClassResolver ;
30
+ use Spryker \Service \Kernel \ClassResolver \Factory \FactoryResolver ;
29
31
use Spryker \Service \Opentelemetry \Instrumentation \Propagation \SymfonyRequestPropagationGetter ;
30
32
use Spryker \Service \Opentelemetry \Instrumentation \Resource \ResourceInfo ;
31
33
use Spryker \Service \Opentelemetry \Instrumentation \Resource \ResourceInfoFactory ;
39
41
use Spryker \Service \Opentelemetry \OpentelemetryInstrumentationConfig ;
40
42
use Spryker \Service \Opentelemetry \Plugin \OpentelemetryMonitoringExtensionPlugin ;
41
43
use Spryker \Service \Opentelemetry \Storage \CustomEventsStorage ;
42
- use Spryker \Service \Opentelemetry \Storage \CustomParameterStorage ;
43
- use Spryker \Service \Opentelemetry \Storage \ExceptionStorage ;
44
- use Spryker \Service \Opentelemetry \Storage \ResourceNameStorage ;
45
- use Spryker \Service \Opentelemetry \Storage \RootSpanNameStorage ;
46
44
use Spryker \Shared \Config \Config ;
47
45
use Spryker \Shared \Opentelemetry \Instrumentation \CachedInstrumentation ;
48
46
use Spryker \Shared \Opentelemetry \OpentelemetryConstants ;
54
52
use Throwable ;
55
53
use function OpenTelemetry \Instrumentation \hook ;
56
54
57
- /**
58
- * @method \Spryker\Service\Opentelemetry\OpentelemetryServiceFactory getFactory()
59
- */
60
55
class SprykerInstrumentationBootstrap
61
56
{
62
57
/**
@@ -65,42 +60,42 @@ class SprykerInstrumentationBootstrap
65
60
public const NAME = 'spryker ' ;
66
61
67
62
/**
68
- * @var string
63
+ * @var string Attribute to mark a trace that is has at least one more span except the root one.
69
64
*/
70
65
public const ATTRIBUTE_IS_DETAILED_TRACE = 'detailed ' ;
71
66
72
67
/**
73
- * @var string
68
+ * @var string Version of the instrumentation. Must be equal to the release version. This version is shown in the traces.
74
69
*/
75
- public const INSTRUMENTATION_VERSION = '1.13 .0 ' ;
70
+ public const INSTRUMENTATION_VERSION = '1.14 .0 ' ;
76
71
77
72
/**
78
- * @var string
73
+ * @var string Default span name placeholder for root spans. By default the first placeholder is for HTTP method and the second one for the route.
79
74
*/
80
75
protected const SPAN_NAME_PLACEHOLDER = '%s %s ' ;
81
76
82
77
/**
83
- * @var string
78
+ * @var string File name of the ZED console binary.
84
79
*/
85
80
protected const ZED_BIN_FILE_NAME = 'console ' ;
86
81
87
82
/**
88
- * @var string
83
+ * @var string Default service name for CLI ZED application.
89
84
*/
90
85
protected const ZED_CLI_APPLICATION_NAME = 'CLI ZED ' ;
91
86
92
87
/**
93
- * @var string
88
+ * @var string Attribute to mark a span as a root span. Required as the span that is considered as root, can be a child span if the trace is collected from multiple services, e.g. Yves call to Zed.
94
89
*/
95
90
protected const ROOT_ATTRIBUTE = 'spr.root ' ;
96
91
97
92
/**
98
- * @var string
93
+ * @var string Attribute to store the number of spans in the trace.
99
94
*/
100
95
protected const ATTRIBUTE_PROCESSED_SPANS = 'spr.processed_spans ' ;
101
96
102
97
/**
103
- * @var string
98
+ * @var string The name of the root span if no other mechanism was executed during the request.
104
99
*/
105
100
protected const FINAL_HTTP_ROOT_SPAN_NAME = '/* ' ;
106
101
@@ -110,20 +105,25 @@ class SprykerInstrumentationBootstrap
110
105
protected static ?ResourceInfo $ resourceInfo = null ;
111
106
112
107
/**
113
- * @var \OpenTelemetry\API\Trace\SpanInterface|null
108
+ * @var \OpenTelemetry\API\Trace\SpanInterface|null In order to always have a reference to the root span, it is stored statically.
114
109
*/
115
110
protected static ?SpanInterface $ rootSpan = null ;
116
111
117
112
/**
118
- * @var int|null
113
+ * @var int|null Will have a value only for CLI applications. It will be set to the return code of the console command.
119
114
*/
120
115
protected static ?int $ cliReturnCode = null ;
121
116
122
117
/**
123
- * @var string|null
118
+ * @var string|null Will have a value only for HTTP requests. It will be set to the route name of the request.
124
119
*/
125
120
protected static ?string $ route = null ;
126
121
122
+ /**
123
+ * @var null|\Spryker\Service\Opentelemetry\OpentelemetryServiceFactory
124
+ */
125
+ protected static $ factory = null ;
126
+
127
127
/**
128
128
* @return void
129
129
*/
@@ -132,10 +132,13 @@ public static function register(): void
132
132
//Disabling error reporting for Otel. Will be overwritten by the application.
133
133
error_reporting (0 );
134
134
135
+ //Get a request
135
136
$ request = RequestProcessor::getRequest ();
136
137
138
+ //Decide whether to sample the request or not
137
139
TraceSampleResult::shouldSample ($ request );
138
140
141
+ //Skip the instrumentation if the request should not be sampled. E.g. the the route is in the stop list.
139
142
if (TraceSampleResult::shouldSkipRootSpan ()) {
140
143
return ;
141
144
}
@@ -145,16 +148,19 @@ public static function register(): void
145
148
146
149
$ tracerProvider = static ::createTracerProvider ($ resource );
147
150
151
+ //Prepare a main SDK instance
148
152
Sdk::builder ()
149
153
->setTracerProvider ($ tracerProvider )
150
154
->setPropagator (TraceContextPropagator::getInstance ())
151
155
->buildAndRegisterGlobal ();
152
156
153
157
static ::registerRootSpan ($ request );
154
158
159
+ // Add a shutdown handler to ensure that the root span is ended and additional attributes are set.
155
160
ShutdownHandler::register ($ tracerProvider ->shutdown (...));
156
161
ShutdownHandler::register ([static ::class, 'shutdownHandler ' ]);
157
162
163
+ //Register the autogenerated and system hooks.
158
164
static ::registerAdditionalHooks ();
159
165
}
160
166
@@ -163,14 +169,18 @@ public static function register(): void
163
169
*/
164
170
protected static function registerAdditionalHooks (): void
165
171
{
172
+ //Will catch return code of the console command.
166
173
static ::registerConsoleReturnCodeHook ();
174
+ //Will catch the route name of the request.
167
175
static ::registerRouteMatcherHooks ();
176
+ //Disable all other hooks
168
177
if (TraceSampleResult::shouldSkipTraceBody ()) {
169
178
putenv ('OTEL_PHP_DISABLED_INSTRUMENTATIONS=all ' );
170
179
171
180
return ;
172
181
}
173
182
183
+ //Autoloading of the generated hooks.
174
184
$ classmapFileName = APPLICATION_ROOT_DIR . '/src/Generated/OpenTelemetry/Hooks/classmap.php ' ;
175
185
if (!file_exists ($ classmapFileName )) {
176
186
return ;
@@ -192,6 +202,8 @@ protected static function registerAdditionalHooks(): void
192
202
}
193
203
194
204
/**
205
+ * Creates a hook to capture the return code of the console command.
206
+ *
195
207
* @return void
196
208
*/
197
209
protected static function registerConsoleReturnCodeHook (): void
@@ -207,6 +219,8 @@ function ($instance, array $params, $returnValue, ?Throwable $exception) {
207
219
}
208
220
209
221
/**
222
+ * Creates hooks to capture the route name of the request. Different hooks are used for different routing systems.
223
+ *
210
224
* @return void
211
225
*/
212
226
protected static function registerRouteMatcherHooks (): void
@@ -246,6 +260,8 @@ function ($instance, array $params, $returnValue, ?Throwable $exception) {
246
260
}
247
261
248
262
/**
263
+ * Prepares a resource with the service name and other attributes.
264
+ *
249
265
* @param string $serviceName
250
266
*
251
267
* @return \Spryker\Service\Opentelemetry\Instrumentation\Resource\ResourceInfo
@@ -276,6 +292,8 @@ protected static function createTracerProvider(ResourceInfo $resource): TracerPr
276
292
}
277
293
278
294
/**
295
+ * Hardcoded span exporter for OTLP gRPC. It is used to export spans to the OpenTelemetry Collector or other OTLP-compatible backends.
296
+ *
279
297
* @return \OpenTelemetry\SDK\Trace\SpanExporterInterface
280
298
*/
281
299
protected static function createSpanExporter (): SpanExporterInterface
@@ -287,6 +305,8 @@ protected static function createSpanExporter(): SpanExporterInterface
287
305
}
288
306
289
307
/**
308
+ * Creates a span processor with a post-filtering mechanism that filters out spans that are faster than the configured threshold.
309
+ *
290
310
* @return \OpenTelemetry\SDK\Trace\SpanProcessorInterface
291
311
*/
292
312
protected static function createSpanProcessor (): SpanProcessorInterface
@@ -408,6 +428,8 @@ protected static function addCliAttributes(SpanInterface $span, Request $request
408
428
}
409
429
410
430
/**
431
+ * Creates a getter for the request propagator. It cannot be created via factory as root span is created before the factory is initialized.
432
+ *
411
433
* @return \OpenTelemetry\Context\Propagation\PropagationGetterInterface
412
434
*/
413
435
protected static function createRequestPropagatorGetter (): PropagationGetterInterface
@@ -434,8 +456,8 @@ protected static function formatSpanName(Request $request, bool $isApplicationAv
434
456
public static function shutdownHandler (): void
435
457
{
436
458
$ request = RequestProcessor::getRequest ();
437
- $ resourceName = ResourceNameStorage:: getInstance ()->getName ();
438
- $ customParamsStorage = CustomParameterStorage:: getInstance ();
459
+ $ resourceName = static :: getFactory ()-> createResourceNameStorage ()->getName ();
460
+ $ customParamsStorage = static :: getFactory ()-> createCustomParameterStorage ();
439
461
$ cli = $ customParamsStorage ->getAttribute (OpentelemetryMonitoringExtensionPlugin::ATTRIBUTE_IS_CONSOLE_COMMAND );
440
462
if ($ resourceName ) {
441
463
if ($ cli ) {
@@ -457,35 +479,81 @@ public static function shutdownHandler(): void
457
479
$ span = static ::addHttpAttributes ($ span , $ request );
458
480
}
459
481
460
- $ name = RootSpanNameStorage::getInstance ()->getName ();
482
+ $ span = static ::updateRootSpanName ($ span , $ request );
483
+ $ span = static ::recordExceptions ($ span );
484
+ $ span = static ::recordEvents ($ span );
485
+ $ span = static ::recordCountOfProcessesSpans ($ span );
486
+
487
+ $ span ->setStatus (static ::getSpanStatus ());
488
+
489
+ $ span ->setAttributes ($ customParamsStorage ->getAttributes ());
490
+ $ span ->end ();
491
+ }
492
+
493
+ /**
494
+ * @param \OpenTelemetry\API\Trace\SpanInterface $span
495
+ * @param \Symfony\Component\HttpFoundation\Request $request
496
+ *
497
+ * @return \OpenTelemetry\API\Trace\SpanInterface
498
+ */
499
+ protected static function updateRootSpanName (SpanInterface $ span , Request $ request ): SpanInterface
500
+ {
501
+ $ name = static ::getFactory ()->createRootSpanNameStorage ()->getName ();
461
502
$ span ->updateName ($ name ?: static ::formatGeneralSpanName ($ request , true ));
462
503
463
- $ exceptions = ExceptionStorage::getInstance ()->getExceptions ();
504
+ return $ span ;
505
+ }
506
+
507
+ /**
508
+ * @param \OpenTelemetry\API\Trace\SpanInterface $span
509
+ *
510
+ * @return \OpenTelemetry\API\Trace\SpanInterface
511
+ */
512
+ protected static function recordExceptions (SpanInterface $ span ): SpanInterface
513
+ {
514
+ $ exceptions = static ::getFactory ()->createExceptionStorage ()->getExceptions ();
464
515
foreach ($ exceptions as $ exception ) {
465
516
$ span ->recordException ($ exception );
466
517
}
467
518
468
- $ events = CustomEventsStorage::getInstance ()->getEvents ();
469
- foreach ($ events as $ eventName => $ eventAttributes ) {
470
- $ span ->addEvent ($ eventName , $ eventAttributes );
519
+ return $ span ;
520
+ }
521
+
522
+ /**
523
+ * @param \OpenTelemetry\API\Trace\SpanInterface $span
524
+ *
525
+ * @return \OpenTelemetry\API\Trace\SpanInterface
526
+ */
527
+ protected static function recordEvents (SpanInterface $ span ): SpanInterface
528
+ {
529
+ $ events = static ::getFactory ()->createCustomEventsStorage ()->getEvents ();
530
+ foreach ($ events as $ eventAttributes ) {
531
+ $ span ->addEvent ($ eventAttributes [CustomEventsStorage::EVENT_NAME ], $ eventAttributes [CustomEventsStorage::EVENT_ATTRIBUTES ]);
471
532
}
472
533
534
+ return $ span ;
535
+ }
536
+
537
+ /**
538
+ * @param \OpenTelemetry\API\Trace\SpanInterface $span
539
+ *
540
+ * @return \OpenTelemetry\API\Trace\SpanInterface
541
+ */
542
+ protected static function recordCountOfProcessesSpans (SpanInterface $ span ): SpanInterface
543
+ {
473
544
$ processedSpans = PostFilterBatchSpanProcessor::getNumberOfProcessedSpans ();
474
545
$ span ->setAttribute (static ::ATTRIBUTE_PROCESSED_SPANS , $ processedSpans );
475
546
$ span ->setAttribute (static ::ATTRIBUTE_IS_DETAILED_TRACE , $ processedSpans >= 1 );
476
547
477
- $ span ->setStatus (static ::getSpanStatus ());
478
-
479
- $ span ->setAttributes ($ customParamsStorage ->getAttributes ());
480
- $ span ->end ();
548
+ return $ span ;
481
549
}
482
550
483
551
/**
484
552
* @return string
485
553
*/
486
554
protected static function getSpanStatus (): string
487
555
{
488
- $ exceptions = ExceptionStorage:: getInstance ()->getExceptions ();
556
+ $ exceptions = static :: getFactory ()-> createExceptionStorage ()->getExceptions ();
489
557
490
558
if ($ exceptions !== []) {
491
559
return StatusCode::STATUS_ERROR ;
@@ -518,4 +586,26 @@ protected static function formatGeneralSpanName(Request $request, bool $isApplic
518
586
519
587
return static ::formatSpanName ($ request , $ isApplicationAvailable );
520
588
}
589
+
590
+ /**
591
+ * Gets a service factory instance. Should be used with caution, as if it called to early, it may fail as Spryker application was not boot properly yet.
592
+ *
593
+ * @return \Spryker\Service\Opentelemetry\OpentelemetryServiceFactory
594
+ */
595
+ protected static function getFactory ()
596
+ {
597
+ if (static ::$ factory === null ) {
598
+ static ::$ factory = static ::getFactoryResolver ()->resolve (static ::class);
599
+ }
600
+
601
+ return static ::$ factory ;
602
+ }
603
+
604
+ /**
605
+ * @return \Spryker\Service\Kernel\ClassResolver\AbstractClassResolver
606
+ */
607
+ protected static function getFactoryResolver (): AbstractClassResolver
608
+ {
609
+ return new FactoryResolver ();
610
+ }
521
611
}
0 commit comments