specoto/trace-reference
Composer 安装命令:
composer require specoto/trace-reference
包简介
PSR-22 Application Tracing: reference implementation with exporters, propagation, and in-memory tracers
关键字:
README 文档
README
This repository is a prototype implementation of the PSR-22 Application Tracing draft proposal.
The proposed PSR-22 interfaces live in the separate specoto/trace package; this package
depends on specoto/trace and provides concrete implementations (tracers, span storage, exporters,
propagation). This separation mirrors psr/log / Monolog: the PSR would define the contracts, an
implementation demonstrates them.
Status
PSR-22 is currently Draft (proposed standard). This prototype explores the interface boundaries, tests design decisions, and validates the scope described in the meta document.
Architecture
The PSR-22 draft scopes itself to what library and framework authors need to emit tracing signals, and nothing more. The meta document is explicit:
"This standard aims to create an intentionally minimalist set of interfaces that can be used to provide tracing signals to 3rd party libraries in a unified manner." — PSR-22 Meta, §1
"By taking transmission mechanisms out of scope for this PSR, we drastically increase the simple adoptability of tracing for all parties involved." — PSR-22 Meta, §2
PSR Surface (what goes in the standard)
| Interface | Role |
|---|---|
TracerProviderInterface | Entry point — factory for named tracers |
TracerInterface | Creates spans. No flush() or shutdown() |
SpanInterface | Read/write span data |
SpanContextInterface | Trace identity + W3C traceparent |
SpanKind enum | INTERNAL, CLIENT, SERVER, PRODUCER, CONSUMER |
SpanStatus enum | UNSET, OK, ERROR |
Non-PSR surface (implementation detail, not standardized)
Exporter\*— transmission is out of scopePropagation\*— context propagation is an SDK concernTracingFactory— convenience wiring, not part of PSRTracer\DefaultTracer,InMemoryTracer,NoopTracer— concrete implementationsTracer\SpanContextGeneratorTrait— shared ID generationAttribute\*—#[Traceable]/#[SpanName]attributes +TracingProxyfor declarative tracing
The distinction mirrors the PSR-3 / Monolog relationship: the PSR defines the contract; implementations handle transport, batching, and configuration.
Attribute-based Tracing
This prototype includes a declarative tracing approach using PHP 8.3 attributes.
Any service class can opt into tracing by marking methods with #[Traceable] — no
modifications to the class itself are needed.
Attributes
| Attribute | Scope | Purpose |
|---|---|---|
#[Traceable] | Method | Marks the method for auto-tracing |
#[SpanName] | Method | Overrides the span name (defaults to method name) |
Usage
use Psr\Trace\Attribute\SpanName;
use Psr\Trace\Attribute\Traceable;
class OrderService
{
#[Traceable]
#[SpanName('order.create')]
public function createOrder(string $product, int $qty): array { ... }
#[Traceable]
public function processPayment(int $orderId, float $amount): string { ... }
}
Wrapping
TracingProxy wraps any object and intercepts traced methods via __call + reflection:
use Psr\Trace\Attribute\TracingProxy;
use Psr\Trace\Tracer\InMemoryTracer;
$tracer = new InMemoryTracer('my_app', '1.0.0');
$service = new TracingProxy(new OrderService(), $tracer);
$service->createOrder('Widget', 2); // auto-traced via #[Traceable]
$service->processPayment(1234, 39.99); // auto-traced via #[Traceable]
Real Runtime Data
Each traced span automatically captures the following from reflection:
| Attribute | Source |
|---|---|
class | $method->class — fully-qualified class name |
method | $method->name — the method called |
file | $method->getFileName() — source file path |
line | $method->getStartLine() — method declaration line |
args | Parameter names and values — JSON-encoded |
No hardcoded metadata needed.
Performance
Benchmarked at 10,000 iterations on PHP 8.3 (opcache hot):
| Path | µs/call | Overhead |
|---|---|---|
| Direct call | 0.125 | baseline |
| Proxy (untraced method) | 0.442 | +0.32 µs |
| Proxy (traced + span lifecycle) | 7.567 | +7.44 µs |
The 7.6 µs per traced call is negligible for typical request-scoped tracing
(a few dozen spans per request adds ~150 µs total). The overhead is dominated
by span object allocation and attribute setting — not reflection — making it
suitable for most applications. For hot-path inner loops, skip tracing or
check $span->isRecording() first.
Design Decisions
1. No flush() or shutdown() on TracerInterface
The meta document lists transmission mechanisms as a non-goal:
"This PSR does not define the mechanisms used for transmitting the data to 3rd party systems." — PSR-22 Meta, §3.1 (Non-goals)
flush() and shutdown() imply a lifecycle that knows about exporters, batching, and
connection management — all transmission concerns. Removing them from the interface keeps
the PSR focused on signal emission. Implementations like DefaultTracer still provide them
as concrete methods for callers that need them.
2. toTraceState() removed from SpanContextInterface
The method previously named toTraceState() emitted 00-{trace_id}-{span_id}-{flags} — the
W3C traceparent format, not the W3C tracestate format (vendor1=val1,vendor2=val2).
This naming collision was a bug risk. The method was renamed to toTraceParent() and the
erroneous toTraceState()/fromTraceState() methods were removed. The W3C tracestate is
exposed as a plain ?string property via getTracestate().
3. TracerProviderInterface added
The meta document says:
"This PSR may provide a minimal TraceProvider, etc. for other providers to extend, should they choose." — PSR-22 Meta, §3.1 (Goals)
getTracer(string $name, ?string $version = null) is the minimal surface needed for framework
integration. It mirrors OpenTelemetry's TracerProvider::getTracer() signature.
4. SpanContextGeneratorTrait deduplicates ID generation
Both DefaultTracer and InMemoryTracer share the same logic for generating new trace/span IDs
and building child contexts. A trait avoids forcing an abstract base class while eliminating
the ~50 lines of duplication.
5. W3C traceparent parsing is self-contained in SpanContext
fromTraceParent() no longer delegates to a misnamed method. Parsing is inline and validates
trace ID length (32 hex chars), span ID length (16 hex chars), and the 00- version prefix.
Community Context
PSR-22 vs OpenTelemetry PHP
The OpenTelemetry PHP SDK (open-telemetry/opentelemetry) is the de facto standard
for distributed tracing in PHP (~1.3M installs). It carries a heavy dependency tree
(PSR-7, PSR-17, PSR-18, Symfony HTTP Client, OTLP/Zipkin exporters) and is governed by the
CNCF cross-language specification process.
A PSR-22-like interface would serve a different role, analogous to PSR-3 vs Monolog:
- PSR-22 (if accepted): vendor-neutral interface contract governed by PHP-FIG
- OTel PHP SDK: full-featured implementation with exporters, propagators, and SDK lifecycle
Library authors who only need to emit spans could depend on psr/trace without pulling in
HTTP factories, gRPC exporters, or the CNCF specification weight. Providers (Datadog,
New Relic, Sentry, etc.) could implement the PSR-22 interfaces behind their existing SDKs.
PHP-FIG vs CNCF
The PHP-FIG serves the PHP ecosystem specifically — its mission is interoperability between PHP frameworks and libraries. The CNCF serves the cross-language cloud-native ecosystem. The two are complementary: PSR-22 (if accepted) would define what PHP libraries need, while the OTel spec defines what distributed systems need.
Repository
GitLab: gitlab.com/specoto/trace-reference
Packagist: specoto/trace-reference
Running Tests
ddev exec /var/www/html/vendor/bin/phpunit -c /var/www/html/trace/phpunit.xml.dist
77 tests, 178 assertions (PHP 8.3).
Key Principles
- Minimalist surface: only interfaces that library authors need to emit traces
- No transmission: exporters, batching, and shipping are implementation concerns
- OTel compatible: data model aligns with OpenTelemetry; interface surface is narrower
- No breaking changes for OTel adopters:
DefaultTracerprovidesflush()/shutdown()as concrete methods even though they are not on the interface
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 1
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-17