Télémétrie : pont OpenTelemetry et repli no-op
Le module Télémétrie est le pont OpenTelemetry facultatif du moteur. Lorsque le SDK OpenTelemetry est installé, il émet des spans et des métriques avec des attributs assainis. Lorsqu’il ne l’est pas, un jeu complet d’objets tracer et meter no-op garde les appels d’instrumentation valides et sans coût. Tu peux donc laisser l’instrumentation en place dans les chemins de code sans risque.
Installation
Section intitulée « Installation »composer require nextpdf/core:^3Vue d’ensemble conceptuelle
Section intitulée « Vue d’ensemble conceptuelle »L’objectif de conception est une observabilité à coût nul lorsqu’elle est absente. Les chemins critiques du moteur appellent un tracer et un meter sans condition. Le fait que ces appels produisent quoi que ce soit dépend de l’environnement d’exécution, et non d’un test à chaque point d’appel.
OpenTelemetryInterceptor est le pont. isAvailable() indique si le SDK OTel est présent. startSpan(string $name, array $attributes = []) / endSpan(?object $span) encadrent une opération tracée, et recordMetric() enregistre la valeur d’un compteur ou d’une jauge. Lorsque OTel est absent, l’intercepteur se déclare indisponible et les appels restent inertes. TelemetryBridge raccorde l’intercepteur aux points d’observation du moteur.
AttributeSanitizer est la couche de sécurité. sanitize(array $attributes) nettoie la carte d’attributs avant qu’elle ne quitte le processus. Les attributs de télémétrie sont un canal classique de fuite accidentelle de DCP ; l’assainissement fait donc partie du contrat, ce n’est pas une option ajoutée après coup. Il est @since 2.3.0, tout comme l’intercepteur et le pont.
NullTracer, NullSpanBuilder, NullSpan, NullMeter, NullCounter et NullHistogram constituent le repli no-op. Ils implémentent les mêmes formes que celles exposées par le SDK OTel — spanBuilder(), setAttribute() (chaînable), startSpan(), end(), createHistogram(), createUpDownCounter(), add(), record() — et ne font rien. Parce que le repli est complet, le code instrumenté n’a pas à se ramifier selon la disponibilité ; il appelle le tracer et le no-op absorbe l’appel.
Surface d’API
Section intitulée « Surface d’API »| Classe | Membres clés | Rôle |
|---|---|---|
OpenTelemetryInterceptor | isAvailable(), startSpan(), endSpan(), recordMetric() | Pont span/metric OTel (@since 2.3.0) |
TelemetryBridge | câblage du moteur | Raccorde l’intercepteur aux points d’observation (@since 2.3.0) |
AttributeSanitizer | sanitize(array $attributes): array | Assainisseur d’attributs sans DCP (@since 2.3.0) |
NullTracer | spanBuilder(string $name): NullSpanBuilder | Tracer no-op |
NullSpanBuilder | setAttribute(), startSpan(): NullSpan | Constructeur de span no-op (chaînable) |
NullSpan | end() | Span no-op |
NullMeter | createHistogram(), createUpDownCounter() | Meter no-op |
NullCounter / NullHistogram | add(), record() | Instruments no-op |
Exécute composer docs:generate-api-php -- --module=Telemetry pour obtenir le tableau PHPDoc complet.
Exemple de code — Démarrage rapide
Section intitulée « Exemple de code — Démarrage rapide »Source : examples/33-opentelemetry-observability.php.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Telemetry\OpenTelemetryInterceptor;
$otel = new OpenTelemetryInterceptor(/* optional OTel tracer/meter */);
$span = $otel->startSpan('pdf.render', ['doc.pages' => 12]);// ... render work ...$otel->endSpan($span);
$otel->recordMetric('pdf.render.bytes', 482_113, ['profile' => 'pdfa4']);Lorsque le SDK OTel est absent, chaque appel ci-dessus est un no-op : le code reste identique, le coût est nul.
Exemple de code — Production
Section intitulée « Exemple de code — Production »Encadre un rendu avec des attributs assainis afin que les métadonnées fournies par l’appelant ne puissent pas se retrouver dans un span.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Telemetry\AttributeSanitizer;use NextPDF\Telemetry\OpenTelemetryInterceptor;
final readonly class InstrumentedRenderer{ public function __construct( private OpenTelemetryInterceptor $otel, private AttributeSanitizer $sanitizer, ) {}
/** * @param callable():string $render Returns the rendered PDF bytes. * @param array<string, mixed> $attributes Caller-supplied span attributes. */ public function render(callable $render, array $attributes): string { $span = $this->otel->startSpan('pdf.render', $this->sanitizer->sanitize($attributes));
try { return $render(); } finally { $this->otel->endSpan($span); } }}Cas limites & pièges
Section intitulée « Cas limites & pièges »- Le repli no-op est complet par conception. Ne protège pas l’instrumentation avec
isAvailable()« pour économiser du travail ». Le no-op ne coûte déjà rien, et le test ajoute précisément l’embranchement que la conception vise à supprimer. - Fais toujours passer les attributs fournis par l’appelant par
AttributeSanitizeravant un span ou une métrique. Les attributs de télémétrie sont un canal de fuite accidentelle de DCP. endSpan(null)est valide — un span nul correspond au cas no-op. Associe chaquestartSpan()à unendSpan()dans unfinally.NullSpanBuilder::setAttribute()retournestaticpour le chaînage ; la chaîne est inerte sous le no-op, ce qui est intentionnel et non un bogue.- Le profil de reproductibilité est
structural: les spans portent des horodatages et des identifiants de trace, donc deux exécutions diffèrent sur ces champs.
Performances
Section intitulée « Performances »Lorsque OTel est absent, le coût se réduit à un appel de méthode vers un no-op — sans coût notable en pratique. Lorsqu’il est présent, le coût est celui du SDK OTel, et non de ce module ; le pont ajoute l’assainissement des attributs, qui est linéaire en fonction du nombre d’attributs. Le performance_budget de 1500 ms d’horloge / 64 Mo en pic est la référence du moteur, et non un SLA de télémétrie.
Notes de sécurité
Section intitulée « Notes de sécurité »La télémétrie est une surface d’exfiltration de données. AttributeSanitizer est le contrôle qui maintient les secrets et les DCP hors des spans et des métriques. Considère l’assainissement comme obligatoire pour tout attribut influencé par l’appelant ; c’est l’exigence du projet pour une télémétrie sûre. L’exportateur OTel envoie les données vers un backend externe. Cette frontière avec le backend est une frontière de confiance. Configure son endpoint et ses identifiants depuis un gestionnaire de secrets, et non depuis une configuration commitée. On doit supposer que les données de span et de métrique atteignent un puits de logs. Nettoie en conséquence. Consulte le modèle de menaces du moteur dans /modules/core/security/.
Conformité
Section intitulée « Conformité »Ce module ne formule aucune affirmation normative sur la spécification PDF. Il fait le pont avec le modèle de données OpenTelemetry, qui est une spécification d’observabilité externe, et non une clause PDF. Le repli no-op reflète la surface d’API OTel afin que le code instrumenté soit portable ; c’est une propriété de compatibilité d’API, et non une déclaration de conformité PDF. La conformité du moteur est validée par l’oracle et les suites golden décrits dans /modules/core/conformance/.
Voir aussi
Section intitulée « Voir aussi »- Module Observabilité — la surface plus large de l’état d’exécution.
- Module Performances — les diagnostics mémoire qui accompagnent la télémétrie.
- Contrats / Observabilité — les contrats d’exception structurée et de dégradation.
- Modèle de sécurité du moteur