ข้ามไปยังเนื้อหา

Telemetry: บริดจ์ OpenTelemetry และตัวเลือกสำรองแบบ no-op

โมดูล Telemetry คือบริดจ์แบบเลือกใช้ของเอนจินสำหรับเชื่อมต่อกับ software development kit (SDK) ของ OpenTelemetry (OTel) เมื่อติดตั้ง SDK แล้ว โมดูลจะปล่อย span และ metric พร้อมแอตทริบิวต์ที่ผ่านการล้างข้อมูลแล้ว เมื่อไม่มี SDK ชุดอ็อบเจกต์ tracer และ meter แบบ no-op ที่ครบถ้วนจะทำให้การเรียกใช้เครื่องมือวัด (instrumentation) ยังคงทำงานได้โดยแทบไม่มีต้นทุน คุณจึงปล่อยให้เครื่องมือวัดอยู่ในเส้นทางโค้ดได้อย่างปลอดภัย

Terminal window
composer require nextpdf/core:^3

เป้าหมายของการออกแบบคือการสังเกตการณ์ (observability) ที่ไม่มีต้นทุนเมื่อไม่มี SDK เส้นทางที่ทำงานบ่อย (hot path) ของเอนจินจะเรียก tracer และ meter โดยไม่ตรวจสอบล่วงหน้า การเรียกเหล่านั้นจะทำงานจริงหรือไม่ขึ้นอยู่กับ runtime ไม่ใช่เงื่อนไขที่จุดเรียกแต่ละจุด

OpenTelemetryInterceptor คือบริดจ์ isAvailable() รายงานว่ามี OTel SDK อยู่หรือไม่ startSpan(string $name, array $attributes = []) / endSpan(?object $span) จะครอบการดำเนินการที่ติดตาม (traced operation) และ recordMetric() จะบันทึกค่าตัวนับ (counter) หรือค่าเกจ (gauge) เมื่อไม่มี OTel interceptor จะรายงานว่าไม่พร้อมใช้งาน และการเรียกต่าง ๆ จะไม่ทำงาน TelemetryBridge เชื่อม interceptor เข้ากับจุดสังเกตการณ์ของเอนจิน

AttributeSanitizer คือชั้นความปลอดภัย sanitize(array $attributes) จะล้างแผนที่แอตทริบิวต์ก่อนที่ข้อมูลจะออกจากกระบวนการ แอตทริบิวต์ของ telemetry เป็นช่องทางที่ข้อมูลซึ่งระบุตัวบุคคลได้ (personally identifiable information หรือ PII) มักหลุดรอดโดยไม่ตั้งใจ ดังนั้นการล้างข้อมูลจึงเป็นส่วนหนึ่งของสัญญา ไม่ใช่ส่วนเสริม ตัวล้างข้อมูล interceptor และบริดจ์มีสถานะ @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()บริดจ์ span และ metric ของ OTel (@since 2.3.0)
TelemetryBridgeการเชื่อมต่อกับเอนจินเชื่อม interceptor เข้ากับจุดสังเกตการณ์ของเอนจิน (@since 2.3.0)
AttributeSanitizersanitize(array $attributes): arrayล้างแอตทริบิวต์เพื่อความปลอดภัยด้าน PII (@since 2.3.0)
NullTracerspanBuilder(string $name): NullSpanBuildertracer แบบ no-op
NullSpanBuildersetAttribute(), startSpan(): NullSpanตัวสร้าง span แบบ no-op (เชื่อมต่อเป็นลูกโซ่ได้)
NullSpanend()span แบบ no-op
NullMetercreateHistogram(), createUpDownCounter()meter แบบ no-op
NullCounter / NullHistogramadd(), record()instrument แบบ no-op

รัน composer docs:generate-api-php -- --module=Telemetry เพื่อสร้างตาราง PHPDoc ฉบับเต็ม

ที่มา: 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 โค้ดยังคงเหมือนเดิมและมีต้นทุนเป็นศูนย์

ครอบการดำเนินการเรนเดอร์ด้วยแอตทริบิวต์ที่ผ่านการล้างข้อมูลแล้ว เพื่อป้องกันไม่ให้ข้อมูลเมทาดาทาที่ผู้เรียกระบุมาหลุดเข้าไปใน 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 ไม่มีต้นทุนอยู่แล้ว และการป้องกันดังกล่าวจะเพิ่มสาขาที่การออกแบบนี้ตัดออกไป
  • ส่งแอตทริบิวต์ที่ผู้เรียกระบุมาผ่าน AttributeSanitizer เสมอ ก่อนที่จะแนบเข้ากับ span หรือ metric แอตทริบิวต์ของ telemetry เป็นช่องทางหลุดรอดของ PII โดยไม่ตั้งใจ
  • endSpan(null) ใช้งานได้: span ที่เป็น null คือกรณี no-op จับคู่ทุก startSpan() กับ endSpan() ใน finally
  • NullSpanBuilder::setAttribute() คืนค่า static เพื่อให้เชื่อมต่อเป็นลูกโซ่ได้ ภายใต้ no-op ลูกโซ่จะไม่ทำงานโดยการออกแบบ
  • โปรไฟล์ความสามารถในการทำซ้ำ (reproducibility) เป็นแบบ structural: span มีการประทับเวลาและตัวระบุการติดตาม (trace identifier) ดังนั้นการรันสองครั้งจะต่างกันในฟิลด์เหล่านั้น

เมื่อไม่มี OTel ต้นทุนคือการเรียกเมท็อดเข้าสู่ no-op ซึ่งแทบไม่มีต้นทุน เมื่อมี OTel ต้นทุนมาจาก OTel SDK ส่วนบริดจ์จะเพิ่มการล้างแอตทริบิวต์ ซึ่งแปรผันเชิงเส้นตามจำนวนแอตทริบิวต์ performance_budget ที่ 1500 ms wall / 64 MB peak คืองบประมาณอ้างอิงของเอนจิน ไม่ใช่ข้อตกลงระดับการให้บริการ (service-level agreement หรือ SLA) ของ telemetry

telemetry เป็นพื้นผิวการส่งออกข้อมูล (data-egress surface) AttributeSanitizer จะกันความลับและ PII ไม่ให้เข้าไปใน span และ metric ถือว่าการล้างข้อมูลเป็นข้อบังคับสำหรับแอตทริบิวต์ใด ๆ ที่ได้รับอิทธิพลจากผู้เรียก นี่คือพันธะด้าน telemetry ที่ปลอดภัยของโครงการ ตัวส่งออก (exporter) ของ OTel จะส่งข้อมูลไปยังแบ็กเอนด์ภายนอก และแบ็กเอนด์นั้นเป็นขอบเขตความเชื่อถือ (trust boundary) กำหนดค่า endpoint และข้อมูลรับรองของแบ็กเอนด์จากตัวจัดการความลับ (secret manager) ไม่ใช่จากคอนฟิกที่คอมมิตไว้ ให้สมมติว่าข้อมูล span และ metric ไปถึงปลายทางบันทึก (log sink) และล้างข้อมูลให้สอดคล้องกัน ดูแบบจำลองภัยคุกคาม (threat model) ของเอนจินได้ที่ /modules/core/security/

โมดูลนี้ไม่ได้กล่าวอ้างเชิงบรรทัดฐานต่อข้อกำหนด Portable Document Format (PDF) โมดูลนี้เป็นบริดจ์ไปยังแบบจำลองข้อมูลของ OpenTelemetry ซึ่งเป็นข้อกำหนดด้านการสังเกตการณ์ภายนอก ไม่ใช่ข้อกำหนด (clause) ของ PDF ตัวเลือกสำรองแบบ no-op สะท้อนพื้นผิว application programming interface (API) ของ OpenTelemetry เพื่อให้โค้ดที่ติดตั้งเครื่องมือวัดยังคงพกพาได้ นั่นเป็นคุณสมบัติด้านความเข้ากันได้ของ API ไม่ใช่คำกล่าวความสอดคล้องของ PDF ความสอดคล้องของเอนจินได้รับการตรวจสอบโดยชุดทดสอบ oracle และ golden ที่อธิบายไว้ใน /modules/core/conformance/