OpenTelemetry でレンダリングを観測する
NextPDF は OpenTelemetry の計装を組み込みで提供します。対象は、PDF 生成ライフサイクル全体にわたる 10 個のトレーススパンと 7 個のメトリクスです。この契約は、OTel SDK が存在しない場合のゼロオーバーヘッドかつ設定不要 です。オートロードの失敗もパフォーマンスのペナルティもありません。SDK をインストールし、TracerProvider/MeterProvider をグローバルに登録すれば、同じコードから自動的にエクスポートされます。許可リストベースの AttributeSanitizer がゼロトラストデータポリシーを強制するため、テレメトリーにドキュメントの内容や PII が含まれることはありません。
このページは、トランスポート非依存の OpenTelemetry コンセプト資料に対応する PHP ネイティブ版です。実行可能なサンプルと、それを裏付けるテストで動作を確認します。
インストール
「インストール」という見出しのセクションcomposer require nextpdf/core:^3Core だけで、no-op セーフな計装サーフェスを利用できます。ライブデータをエクスポートするには、SDK と 1 つのエクスポーターを追加します。
composer require open-telemetry/sdk:^1composer 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=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 ガイダンスの 2 つのポイントが実現されます。テレメトリーは機密性の高い内容を含んではならないこと、そしてテレメトリーの機密性は明示的な制御であることです。
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 パスを実証します。サニタイザーの許可リストが機能することを示し、ハーネスの出力チャネルを尊重します。再現性ハーネスはこのスクリプトを 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=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 がログコンテキストに適用しているのと同じ規律です。
| 記述 | 仕様 | 箇条 | リファレンス 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 の参照は、コンテンツをテレメトリーに含めないという設計意図の根拠であり、準拠の主張ではありません。