テレメトリ:OpenTelemetry ブリッジと no-op フォールバック
テレメトリモジュールは、エンジン向けのオプションの OpenTelemetry ブリッジです。OpenTelemetry SDK がインストールされている場合は、サニタイズ済みの属性とともにスパンとメトリクスを発行します。インストールされていない場合は、完全な no-op の tracer オブジェクトと meter オブジェクト一式により、インストルメンテーションの呼び出しは有効なままコストゼロで維持されます。そのため、インストルメンテーションをコードパスに残しておいても常に安全です。
インストール
「インストール」という見出しのセクションcomposer require nextpdf/core:^3概念の概要
「概念の概要」という見出しのセクション設計目標は、OTel が存在しない場合にゼロコストで済む可観測性です。エンジンのホットパスは、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 がその呼び出しを吸収します。
API サーフェス
「API サーフェス」という見出しのセクション| クラス | 主なメンバー | 役割 |
|---|---|---|
OpenTelemetryInterceptor | isAvailable(), startSpan(), endSpan(), recordMetric() | OTel の span/metric ブリッジ(@since 2.3.0) |
TelemetryBridge | エンジンへの接続 | インターセプターを観測ポイントに接続(@since 2.3.0) |
AttributeSanitizer | sanitize(array $attributes): array | PII に安全な属性スクラバー(@since 2.3.0) |
NullTracer | spanBuilder(string $name): NullSpanBuilder | no-op の tracer |
NullSpanBuilder | setAttribute(), startSpan(): NullSpan | no-op の span builder(チェーン可能) |
NullSpan | end() | no-op の span |
NullMeter | createHistogram(), createUpDownCounter() | no-op の meter |
NullCounter / NullHistogram | add(), 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 です。コードは同じままで、コストはゼロです。
コードサンプル:本番
「コードサンプル:本番」という見出しのセクション呼び出し元が提供したメタデータがスパンに漏れないように、サニタイズ済みの属性でレンダリングをラップします。
<?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 はすでにコストゼロであり、ガードすると、この設計が取り除こうとしている分岐を追加してしまいます。 - 呼び出し元が提供した属性は、スパンやメトリクスに渡す前に必ず
AttributeSanitizerに通してください。テレメトリ属性は偶発的な PII 流出の経路です。 endSpan(null)は有効です。null のスパンは no-op のケースとして扱われます。すべてのstartSpan()をfinally内のendSpan()と対にしてください。NullSpanBuilder::setAttribute()はメソッドチェーン用にstaticを返します。チェーンは no-op では不活性ですが、これはバグではなく意図的な動作です。- 再現性プロファイルは
structuralです。スパンはタイムスタンプとトレース ID を保持するため、2 回の実行ではこれらのフィールドが異なります。
パフォーマンス
「パフォーマンス」という見出しのセクションOTel が存在しない場合、発生するのは no-op へのメソッド呼び出しだけで、実質的にコストはありません。存在する場合、コストはこのモジュールではなく OTel SDK 側で発生します。ブリッジは属性のサニタイズを追加しますが、そのコストは属性数に対して線形です。performance_budget の 1500 ms ウォール / 64 MB ピークはエンジンの基準値であり、テレメトリの SLA ではありません。
セキュリティ上の注意
「セキュリティ上の注意」という見出しのセクションテレメトリは、データを外部へ送出する境界面です。AttributeSanitizer は、機密情報や PII をスパンとメトリクスから排除するための制御です。呼び出し元の影響を受けるすべての属性で、サニタイズを必須として扱ってください。これは、安全なテレメトリに対するプロジェクト上の義務です。OTel エクスポーターは、データを外部バックエンドに送信します。そのバックエンドとの境界は信頼境界です。そのエンドポイントと認証情報は、コミット済みの設定ではなくシークレットマネージャーから構成してください。スパンとメトリクスのデータは、ログシンクに到達すると想定すべきです。その前提でスクラブしてください。エンジンの脅威モデルについては /modules/core/security/ を参照してください。
このモジュールは、PDF 仕様について規範的な主張を一切行いません。これは OpenTelemetry データモデルへのブリッジであり、PDF の条項ではなく外部の可観測性仕様に関するものです。no-op フォールバックは OTel API サーフェスを反映するため、インストルメント化されたコードは移植可能です。これは API 互換性の性質であり、PDF 準拠の表明ではありません。エンジンの準拠は、/modules/core/conformance/ に記載されたオラクルとゴールデンスイートによって検証されます。
- 可観測性モジュール — より広範なランタイム状態の境界。
- パフォーマンスモジュール — テレメトリと併用するメモリ診断。
- 契約 / 可観測性 — 構造化された例外と劣化の契約。
- エンジンセキュリティモデル