Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Gen Otlp Protobuf Laravel Package

open-telemetry/gen-otlp-protobuf

Generated OTLP protobuf classes for OpenTelemetry PHP, enabling efficient trace, metric, and log export. Requires google/protobuf, with the optional PHP C extension recommended for production performance.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a low-level dependency: it ships generated PHP classes for the OTLP protobuf schema (Opentelemetry\Proto\...) and nothing else. In practice you almost never composer require it directly — it arrives transitively with the exporter that actually uses it:

composer require open-telemetry/exporter-otlp
composer show open-telemetry/gen-otlp-protobuf   # confirm version resolved

It requires google/protobuf (the native PHP runtime). For anything beyond a local experiment, install the C extension — same API, dramatically faster serialization, and it takes precedence automatically when loaded:

pecl install protobuf
# then enable extension=protobuf.so in your php.ini
php -m | grep protobuf

Where to look first. Browse the generated classes in the package's autoload root. The single most useful entry point is the top-level message the exporter sends:

  • Opentelemetry\Proto\Collector\Trace\V1\ExportTraceServiceRequest
  • Opentelemetry\Proto\Trace\V1\{ResourceSpans, ScopeSpans, Span, Status}
  • Opentelemetry\Proto\Common\V1\{AnyValue, KeyValue}
  • Opentelemetry\Proto\Resource\V1\Resource

Metrics and logs mirror the same shape (ExportMetricsServiceRequest, ExportLogsServiceRequest).

First use case: decode what you actually sent. Configure the exporter normally (OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf), then capture a request body — from a proxy, a dd() in a custom exporter, or a tcpdump on the collector — and replay it through the generated classes. This is the fastest way to verify attribute mapping, span IDs and timestamps:

use Opentelemetry\Proto\Collector\Trace\V1\ExportTraceServiceRequest;

$request = new ExportTraceServiceRequest();
$request->mergeFromString($binaryBody); // the raw application/x-protobuf body

foreach ($request->getResourceSpans() as $rs) {
    foreach ($rs->getResource()->getAttributes() as $kv) {
        echo $kv->getKey(), '=', $kv->getValue()->getStringValue(), PHP_EOL;
    }
    foreach ($rs->getScopeSpans() as $ss) {
        foreach ($ss->getSpans() as $span) {
            printf("%s %s %.2fms%s", bin2hex($span->getSpanId()), $span->getName(),
                ($span->getEndTimeUnixNano() - $span->getStartTimeUnixNano()) / 1e6, PHP_EOL);
        }
    }
}

That one script covers 90% of the debugging you will ever do with this package.

Implementation Patterns

Pattern 1 — Let the exporter own the types (the default). Wire the SDK once in a service provider and never touch protobuf classes again. The open-telemetry/exporter-otlp package constructs ExportTraceServiceRequest for you and sets Content-Type: application/x-protobuf. Your entire composer.json surface should be the SDK + exporter + configuration; pinning gen-otlp-protobuf yourself is a smell unless you are writing an exporter.

Pattern 2 — Custom exporter for a non-standard transport. This is where the package earns its keep. When you must, say, sign requests, route through a corporate gateway, or post to a vendor's HTTP endpoint with bespoke headers, implement SpanExporterInterface and use the generated types directly:

use Opentelemetry\Proto\Collector\Trace\V1\ExportTraceServiceRequest;

final class GatewaySpanExporter implements SpanExporterInterface
{
    public function export(iterable $batch): bool
    {
        $request = new ExportTraceServiceRequest();
        $request->setResourceSpans(/* map SDK ReadableSpan → ResourceSpans */);

        return Http::withToken(config('otel.token'))
            ->withHeaders(['Content-Type' => 'application/x-protobuf'])
            ->withBody($request->serializeToString(), 'application/x-protobuf')
            ->post(config('otel.endpoint').'/v1/traces')
            ->successful();
    }
}

Pattern 3 — Manual span construction for fixtures and tools. For an artisan command that emits synthetic telemetry, or a fixture generator for collector-side load tests, build messages field by field:

$value = new AnyValue();
$value->setStringValue('GET');

$attr = new KeyValue();
$attr->setKey('http.request.method');
$attr->setValue($value);

$span = new Span();
$span->setTraceId(hex2bin($traceIdHex));   // 16 raw bytes, not hex
$span->setSpanId(hex2bin($spanIdHex));     // 8 raw bytes
$span->setName('GET /orders');
$span->setKind(Span\SpanKind::SPAN_KIND_SERVER);
$span->setStartTimeUnixNano(hrtime(true));
$span->setAttributes([$attr]);

Pattern 4 — Assert on telemetry in tests. Keep OTEL_SDK_DISABLED=true in phpunit.xml globally, and in the few tests that need assertions swap in an in-memory exporter or a fake transport that captures the serialized body, then mergeFromString() it and assert on the resulting Span objects. This tests your instrumentation, not the SDK.

Pattern 5 — Instrument jobs and events, not just HTTP. Wrap queue workers with the same span builder you use in HTTP middleware, but attach the trace context to the job payload (W3C traceparent) so the consumer can resume the trace. The protobuf layer never changes between these paths — only the Resource attributes and span names do.

Gotchas and Tips

  • Namespace spelling. Generated classes live under Opentelemetry\Proto (lowercase "t"), not OpenTelemetry. IDE autocompletion lies about this constantly.
  • Do not edit generated files. This repo is a read-only git subtree split of open-telemetry/opentelemetry-php (proto/otel). Patches go upstream; regeneration is a release-time job.
  • Version skew is the #1 production failure. Package versions track the OTLP proto spec (1.5 → 1.8 → 1.9 in the recent tags), not the SDK's semver. A stale gen-otlp-protobuf paired with a newer exporter silently drops fields — e.g. the remote-parent flags bit added in 1.2.0 is absent in older generated code, so your traces lose parent context without any error. Always upgrade the whole stack together and regenerate the collector image at the same cadence.
  • Two protobuf runtimes, one API. google/protobuf and ext-protobuf expose identical classes; the extension wins if loaded. Never run both in separate processes and compare output formats — they match, but --ignore-platform-reqs during composer install in CI often hides a missing extension until you benchmark.
  • unix_nano needs 64-bit PHP. These fields are int64. On a 32-bit build timestamps overflow and the collector rejects or misorders spans. Confirm PHP_INT_SIZE === 8.
  • Enums are integer constants, never strings. Use Span\SpanKind::SPAN_KIND_SERVER, Status\StatusCode::STATUS_CODE_ERROR, etc. Passing 'SERVER' yields silent garbage.
  • AnyValue is a oneof. getValue() returns the field name ("string_value"), not the value. Call getStringValue() / getIntValue() / getBoolValue() based on that name.
  • IDs are raw bytes. trace_id is 16 bytes, span_id is 8. If you sourced them from a traceparent header, run hex2bin() and validate length first — a 32-char string in setTraceId() produces a span the collector drops.
  • Repeated fields are RepeatedField objects. Getters return live collections you can iterate; setters accept plain arrays. Do not cast with (array).
  • Binary vs JSON is all-or-nothing. serializeToString() with application/x-protobuf, or serializeToJsonString() with application/json, never mixed. The HTTP exporter handles this for you — hand-rolling is where people get it wrong.
  • Flush on long-running workers. The default batch processor buffers spans. In queue workers, Octane and other long-lived processes, call forceFlush() in terminating callbacks or on SIGTERM, or you will lose traces on every deploy.
  • Transport constraints. gRPC needs ext-grpc + grpc/grpc and HTTP/2 end-to-end — corporate proxies break it routinely. OTLP/HTTP on port 4318 is the pragmatic default in Laravel deployments.
  • Debugging a rejected payload. Set OTEL_LOG_LEVEL=debug, log the raw body and Content-Type, then decode with mergeFromString(). If the collector complains about message size, tune batching or the collector's max_recv_msg_size_mib (default 4 MiB) rather than shrinking spans.
  • Do not pin this package directly unless you are writing a custom exporter. Let open-telemetry/exporter-otlp resolve it so the protobuf encoding always matches the exporter that produces it.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log