コンテンツにスキップ

OpenTelemetry でレンダリングを観測する

NextPDF は OpenTelemetry の計装を組み込みで提供します。対象は、PDF 生成ライフサイクル全体にわたる 10 個のトレーススパンと 7 個のメトリクスです。この契約は、OTel SDK が存在しない場合のゼロオーバーヘッドかつ設定不要 です。オートロードの失敗もパフォーマンスのペナルティもありません。SDK をインストールし、TracerProvider/MeterProvider をグローバルに登録すれば、同じコードから自動的にエクスポートされます。許可リストベースの AttributeSanitizer がゼロトラストデータポリシーを強制するため、テレメトリーにドキュメントの内容や PII が含まれることはありません。

このページは、トランスポート非依存の OpenTelemetry コンセプト資料に対応する PHP ネイティブ版です。実行可能なサンプルと、それを裏付けるテストで動作を確認します。

Terminal window
composer require nextpdf/core:^3

Core だけで、no-op セーフな計装サーフェスを利用できます。ライブデータをエクスポートするには、SDK と 1 つのエクスポーターを追加します。

Terminal window
composer require open-telemetry/sdk:^1
composer require open-telemetry/exporter-otlp:^1 # or zipkin / prometheus

エントリーポイントは 2 つあります。

  • TelemetryBridge — 静的ファサードです。isAvailable() は OTel の存在チェックを 1 回だけ実行し、その結果をキャッシュします。startSpan() / endSpan() / recordMetric() は、OTel が存在しない場合は短絡して no-op として動作します。これがゼロオーバーヘッドの契約です。OTel が存在しない場合、これらの呼び出しは 1 マイクロ秒を大きく下回って完了します。
  • OpenTelemetryInterceptor — SDK 統合用のパスです。既知の 10 個のスパンを自動的にトレースし、既知の 7 個のメトリクスを記録し、すべての属性を AttributeSanitizer 経由でルーティングします。構築時に SDK の存在を確認し、その結果をキャッシュします。すべての OTel クラス参照はランタイムガードの背後に置かれているため、SDK がなくてもクラスはロードされます。推奨される BatchSpanProcessor の上限値(maxQueueSize=2048maxExportBatchSize=512)は、静的アクセサーとして公開されています。

10 個のスパンは document.buildfont.resolvehtml.parsewriter.serializeimage.decodelayout.passbarcode.encodeform.buildnavigation.buildattachment.embed です。7 個のメトリクスは render.durationrender.page_countrender.warningsrender.memory_peakrender.file_sizerender.font_countrender.image_count です。

AttributeSanitizer は許可リスト専用です。許可されるキーは、pdf.page_countpdf.file_size_bytespdf.output_profilenextpdf.tier といった構造的なメタデータです。生の HTML、PDF バイトストリーム、base64 BLOB、ファイルシステムパスは、無条件に破棄されます。これにより、NIST SP 800-92 ガイダンスの 2 つのポイントが実現されます。テレメトリーは機密性の高い内容を含んではならないこと、そしてテレメトリーの機密性は明示的な制御であることです。

API サーフェスは、NextPDF\Telemetry\TelemetryBridgeNextPDF\Telemetry\OpenTelemetryInterceptorNextPDF\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 パスを実証します。サニタイザーの許可リストが機能することを示し、ハーネスの出力チャネルを尊重します。再現性ハーネスはこのスクリプトを 2 回実行します。

<?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() は最初の呼び出し後にキャッシュされます。以降のスパンとメトリクスの呼び出しは、ブール値を 1 回確認して return するだけです。計装されたサンプルは、SDK が存在しない状態でも最後まで実行されます。
  • OTel ありの場合: BatchSpanProcessor の上限値(maxQueueSize=2048maxExportBatchSize=512)が継続的な負荷下でメモリを抑え、エクスポートはホットパスから外れたままになります。
  • この performance_budgetwall_ms: 3000peak_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 がログコンテキストに適用しているのと同じ規律です。
記述仕様箇条リファレンス 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 の参照は、コンテンツをテレメトリーに含めないという設計意図の根拠であり、準拠の主張ではありません。