Skip to content

Commit 62bdd44

Browse files
authored
Merge pull request #28 from spryker/bugfix/frw-10463-fixed-events-sorage
FRW-10463 Fixed events storage
2 parents a8815b3 + 209c052 commit 62bdd44

File tree

4 files changed

+159
-37
lines changed

4 files changed

+159
-37
lines changed

src/Spryker/Service/Opentelemetry/Instrumentation/GuzzleInstrumentation.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ public static function register(): void
107107
if ($exception) {
108108
$span->recordException($exception);
109109
$span->setStatus(StatusCode::STATUS_ERROR);
110+
if ($exception instanceof BadResponseException) {
111+
$response = $exception->getResponse();
112+
$span->setAttribute(TraceAttributes::HTTP_RESPONSE_STATUS_CODE, $response->getStatusCode());
113+
}
110114
$span->end();
111115

112116
return;

src/Spryker/Service/Opentelemetry/Instrumentation/Span/Span.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ public function addEvent(string $name, iterable $attributes = [], ?int $timestam
229229
return $this;
230230
}
231231
if (++$this->totalRecordedEvents > $this->spanLimits->getEventCountLimit()) {
232-
return $this;
232+
array_shift($this->events);
233233
}
234234

235235
$timestamp ??= Clock::getDefault()->now();
@@ -253,7 +253,7 @@ public function recordException(Throwable $exception, iterable $attributes = [],
253253
return $this;
254254
}
255255
if (++$this->totalRecordedEvents > $this->spanLimits->getEventCountLimit()) {
256-
return $this;
256+
array_shift($this->events);
257257
}
258258

259259
$timestamp ??= Clock::getDefault()->now();

src/Spryker/Service/Opentelemetry/Instrumentation/SprykerInstrumentationBootstrap.php

Lines changed: 121 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
use OpenTelemetry\SemConv\ResourceAttributes;
2727
use OpenTelemetry\SemConv\TraceAttributes;
2828
use Spryker\Glue\GlueApplication\Rest\ResourceRouter;
29+
use Spryker\Service\Kernel\ClassResolver\AbstractClassResolver;
30+
use Spryker\Service\Kernel\ClassResolver\Factory\FactoryResolver;
2931
use Spryker\Service\Opentelemetry\Instrumentation\Propagation\SymfonyRequestPropagationGetter;
3032
use Spryker\Service\Opentelemetry\Instrumentation\Resource\ResourceInfo;
3133
use Spryker\Service\Opentelemetry\Instrumentation\Resource\ResourceInfoFactory;
@@ -39,10 +41,6 @@
3941
use Spryker\Service\Opentelemetry\OpentelemetryInstrumentationConfig;
4042
use Spryker\Service\Opentelemetry\Plugin\OpentelemetryMonitoringExtensionPlugin;
4143
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;
4644
use Spryker\Shared\Config\Config;
4745
use Spryker\Shared\Opentelemetry\Instrumentation\CachedInstrumentation;
4846
use Spryker\Shared\Opentelemetry\OpentelemetryConstants;
@@ -54,9 +52,6 @@
5452
use Throwable;
5553
use function OpenTelemetry\Instrumentation\hook;
5654

57-
/**
58-
* @method \Spryker\Service\Opentelemetry\OpentelemetryServiceFactory getFactory()
59-
*/
6055
class SprykerInstrumentationBootstrap
6156
{
6257
/**
@@ -65,42 +60,42 @@ class SprykerInstrumentationBootstrap
6560
public const NAME = 'spryker';
6661

6762
/**
68-
* @var string
63+
* @var string Attribute to mark a trace that is has at least one more span except the root one.
6964
*/
7065
public const ATTRIBUTE_IS_DETAILED_TRACE = 'detailed';
7166

7267
/**
73-
* @var string
68+
* @var string Version of the instrumentation. Must be equal to the release version. This version is shown in the traces.
7469
*/
75-
public const INSTRUMENTATION_VERSION = '1.13.0';
70+
public const INSTRUMENTATION_VERSION = '1.14.0';
7671

7772
/**
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.
7974
*/
8075
protected const SPAN_NAME_PLACEHOLDER = '%s %s';
8176

8277
/**
83-
* @var string
78+
* @var string File name of the ZED console binary.
8479
*/
8580
protected const ZED_BIN_FILE_NAME = 'console';
8681

8782
/**
88-
* @var string
83+
* @var string Default service name for CLI ZED application.
8984
*/
9085
protected const ZED_CLI_APPLICATION_NAME = 'CLI ZED';
9186

9287
/**
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.
9489
*/
9590
protected const ROOT_ATTRIBUTE = 'spr.root';
9691

9792
/**
98-
* @var string
93+
* @var string Attribute to store the number of spans in the trace.
9994
*/
10095
protected const ATTRIBUTE_PROCESSED_SPANS = 'spr.processed_spans';
10196

10297
/**
103-
* @var string
98+
* @var string The name of the root span if no other mechanism was executed during the request.
10499
*/
105100
protected const FINAL_HTTP_ROOT_SPAN_NAME = '/*';
106101

@@ -110,20 +105,25 @@ class SprykerInstrumentationBootstrap
110105
protected static ?ResourceInfo $resourceInfo = null;
111106

112107
/**
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.
114109
*/
115110
protected static ?SpanInterface $rootSpan = null;
116111

117112
/**
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.
119114
*/
120115
protected static ?int $cliReturnCode = null;
121116

122117
/**
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.
124119
*/
125120
protected static ?string $route = null;
126121

122+
/**
123+
* @var null|\Spryker\Service\Opentelemetry\OpentelemetryServiceFactory
124+
*/
125+
protected static $factory = null;
126+
127127
/**
128128
* @return void
129129
*/
@@ -132,10 +132,13 @@ public static function register(): void
132132
//Disabling error reporting for Otel. Will be overwritten by the application.
133133
error_reporting(0);
134134

135+
//Get a request
135136
$request = RequestProcessor::getRequest();
136137

138+
//Decide whether to sample the request or not
137139
TraceSampleResult::shouldSample($request);
138140

141+
//Skip the instrumentation if the request should not be sampled. E.g. the the route is in the stop list.
139142
if (TraceSampleResult::shouldSkipRootSpan()) {
140143
return;
141144
}
@@ -145,16 +148,19 @@ public static function register(): void
145148

146149
$tracerProvider = static::createTracerProvider($resource);
147150

151+
//Prepare a main SDK instance
148152
Sdk::builder()
149153
->setTracerProvider($tracerProvider)
150154
->setPropagator(TraceContextPropagator::getInstance())
151155
->buildAndRegisterGlobal();
152156

153157
static::registerRootSpan($request);
154158

159+
// Add a shutdown handler to ensure that the root span is ended and additional attributes are set.
155160
ShutdownHandler::register($tracerProvider->shutdown(...));
156161
ShutdownHandler::register([static::class, 'shutdownHandler']);
157162

163+
//Register the autogenerated and system hooks.
158164
static::registerAdditionalHooks();
159165
}
160166

@@ -163,14 +169,18 @@ public static function register(): void
163169
*/
164170
protected static function registerAdditionalHooks(): void
165171
{
172+
//Will catch return code of the console command.
166173
static::registerConsoleReturnCodeHook();
174+
//Will catch the route name of the request.
167175
static::registerRouteMatcherHooks();
176+
//Disable all other hooks
168177
if (TraceSampleResult::shouldSkipTraceBody()) {
169178
putenv('OTEL_PHP_DISABLED_INSTRUMENTATIONS=all');
170179

171180
return;
172181
}
173182

183+
//Autoloading of the generated hooks.
174184
$classmapFileName = APPLICATION_ROOT_DIR . '/src/Generated/OpenTelemetry/Hooks/classmap.php';
175185
if (!file_exists($classmapFileName)) {
176186
return;
@@ -192,6 +202,8 @@ protected static function registerAdditionalHooks(): void
192202
}
193203

194204
/**
205+
* Creates a hook to capture the return code of the console command.
206+
*
195207
* @return void
196208
*/
197209
protected static function registerConsoleReturnCodeHook(): void
@@ -207,6 +219,8 @@ function ($instance, array $params, $returnValue, ?Throwable $exception) {
207219
}
208220

209221
/**
222+
* Creates hooks to capture the route name of the request. Different hooks are used for different routing systems.
223+
*
210224
* @return void
211225
*/
212226
protected static function registerRouteMatcherHooks(): void
@@ -246,6 +260,8 @@ function ($instance, array $params, $returnValue, ?Throwable $exception) {
246260
}
247261

248262
/**
263+
* Prepares a resource with the service name and other attributes.
264+
*
249265
* @param string $serviceName
250266
*
251267
* @return \Spryker\Service\Opentelemetry\Instrumentation\Resource\ResourceInfo
@@ -276,6 +292,8 @@ protected static function createTracerProvider(ResourceInfo $resource): TracerPr
276292
}
277293

278294
/**
295+
* Hardcoded span exporter for OTLP gRPC. It is used to export spans to the OpenTelemetry Collector or other OTLP-compatible backends.
296+
*
279297
* @return \OpenTelemetry\SDK\Trace\SpanExporterInterface
280298
*/
281299
protected static function createSpanExporter(): SpanExporterInterface
@@ -287,6 +305,8 @@ protected static function createSpanExporter(): SpanExporterInterface
287305
}
288306

289307
/**
308+
* Creates a span processor with a post-filtering mechanism that filters out spans that are faster than the configured threshold.
309+
*
290310
* @return \OpenTelemetry\SDK\Trace\SpanProcessorInterface
291311
*/
292312
protected static function createSpanProcessor(): SpanProcessorInterface
@@ -408,6 +428,8 @@ protected static function addCliAttributes(SpanInterface $span, Request $request
408428
}
409429

410430
/**
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+
*
411433
* @return \OpenTelemetry\Context\Propagation\PropagationGetterInterface
412434
*/
413435
protected static function createRequestPropagatorGetter(): PropagationGetterInterface
@@ -434,8 +456,8 @@ protected static function formatSpanName(Request $request, bool $isApplicationAv
434456
public static function shutdownHandler(): void
435457
{
436458
$request = RequestProcessor::getRequest();
437-
$resourceName = ResourceNameStorage::getInstance()->getName();
438-
$customParamsStorage = CustomParameterStorage::getInstance();
459+
$resourceName = static::getFactory()->createResourceNameStorage()->getName();
460+
$customParamsStorage = static::getFactory()->createCustomParameterStorage();
439461
$cli = $customParamsStorage->getAttribute(OpentelemetryMonitoringExtensionPlugin::ATTRIBUTE_IS_CONSOLE_COMMAND);
440462
if ($resourceName) {
441463
if ($cli) {
@@ -457,35 +479,81 @@ public static function shutdownHandler(): void
457479
$span = static::addHttpAttributes($span, $request);
458480
}
459481

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();
461502
$span->updateName($name ?: static::formatGeneralSpanName($request, true));
462503

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();
464515
foreach ($exceptions as $exception) {
465516
$span->recordException($exception);
466517
}
467518

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]);
471532
}
472533

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+
{
473544
$processedSpans = PostFilterBatchSpanProcessor::getNumberOfProcessedSpans();
474545
$span->setAttribute(static::ATTRIBUTE_PROCESSED_SPANS, $processedSpans);
475546
$span->setAttribute(static::ATTRIBUTE_IS_DETAILED_TRACE, $processedSpans >= 1);
476547

477-
$span->setStatus(static::getSpanStatus());
478-
479-
$span->setAttributes($customParamsStorage->getAttributes());
480-
$span->end();
548+
return $span;
481549
}
482550

483551
/**
484552
* @return string
485553
*/
486554
protected static function getSpanStatus(): string
487555
{
488-
$exceptions = ExceptionStorage::getInstance()->getExceptions();
556+
$exceptions = static::getFactory()->createExceptionStorage()->getExceptions();
489557

490558
if ($exceptions !== []) {
491559
return StatusCode::STATUS_ERROR;
@@ -518,4 +586,26 @@ protected static function formatGeneralSpanName(Request $request, bool $isApplic
518586

519587
return static::formatSpanName($request, $isApplicationAvailable);
520588
}
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+
}
521611
}

0 commit comments

Comments
 (0)