콘텐츠로 이동

텔레메트리: OpenTelemetry 브리지와 no-op 폴백

텔레메트리 모듈은 엔진의 선택적 OpenTelemetry 브리지입니다. OpenTelemetry SDK가 설치되어 있으면 정제된 속성과 함께 span과 metric을 내보냅니다. 설치되어 있지 않으면 완전한 no-op tracer 및 meter 객체 집합이 계측 호출을 유효하게 유지하면서도 비용이 들지 않게 합니다. 따라서 계측 코드를 실행 경로에 그대로 남겨 두어도 항상 안전합니다.

Terminal window
composer require nextpdf/core:^3

설계 목표는 부재 시 비용이 전혀 들지 않는 관측 가능성입니다. 엔진의 핫 경로는 tracer와 meter를 조건 없이 호출합니다. 이러한 호출이 실제로 어떤 작업을 수행하는지는 각 호출 지점의 조건문이 아니라 런타임에 따라 달라집니다.

OpenTelemetryInterceptor가 브리지입니다. isAvailable()는 OTel SDK의 존재 여부를 보고합니다. startSpan(string $name, array $attributes = []) / endSpan(?object $span)는 추적 대상 작업을 감싸고, recordMetric()는 카운터 또는 게이지 값을 기록합니다. OTel이 없으면 인터셉터는 사용할 수 없다고 보고하며 호출은 아무 동작도 하지 않습니다. TelemetryBridge는 인터셉터를 엔진의 관측 지점에 연결합니다.

AttributeSanitizer는 안전 계층입니다. sanitize(array $attributes)는 속성 맵이 프로세스를 벗어나기 전에 정제합니다. 텔레메트리 속성은 흔한 우발적 PII 유출 경로이므로, 정제는 부가 기능이 아니라 계약의 일부입니다. 이 역시 인터셉터 및 브리지와 마찬가지로 @since 2.3.0입니다.

NullTracer, NullSpanBuilder, NullSpan, NullMeter, NullCounter, NullHistogram은 no-op 폴백입니다. 이들은 OTel SDK가 노출하는 것과 동일한 형태 — spanBuilder(), setAttribute()(체이닝 가능), startSpan(), end(), createHistogram(), createUpDownCounter(), add(), record() — 를 구현하지만 아무 동작도 하지 않습니다. 폴백이 완전하기 때문에 계측된 코드는 가용성에 따라 분기하지 않습니다. 코드는 tracer를 호출하고 no-op이 그 호출을 흡수합니다.

클래스주요 멤버역할
OpenTelemetryInterceptorisAvailable(), startSpan(), endSpan(), recordMetric()OTel span/metric 브리지 (@since 2.3.0)
TelemetryBridge엔진 연결인터셉터를 관측 지점에 연결합니다 (@since 2.3.0)
AttributeSanitizersanitize(array $attributes): arrayPII에 안전한 속성 스크러버 (@since 2.3.0)
NullTracerspanBuilder(string $name): NullSpanBuilderNo-op tracer
NullSpanBuildersetAttribute(), startSpan(): NullSpanNo-op span 빌더 (체이닝 가능)
NullSpanend()No-op span
NullMetercreateHistogram(), createUpDownCounter()No-op meter
NullCounter / NullHistogramadd(), record()No-op 계측기

전체 PHPDoc 표는 composer docs:generate-api-php -- --module=Telemetry를 실행해서 확인하십시오.

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

OTel SDK가 없으면 위의 모든 호출은 no-op입니다 — 코드는 동일하며 비용은 0입니다.

호출자가 제공한 메타데이터가 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);
}
}
}
  • no-op 폴백은 설계상 완전합니다. “작업을 줄이려고” isAvailable()로 계측을 감싸지 마십시오. no-op은 이미 아무 비용도 들지 않으며, 이러한 가드는 설계가 제거하려는 분기를 오히려 추가합니다.
  • 호출자가 제공한 속성은 span이나 metric에 사용하기 전에 항상 AttributeSanitizer를 거치게 하십시오. 텔레메트리 속성은 우발적 PII 유출 경로입니다.
  • endSpan(null)은 유효합니다 — null span은 no-op 사례입니다. 모든 startSpan()finally 블록 안의 endSpan()과 짝을 이루게 하십시오.
  • NullSpanBuilder::setAttribute()는 체이닝을 위해 static을 반환합니다. no-op에서는 체인이 아무 동작도 하지 않으며, 이는 의도된 것이지 버그가 아닙니다.
  • 재현성 프로파일은 structural입니다. span은 타임스탬프와 trace id를 담고 있으므로, 두 번 실행하면 해당 필드에 차이가 납니다.

OTel이 없을 때 비용은 no-op으로의 메서드 호출뿐이며 — 사실상 무료입니다. OTel이 있을 때 비용은 이 모듈이 아니라 OTel SDK의 비용입니다. 브리지는 속성 정제를 추가하며, 이는 속성 개수에 대해 선형입니다. 1500 ms wall / 64 MB 피크의 performance_budget는 엔진 기준값이며, 텔레메트리 SLA가 아닙니다.

텔레메트리는 데이터 유출 표면입니다. AttributeSanitizer는 비밀 정보와 PII가 span 및 metric에 들어가지 않도록 막는 제어 장치입니다. 호출자의 영향을 받는 모든 속성에 대해 정제를 필수로 취급하십시오. 이는 프로젝트의 안전한 텔레메트리 의무입니다. OTel 익스포터는 데이터를 외부 백엔드로 전송합니다. 그 백엔드 경계는 신뢰 경계입니다. 엔드포인트와 자격 증명은 커밋된 설정이 아니라 시크릿 매니저에서 구성하십시오. span 및 metric 데이터는 로그 싱크에 도달한다고 가정해야 합니다. 그에 맞게 스크러빙하십시오. 엔진 위협 모델은 /modules/core/security/를 참조하십시오.

이 모듈은 PDF 사양에 대해 어떠한 규범적 주장도 하지 않습니다. 이 모듈은 OpenTelemetry 데이터 모델에 연결되며, 이는 PDF 조항이 아니라 외부 관측 가능성 사양입니다. no-op 폴백은 OTel API 표면을 그대로 반영하므로 계측된 코드는 이식 가능합니다. 이는 API 호환성 속성이지 PDF 적합성 선언이 아닙니다. 엔진 적합성은 /modules/core/conformance/에 설명된 오라클 및 골든 스위트로 검증됩니다.