Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ composer require flow-php/symfony-telemetry-bundle:~--FLOW_PHP_VERSION--
## Core Dependencies

- [flow-php/telemetry](/documentation/installation/packages/telemetry.md)
- [flow-php/symfony-http-foundation-telemetry-bridge](/documentation/installation/packages/symfony-http-foundation-telemetry-bridge.md)
- [symfony/config](https://packagist.org/packages/symfony/config)
- [symfony/console](https://packagist.org/packages/symfony/console)
- [symfony/dependency-injection](https://packagist.org/packages/symfony/dependency-injection)
Expand All @@ -29,6 +28,7 @@ composer require flow-php/symfony-telemetry-bundle:~--FLOW_PHP_VERSION--
## Suggested Dependencies

- [flow-php/symfony-postgresql-bundle](/documentation/installation/packages/symfony-postgresql-bundle.md) — for PostgreSQL database management and migrations with telemetry support
- [flow-php/symfony-http-foundation-telemetry-bridge](/documentation/installation/packages/symfony-http-foundation-telemetry-bridge.md) — for HTTP trace context propagation (extract incoming / inject outgoing W3C trace headers)
- [flow-php/psr18-telemetry-bridge](/documentation/installation/packages/psr18-telemetry-bridge.md) — for PSR-18 HTTP client tracing
- [flow-php/telemetry-otlp-bridge](/documentation/installation/packages/telemetry-otlp-bridge.md) — for OTLP exporter support
- [symfony/messenger](https://packagist.org/packages/symfony/messenger) — for Messenger tracing middleware
Expand Down
22 changes: 20 additions & 2 deletions documentation/upgrading.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@ Please follow the instructions for your specific version to ensure a smooth upgr

### 1) Removal of Elasticsearch Adapter

The Elasticsearch adapter has been removed from Flow PHP and replaced by the [SEAL](https://php-cmsig.github.io/search/) adapter (`flow-php/etl-adapter-seal`), a search engine abstraction layer that supports Elasticsearch, OpenSearch, Meilisearch, Solr, Typesense, Algolia, RediSearch and Loupe.
The Elasticsearch adapter has been removed from Flow PHP and replaced by the [SEAL](https://php-cmsig.github.io/search/)
adapter (`flow-php/etl-adapter-seal`), a search engine abstraction layer that supports Elasticsearch, OpenSearch,
Meilisearch, Solr, Typesense, Algolia, RediSearch and Loupe.

To migrate, install the SEAL adapter together with the engine adapter for your backend:

```
composer require flow-php/etl-adapter-seal cmsig/seal-elasticsearch-adapter
```

Then build a `CmsIg\Seal\Engine` and pass it to `to_seal_upsert()` instead of the previous `to_es_bulk_index()` (or Meilisearch) DSL functions:
Then build a `CmsIg\Seal\Engine` and pass it to `to_seal_upsert()` instead of the previous `to_es_bulk_index()` (or
Meilisearch) DSL functions:

```php
use CmsIg\Seal\Engine;
Expand All @@ -38,6 +41,21 @@ data_frame()
->run();
```

### 2) `flow-php/symfony-telemetry-bundle` -

`flow-php/symfony-http-foundation-telemetry-bridge` is now an optional dependency

| Before | After |
|--------------------------------------|-------------------------------------------------------------|
| installed transitively by the bundle | install explicitly to enable HTTP trace context propagation |

`instrumentation.http_kernel.context_propagation` is silently disabled when the bridge is absent. To keep extracting
incoming and injecting outgoing W3C trace headers:

```
composer require flow-php/symfony-http-foundation-telemetry-bridge
```

---

## Upgrading from 0.39.x to 0.40.x
Expand Down
3 changes: 2 additions & 1 deletion src/bridge/symfony/telemetry-bundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
"require": {
"php": "~8.3.0 || ~8.4.0 || ~8.5.0",
"flow-php/psr3-telemetry-bridge": "self.version",
"flow-php/symfony-http-foundation-telemetry-bridge": "self.version",
"flow-php/telemetry": "self.version",
"psr/clock": "^1.0",
"symfony/config": "^6.4 || ^7.4 || ^8.0",
Expand All @@ -32,6 +31,7 @@
"composer/semver": "^3.4",
"doctrine/dbal": "^4.4",
"flow-php/psr18-telemetry-bridge": "self.version",
"flow-php/symfony-http-foundation-telemetry-bridge": "self.version",
"flow-php/telemetry-otlp-bridge": "self.version",
"nyholm/psr7": "^1.8",
"symfony/framework-bundle": "^6.4 || ^7.4 || ^8.0",
Expand All @@ -44,6 +44,7 @@
"suggest": {
"doctrine/dbal": "Required for Doctrine DBAL tracing (^4.4)",
"flow-php/psr18-telemetry-bridge": "Required for PSR-18 HTTP client tracing",
"flow-php/symfony-http-foundation-telemetry-bridge": "Required for HTTP trace context propagation (extract incoming / inject outgoing W3C trace headers)",
"flow-php/telemetry-otlp-bridge": "Required for OTLP exporter support",
"symfony/messenger": "Required for Messenger tracing middleware",
"symfony/web-profiler-bundle": "Required for the dev-only Flow Telemetry Web Profiler panel",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ final class FlowTelemetryBundle extends AbstractBundle

private const string HTTP_CLIENT_INTERFACE = 'Symfony\\Contracts\\HttpClient\\HttpClientInterface';

private const string HTTP_FOUNDATION_REQUEST_CARRIER = 'Flow\\Bridge\\Symfony\\HttpFoundationTelemetry\\RequestCarrier';

private const string MESSENGER_MIDDLEWARE_INTERFACE = 'Symfony\\Component\\Messenger\\Middleware\\MiddlewareInterface';

private const string PSR18_CLIENT_INTERFACE = 'Psr\\Http\\Client\\ClientInterface';
Expand Down Expand Up @@ -471,7 +473,7 @@ public function configure(DefinitionConfigurator $definition): void
->end()
->end()
->booleanNode('context_propagation')
->info('Enable context propagation from incoming HTTP headers (requires propagator)')
->info('Extract trace context from incoming request headers and inject it into outgoing response headers (requires flow-php/symfony-http-foundation-telemetry-bridge; silently disabled when absent)')
->defaultTrue()
->end()
->end()
Expand Down Expand Up @@ -2537,7 +2539,7 @@ private function registerInstrumentation(array $config, ContainerConfigurator $c
);
$builder->setParameter(
'flow.telemetry.http_kernel.context_propagation',
$httpKernelConfig['context_propagation'] ?? true,
($httpKernelConfig['context_propagation'] ?? true) && class_exists(self::HTTP_FOUNDATION_REQUEST_CARRIER),
);
$container->import(__DIR__ . '/Resources/config/instrumentation/http_kernel.php');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

use Closure;
use DateTimeImmutable;
use Flow\Bridge\Symfony\HttpFoundationTelemetry\RequestCarrier;
use Flow\Bridge\Symfony\HttpFoundationTelemetry\ResponseCarrier;
use Flow\Telemetry\Context\Context;
use Flow\Telemetry\Context\ContextStorage;
use Flow\Telemetry\PackageVersion;
use Flow\Telemetry\Propagation\ArrayCarrier;
use Flow\Telemetry\Propagation\PropagationContext;
use Flow\Telemetry\Propagation\Propagator;
use Flow\Telemetry\Telemetry;
use Flow\Telemetry\Tracer\Span;
Expand All @@ -18,6 +20,7 @@
use Flow\Telemetry\Tracer\Tracer;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Event\RequestEvent;
Expand Down Expand Up @@ -48,7 +51,7 @@ public function __construct(
array $excludePaths,
private ContextStorage $contextStorage,
private Propagator $propagator,
private bool $extractContext = true,
private bool $contextPropagation = true,
) {
$this->excludePathRules = array_map(
static fn(array $config): PathExclusionRule => PathExclusionRule::fromConfig($config),
Expand Down Expand Up @@ -111,7 +114,7 @@ public function onRequest(RequestEvent $event): void
return;
}

if ($event->isMainRequest() && $this->extractContext) {
if ($event->isMainRequest() && $this->contextPropagation) {
$this->extractContextFromRequest($request);
}

Expand Down Expand Up @@ -149,6 +152,10 @@ public function onResponse(ResponseEvent $event): void
} else {
$span->setStatus(SpanStatus::ok());
}

if ($event->isMainRequest() && $this->contextPropagation) {
$this->injectContextIntoResponse($span, $response);
}
}

public function onTerminate(TerminateEvent $event): void
Expand All @@ -173,16 +180,7 @@ public function onTerminate(TerminateEvent $event): void

private function extractContextFromRequest(Request $request): void
{
$headers = [];

foreach ($request->headers->all() as $key => $values) {
if (count($values) > 0 && is_string($values[0])) {
$headers[$key] = $values[0];
}
}

$carrier = new ArrayCarrier($headers);
$propagationContext = $this->propagator->extract($carrier);
$propagationContext = $this->propagator->extract(new RequestCarrier($request));

$spanContext = $propagationContext->spanContext;

Expand All @@ -198,6 +196,13 @@ private function extractContextFromRequest(Request $request): void
}
}

private function injectContextIntoResponse(Span $span, Response $response): void
{
$propagationContext = new PropagationContext($span->context(), $this->contextStorage->current()->baggage);

$this->propagator->inject($propagationContext, new ResponseCarrier($response));
}

/**
* @param array<int, object|string>|callable|object $controller
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,10 @@
{% if collector.metrics is empty %}
<div class="empty"><p>No metrics were recorded for this request.</p></div>
{% else %}
<table>
<style>
.flow-metrics tr.sf-toggle-content.sf-toggle-visible { display: table-row; }
</style>
<table class="flow-metrics">
<thead>
<tr>
<th>Name</th>
Expand All @@ -198,12 +201,16 @@
{% else %}
<a class="btn btn-link text-small sf-toggle" data-toggle-selector="#metric-attr-{{ loop.index }}" data-toggle-alt-content="Hide attributes">Attributes ({{ metric.attributes|length }})</a>
<div id="metric-attr-{{ loop.index }}" class="hidden">
{{ _self.attribute_table(metric.attributes) }}
</div>
{% endif %}
</td>
</tr>
{% if metric.attributes is not empty %}
<tr id="metric-attr-{{ loop.index }}" class="hidden">
<td colspan="6">
{{ _self.attribute_table(metric.attributes) }}
</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -572,4 +572,113 @@ public function test_traces_successful_http_request(): void
static::assertSame('test_index', $attributes['http.route']);
static::assertSame(TestController::class . '::index', $attributes['controller']);
}

public function test_injects_context_into_response_when_propagation_enabled(): void
{
$kernel = $this->bootKernel([
'config' => static function (TestKernel $kernel): void {
$kernel->addTestBundle(FrameworkBundle::class);
$kernel->addTestExtensionConfig('framework', [
'router' => [
'utf8' => true,
'resource' => __DIR__ . '/../../../Fixtures/config/routes.php',
],
'http_method_override' => false,
'handle_all_throwables' => true,
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
'exporter' => 'memory',
],
],
'instrumentation' => [
'http_kernel' => [
'enabled' => true,
'context_propagation' => true,
],
'console' => ['enabled' => false],
'messenger' => false,
],
]);
},
]);

$container = $this->getContainer();

/** @var Router $router */
$router = $container->get('router');
$routes = $router->getRouteCollection();
$routes->add('test_index', new Route('/test', ['_controller' => TestController::class . '::index']));

$request = Request::create('/test', 'GET');
$response = $kernel->handle($request);
$kernel->terminate($request, $response);

static::assertSame(200, $response->getStatusCode());

/** @var MemorySpanProcessor $processor */
$processor = $container->get('flow.telemetry.tracer_provider.processor');
$spans = $processor->endedSpans();

static::assertCount(1, $spans);

$span = $spans[0];
static::assertSame(
"00-{$span->context()->traceId->toHex()}-{$span->context()->spanId->toHex()}-01",
$response->headers->get('traceparent'),
);
}

public function test_does_not_inject_context_into_response_when_propagation_disabled(): void
{
$kernel = $this->bootKernel([
'config' => static function (TestKernel $kernel): void {
$kernel->addTestBundle(FrameworkBundle::class);
$kernel->addTestExtensionConfig('framework', [
'router' => [
'utf8' => true,
'resource' => __DIR__ . '/../../../Fixtures/config/routes.php',
],
'http_method_override' => false,
'handle_all_throwables' => true,
]);
$kernel->addTestExtensionConfig('flow_telemetry', [
'resource' => [],
'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]],
'tracer_provider' => [
'processor' => [
'type' => 'memory',
'exporter' => 'memory',
],
],
'instrumentation' => [
'http_kernel' => [
'enabled' => true,
'context_propagation' => false,
],
'console' => ['enabled' => false],
'messenger' => false,
],
]);
},
]);

$container = $this->getContainer();

/** @var Router $router */
$router = $container->get('router');
$routes = $router->getRouteCollection();
$routes->add('test_index', new Route('/test', ['_controller' => TestController::class . '::index']));

$request = Request::create('/test', 'GET');
$response = $kernel->handle($request);
$kernel->terminate($request, $response);

static::assertSame(200, $response->getStatusCode());
static::assertFalse($response->headers->has('traceparent'));
}
}
Loading