OpenTelemetry로 렌더링 관찰하기
한눈에 보기
섹션 제목: “한눈에 보기”NextPDF는 기본 제공 OpenTelemetry 계측을 함께 제공합니다. PDF 생성 수명 주기 전반에 걸친 10개의 추적 스팬과 7개의 메트릭입니다. 이 계약의 핵심은 OTel SDK가 없을 때 오버헤드가 전혀 없고 구성도 전혀 필요 없다는 점입니다. 즉 자동 로드 실패도, 성능 저하도 없습니다. SDK를 설치하고 TracerProvider/MeterProvider를 전역으로 등록하면 동일한 코드가 자동으로 내보냅니다. 허용 목록 기반의 AttributeSanitizer가 Zero-Trust 데이터 정책을 적용하므로 텔레메트리는 문서 콘텐츠나 PII를 절대 전달하지 않습니다.
이 페이지는 전송 방식에 종속되지 않는 OpenTelemetry 개념 자료에 대응하는 PHP 네이티브 안내서입니다. 실행 가능한 예제와 이를 뒷받침하는 테스트로 이 동작을 검증합니다.
composer require nextpdf/core:^3Core만으로도 no-op으로 안전하게 동작하는 계측 표면을 사용할 수 있습니다. 라이브 데이터를 내보내려면 SDK와 익스포터 하나를 추가합니다.
composer require open-telemetry/sdk:^1composer require open-telemetry/exporter-otlp:^1 # or zipkin / prometheus개념 개요
섹션 제목: “개념 개요”진입점은 두 가지입니다.
TelemetryBridge— 정적 파사드입니다.isAvailable()은 OTel 존재 여부를 한 번 확인하고 그 결과를 캐시합니다.startSpan()/endSpan()/recordMetric()는 OTel이 없을 때 no-op으로 단락(short-circuit) 처리됩니다. 이것이 오버헤드 제로 계약입니다. OTel이 없으면 이러한 호출은 1마이크로초보다 훨씬 짧은 시간에 완료됩니다.OpenTelemetryInterceptor— SDK 통합 경로입니다. 알려진 10개의 스팬을 자동으로 추적하고, 알려진 7개의 메트릭을 기록하며, 모든 속성을AttributeSanitizer를 거쳐 전달합니다. 생성 시점에 SDK의 존재 여부를 확인하고 그 결과를 캐시합니다. 모든 OTel 클래스 참조는 런타임 가드 뒤에 위치하므로, SDK가 없어도 클래스가 로드됩니다. 권장되는BatchSpanProcessor한계값(maxQueueSize=2048,maxExportBatchSize=512)은 정적 접근자로 노출됩니다.
10개의 스팬: document.build, font.resolve, html.parse, writer.serialize, image.decode, layout.pass, barcode.encode, form.build, navigation.build, attachment.embed. 7개의 메트릭: render.duration, render.page_count, render.warnings, render.memory_peak, render.file_size, render.font_count, render.image_count.
AttributeSanitizer는 허용 목록 기반으로만 동작합니다. 허용되는 키는 pdf.page_count, pdf.file_size_bytes, pdf.output_profile, nextpdf.tier 같은 구조적 메타데이터입니다. 원시 HTML, PDF 바이트 스트림, base64 blob, 파일 시스템 경로는 무조건 폐기됩니다. 이는 NIST SP 800-92 지침의 두 가지 요지를 구현한 것입니다. 텔레메트리는 민감한 콘텐츠를 전달해서는 안 되며, 텔레메트리 기밀성은 명시적 제어 항목입니다.
API 표면
섹션 제목: “API 표면”API 표면은 NextPDF\Telemetry\TelemetryBridge, NextPDF\Telemetry\OpenTelemetryInterceptor, NextPDF\Telemetry\AttributeSanitizer의 PHPDoc에서 생성됩니다. 아래에서 사용하는 핵심 멤버는 TelemetryBridge::isAvailable() / startSpan() / endSpan() / recordMetric(), OpenTelemetryInterceptor::knownSpans() / knownMetrics() / maxQueueSize() / maxExportBatchSize(), 그리고 AttributeSanitizer::sanitize()입니다.
코드 예제 — 빠른 시작
섹션 제목: “코드 예제 — 빠른 시작”<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;use NextPDF\Telemetry\TelemetryBridge;
$span = TelemetryBridge::startSpan('document.build', [ 'pdf.page_count' => 1, 'nextpdf.tier' => 'core',]);
$doc = Document::createStandalone();$doc->addPage();$doc->setFont('helvetica', '', 12);$doc->cell(0, 10, 'Observed render');$pdf = $doc->getPdfData();
TelemetryBridge::recordMetric('render.file_size', strlen($pdf), []);TelemetryBridge::endSpan($span); // null-safe when OTel is absent
$doc->save(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/out.pdf');코드 예제 — 프로덕션
섹션 제목: “코드 예제 — 프로덕션”전체 예제는 SDK가 없는 상태로 실행되어 오버헤드 제로 no-op 경로를 입증합니다. 새니타이저의 허용 목록을 검증하고 하네스 출력 채널을 준수합니다. 재현성 하네스는 이 스크립트를 두 번 실행합니다.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;use NextPDF\Telemetry\AttributeSanitizer;use NextPDF\Telemetry\OpenTelemetryInterceptor;use NextPDF\Telemetry\TelemetryBridge;
// Discover the surface — static, dependency-free, SDK-optional.$spans = OpenTelemetryInterceptor::knownSpans();$metrics = OpenTelemetryInterceptor::knownMetrics();
// Zero-Trust Data Policy: the sanitizer drops anything off the allowlist// and anything that looks like a payload.$sanitizer = new AttributeSanitizer();$exported = $sanitizer->sanitize([ 'pdf.page_count' => 1, 'pdf.output_profile' => 'PDF/A-4', 'document.raw_html' => '<html><body>secret</body></html>', // dropped 'source.path' => '/var/secret/invoice.pdf', // dropped]);
$span = TelemetryBridge::startSpan('document.build', [ 'pdf.page_count' => 1, 'nextpdf.tier' => 'core',]);
$doc = Document::createStandalone();$doc->setTitle('Observability demo');$doc->addPage();$doc->setFont('helvetica', 'B', 16);$doc->cell(0, 12, 'Observe rendering with OpenTelemetry', newLine: true);$pdf = $doc->getPdfData();
TelemetryBridge::recordMetric('render.page_count', 1, []);TelemetryBridge::recordMetric('render.file_size', strlen($pdf), []);TelemetryBridge::endSpan($span);
$doc->save(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/out.pdf');
fwrite(STDERR, sprintf( "spans=%d metrics=%d otel_available=%s sanitized_keys=%s\n", count($spans), count($metrics), TelemetryBridge::isAvailable() ? 'yes' : 'no', implode(',', array_keys($exported)),));엣지 케이스 및 주의 사항
섹션 제목: “엣지 케이스 및 주의 사항”- 텔레메트리는 렌더링을 절대 중단시키지 않습니다. 브리지와 인터셉터는 모두 내부 오류를 자체적으로 흡수합니다. 잘못 구성된 익스포터는 관찰 가능성을 저하시킬 뿐, PDF 출력에는 절대 영향을 주지 않습니다. 텔레메트리 실패를 렌더링 실패로 취급하는 catch로 렌더링 코드를 감싸지 마십시오.
endSpan(null)은 안전합니다.startSpan()은 OTel이 없을 때null을 반환하고,endSpan()은null을 no-op으로 받아들입니다. 항상 둘을 짝지어 사용하고, 반환값으로 분기하지 마십시오.- 메트릭에는 등록된
MeterProvider가 필요합니다.TracerProvider만 등록되어 있으면 스팬은 내보내지지만 메트릭은 조용히 건너뜁니다. 메트릭은 권고 사항에 해당합니다. 완전한 커버리지를 위해 두 프로바이더를 모두 등록하십시오. - 새니타이저는 허용 목록 전용입니다. 허용 목록에 없는 새로운 속성 키는 내보내지지 않습니다. 이 동작은 의도된 것입니다. 엔진에서 허용 목록을 확장하고, 새니타이저를 우회하지 마십시오.
- W3C Trace Context 전파. 서비스 간 추적 전파는 W3C Trace Context
traceparent/tracestate헤더를 사용합니다. 이를 처리하는 것은 NextPDF가 아니라 OTel SDK의 프로파게이터입니다. W3C Trace Context 권고안은 검증 코퍼스에 포함되어 있지 않으므로, 이 전파 관련 설명은 RAG로 확인되지 않았으며 규범적 주장이 아니라 통합 안내로 기술됩니다. 사이드카를 참조하십시오. - 재현성 관련 주의 사항. 렌더링은 저장할 때마다
/ID와 수정 날짜가 다시 생성되는 문서를 내보냅니다(ISO 32000-2 §14.3). 캡처된 PDF는 구조적 AST와 메타데이터만 다루는 시맨틱 프로파일과 비교됩니다.
- OTel이 없는 경로:
isAvailable()은 첫 호출 이후 캐시됩니다. 이후의 스팬 및 메트릭 호출은 단일 불리언 확인 후 반환으로 끝납니다. 계측 예제는 SDK가 없는 상태에서도 끝까지 실행됩니다. - OTel을 사용하는 경우:
BatchSpanProcessor한계값(maxQueueSize=2048,maxExportBatchSize=512)이 지속적인 부하에서 메모리를 제한하며, 내보내기는 핫 패스 밖에서 수행됩니다. - 이
performance_budget(wall_ms: 3000,peak_mb: 128)은 임의의 문서가 아니라 하네스 실행에 적용됩니다. - 이 레시피는 #33에 대한 §4.3 격차 목록 커버리지입니다. 이전에는 PHP 네이티브 예제가 없었고, MCP 스타일의 개념 페이지만 존재했습니다. 새로운
examples/33-opentelemetry-observability.php와 이를 뒷받침하는tests/Cookbook/Php/ObserveWithOpenTelemetryRecipeTest.php가 작성되었습니다.
보안 참고 사항
섹션 제목: “보안 참고 사항”- 텔레메트리는 문서 콘텐츠나 PII를 전달해서는 안 됩니다.
AttributeSanitizer허용 목록이 이를 코드 수준에서 강제합니다. 원시 HTML, PDF 스트림, base64 blob, 파일 시스템 경로는 폐기됩니다. 이는 민감한 콘텐츠를 로그와 텔레메트리에서 제외하고 텔레메트리 기밀성을 보호하는 것에 관한 NIST SP 800-92 지침에 부합합니다. - 직접 추가한 속성도 허용 목록의 적용을 받습니다. 허용된 키 아래에 민감한 값을 넣지 않을 책임은 사용자에게 있습니다. 예를 들어 사용자 식별자를
pdf.output_profile에 넣지 마십시오. - 진단 세부 정보는 자유 형식 페이로드가 아니라 구조화된 키로 전달합니다. 이는 PSR-3 §1.2가 로그 컨텍스트에 적용하는 것과 동일한 원칙입니다.
적합성
섹션 제목: “적합성”| 진술 | 규격 | 조항 | reference_id (참조 ID) |
|---|---|---|---|
| 텔레메트리는 민감한 콘텐츠를 전달해서는 안 되며, PII 처리가 필요합니다. | NIST SP 800-92 | §3 | |
| 텔레메트리/로그 기밀성은 명시적 제어 항목입니다. | NIST SP 800-92 | §3 | |
| 세부 정보는 자유 형식 페이로드가 아니라 구조화된 컨텍스트 키로 전달합니다. | PSR-3 | §1.2 | |
출력 /ID와 날짜는 저장할 때마다 다시 생성됨 → 시맨틱 프로파일. | ISO 32000-2 | §14.3 |
이 레시피는 엔지니어링 계측 동작을 설명합니다. 어떠한 규정 준수 인증도 주장하지 않습니다. NIST SP 800-92 참조는 적합성 주장이 아니라 텔레메트리에 콘텐츠를 담지 않는다는 설계 의도를 뒷받침합니다.