Salta ai contenuti

Telemetria: bridge OpenTelemetry e fallback no-op

Il modulo Telemetry è il bridge OpenTelemetry opzionale del motore. Quando l’SDK OpenTelemetry è installato, emette span e metriche con attributi sanitizzati. Quando non lo è, un insieme completo di oggetti tracer e meter no-op mantiene valide e a costo nullo le chiamate di strumentazione. La strumentazione può quindi rimanere sempre in sicurezza nel percorso del codice.

Terminal window
composer require nextpdf/core:^3

L’obiettivo progettuale è garantire un’osservabilità a costo zero quando OTel è assente. I percorsi critici del motore chiamano un tracer e un meter senza condizioni. Che tali chiamate producano effetti dipende dal runtime, non da un condizionale in ogni punto di chiamata.

OpenTelemetryInterceptor è il bridge. isAvailable() segnala se l’SDK OTel è presente. startSpan(string $name, array $attributes = []) / endSpan(?object $span) racchiudono un’operazione tracciata, mentre recordMetric() registra il valore di un contatore o di un gauge. Quando OTel è assente, l’interceptor si segnala come non disponibile e le chiamate restano inerti. TelemetryBridge collega l’interceptor ai punti di osservazione del motore.

AttributeSanitizer è il livello di sicurezza. sanitize(array $attributes) ripulisce la mappa degli attributi prima che lasci il processo. Gli attributi di telemetria sono un classico canale di PII involontaria, quindi la sanitizzazione fa parte del contratto, non è un’aggiunta successiva. È @since 2.3.0, così come l’interceptor e il bridge.

NullTracer, NullSpanBuilder, NullSpan, NullMeter, NullCounter e NullHistogram sono il fallback no-op. Implementano le stesse forme esposte dall’SDK OTel — spanBuilder(), setAttribute() (concatenabile), startSpan(), end(), createHistogram(), createUpDownCounter(), add(), record() — e non fanno nulla. Poiché il fallback è completo, il codice strumentato non introduce diramazioni in base alla disponibilità; chiama il tracer e il no-op assorbe la chiamata.

ClasseMembri principaliRuolo
OpenTelemetryInterceptorisAvailable(), startSpan(), endSpan(), recordMetric()Bridge span/metric OTel (@since 2.3.0)
TelemetryBridgecablaggio del motoreCollega l’interceptor ai punti di osservazione (@since 2.3.0)
AttributeSanitizersanitize(array $attributes): arrayScrubber degli attributi per proteggere le PII (@since 2.3.0)
NullTracerspanBuilder(string $name): NullSpanBuilderTracer no-op
NullSpanBuildersetAttribute(), startSpan(): NullSpanSpan builder no-op (concatenabile)
NullSpanend()Span no-op
NullMetercreateHistogram(), createUpDownCounter()Meter no-op
NullCounter / NullHistogramadd(), record()Strumenti no-op

Eseguire composer docs:generate-api-php -- --module=Telemetry per ottenere la tabella PHPDoc completa.

Sorgente: 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']);

Quando l’SDK OTel è assente, ogni chiamata qui sopra è un no-op — il codice è identico, il costo è zero.

Avvolgere un rendering con attributi sanitizzati in modo che i metadati forniti dal chiamante non possano trapelare in uno 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);
}
}
}
  • Il fallback no-op è completo per progettazione. Non proteggere la strumentazione con isAvailable() «per risparmiare lavoro». Il no-op è già a costo nullo, e la protezione aggiunge una diramazione che il design esiste per rimuovere.
  • Far passare sempre gli attributi forniti dal chiamante attraverso AttributeSanitizer prima di uno span o di una metrica. Gli attributi di telemetria sono un canale di PII accidentale.
  • endSpan(null) è valido — uno span null è il caso no-op. Abbinare ogni startSpan() a un endSpan() in un finally.
  • NullSpanBuilder::setAttribute() restituisce static per la concatenazione; la catena resta inerte con il no-op, il che è intenzionale, non un bug.
  • Il profilo di riproducibilità è structural: gli span portano timestamp e ID di traccia, quindi due esecuzioni differiscono in tali campi.

Quando OTel è assente, il costo è una chiamata di metodo a un no-op — di fatto gratuito. Quando è presente, il costo è quello dell’SDK OTel, non di questo modulo; il bridge aggiunge la sanitizzazione degli attributi, che è lineare rispetto al numero di attributi. Il performance_budget di 1500 ms wall / 64 MB di picco è il riferimento del motore, non uno SLA di telemetria.

La telemetria è una superficie di uscita dei dati. AttributeSanitizer è il controllo che tiene segreti e PII fuori da span e metriche. Considerare obbligatoria la sanitizzazione per qualsiasi attributo influenzato dal chiamante: è il requisito di telemetria sicura del progetto. L’exporter OTel invia i dati a un backend esterno. Il confine con quel backend è un confine di fiducia. Configurare il suo endpoint e le credenziali da un gestore di segreti, non da configurazione versionata. Si deve presumere che i dati di span e metriche raggiungano un sink di log. Ripulire di conseguenza. Consultare il modello delle minacce del motore in /modules/core/security/.

Questo modulo non formula alcuna asserzione normativa sulla specifica PDF. Funge da bridge verso il modello dati OpenTelemetry, che è una specifica di osservabilità esterna, non una clausola PDF. Il fallback no-op rispecchia la superficie API OTel in modo che il codice strumentato sia portabile; questa è una proprietà di compatibilità API, non una dichiarazione di conformità PDF. La conformità del motore è validata dalle suite oracle e golden descritte in /modules/core/conformance/.