All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Recommended-tier semconv attributes —
network.peer.address/network.peer.porton HTTP SERVER spans (the immediate TCP peer, distinct from the proxy-resolvedclient.address), andnetwork.protocol.versionon HTTP server and client duration/body-size metrics (deliberately not onhttp.server.active_requests, which the spec's attribute table excludes). - End-to-end test harness (
e2e/) — boots a real kernel, exports over a real OTLPhttp/jsonpipeline to a real collector, and asserts on the spans the collector received; runs in CI (.github/workflows/e2e.yml). Catches the silent-export failure class that mocked tests cannot. docs/semantic-conventions.md— the conformance statement, deliberate deviations, and known limitations; the README was slimmed to standard bundle shape with details moved intodocs/.
- Handled 4xx responses no longer mark SERVER spans as errors — semconv: "For HTTP status codes in the 4xx range span status MUST be left unset in case of SpanKind.SERVER". The error decision is now deferred from the exception event to response/finish time, so this also holds when a plain exception is mapped to 4xx via
#[WithHttpStatus]or a custom exception listener (span Error status is irreversible in the SDK, so deciding early was unfixable after the fact). The exception event is always recorded; responses >=error_status_thresholdkeep the exception FQCN aserror.type; unhandled exceptions (no response produced) mark the span Error witherror.typeat FINISH_REQUEST and now also record anhttp.server.request.durationsample witherror.type(failed requests were previously invisible in the duration metric). Dashboards note: HTTP error-span counts will drop — those were false positives. http.response.status_codeis kept whenever one was received — throwing accessors (getContent(true)/getHeaders(true)/toArray(true)on 4xx/5xx) and cancellations after headers arrived now record the received status on the client span and metrics (semconv: required "if and only if one was received").- Relative-URL requests keep Required client attributes on pre-transport failures — the trace/metrics HTTP client decorators capture
base_uri(fromwithOptions()or per-request options) and backfillserver.address/server.portand an absolute, sanitizedurl.fullwhenrequest()throws before an effective URL exists; query-only references resolve per RFC 3986 and scheme-relative (//host) URLs now get effective-URL enrichment sourl.fullends up absolute. Known limit:framework.http_client.scoped_clientskeepbase_uriinsideScopingHttpClient, invisible to the decorators — those still rely on effective-URL enrichment. db.collection.nameomitted for multi-table SELECTs — semconv conditions the attribute on the operation applying to a single collection; JOINs after the top-level FROM and inside derived tables no longer claim a single table (span names fall back to{operation} {db.namespace}).
- X-Ray tracer provider registers a shutdown flush — with
traces.id_generator: xray, the customTracerProvidernever registered a shutdown handler, so the defaultBatchSpanProcessorsilently dropped most spans in short-lived PHP-FPM processes.XRayBootstrappernow registersShutdownHandler::register($provider->shutdown(...))like the SDK autoloader does. - Failed requests first touched via
getStatusCode()now record a failure metric —MeteredResponse::getStatusCode()had no try/catch (unlike every other accessor), so a DNS/connect failure surfacing there left the request invisible inhttp.client.request.duration. toStream()works with tracing and metrics both enabled — the decorator stack isTracedResponsewrappingMeteredResponse, andMeteredResponsedidn't implementStreamableInterface, soTracedResponse::toStream()always threwLogicExceptionand flagged a successful request as an error.MeteredResponsenow implementsStreamableInterfaceand finalizes metrics at stream time.- Route-template warmer output is found on read-only-container deploys — the warmer writes to
kernel.build_dirbut the resolver only searchedkernel.cache_dir; on kernels separating the two, every process fell back to rebuilding the route collection at runtime. The resolver now checks both. - Messenger consumer
error.typereflects the real handler exception —HandlerFailedExceptionwrappers (single-wrapped) are unwrapped viagetWrappedExceptions()inErrorTypeResolver, so error breakdowns no longer show a constant wrapper FQCN on consumer spans and metrics. - Messenger middleware survives app-defined bus middleware — registration moved from
prepend()to a compiler pass (MessengerMiddlewarePass): FrameworkBundle'smiddlewarenode usesperformNoDeepMerging(), so any app configuring its ownmiddleware:silently replaced the bundle's list and dropped tracing/metrics. The pass also inherits FrameworkBundle's owndefault_busresolution, fixing single-custom-bus setups that previously aborted compilation (#63 — thanks @d-mitrofanov-v). OpenTelemetryBundle::version()no longer throws when not Composer-installed —InstalledVersions::getPrettyVersion()throwsOutOfBoundsException(rather than returning null) for unknown packages; the documented"unknown"fallback now actually applies (phar/single-file deploys).- Tag-aware pools keep
withSubNamespace()— Symfony 7.3+TagAwareAdapterimplementsNamespacedPoolInterface, but the tag-aware decorator hid it, fataling onwithSubNamespace(). Such pools now get the newTraceableNamespacedTagAwareCachePool. - SQL operation/target extraction hardened — a comment between a CTE list and its body no longer wins keyword matching (
WITH x AS (...) /* delete old */ SELECT ...reportedDELETE), anddb.collection.namefor SELECTs now comes from the depth-zeroFROM, so string literals (SELECT 'from paris' ...) and scalar subqueries in the select list can't hijack the table name. Doubled-quote escapes ('it''s') are handled. - DBAL3 transaction methods return the driver result —
beginTransaction()/commit()/rollBack()on the traced DBAL3 connection discarded the inner result and always returnedtrue. - DSN-style credentials and SigV2 signatures redacted —
UrlSanitizernow also redactsAWSAccessKeyId/Signaturequery params (SigV2 pre-signed URLs), handles protocol-relative URLs, and fully redacts userinfo containing a literal@. - HTTP server metrics drop
server.address/server.port— semconv marks them Opt-In onhttp.server.request.duration/active_requestsbecause the Host header is client-controlled; keeping them allowed unbounded time-series cardinality from arbitrary Host headers. http.routeis never a raw concrete path — when the route template can't be resolved and whole-segment substitution replaces nothing, the resolver now returns no route at all (per semconv) instead of emitting the request path as a metric dimension.- Long-running worker hygiene —
OpenTelemetrySubscriber::reset()now detaches scopes and ends spans for in-flight requests instead of dropping them (context-stack corruption on RoadRunner/FrankenPHP resets);ConsoleSubscriberno longer leaks a previous orphan span when commands without aCommandinstance run back-to-back; cached tracers no longer pin a pre-SDK-init noop tracer forever. OTEL_EXPORTER_OTLP_HEADERS/OTEL_RESOURCE_ATTRIBUTESvalues are percent-encoded on merge — values containing,or=no longer corrupt the whole variable; existing values are decoded before merging so the round-trip is lossless (the SDK/exporter url-decode on read per spec).traceway:doctorcorrectness — unknown--onlynames now error instead of running zero checks and exiting 0 in CI; non-numeric--timeoutreturns INVALID like other bad options; gRPC endpoints are probed with a TCP connect instead of an HTTP/1.1 HEAD that healthy collectors reject.composer testno longer fails the SDK autoload test — composer scripts exportCOMPOSER_DEV_MODE, which makes the SDK's_autoload.phpa no-op; phpunit now clears it.Tracing::trace()leaves span status UNSET on success — per the OTel spec, instrumentation should not setOk; error paths still setERROR.- Trace/metrics parity — the HTTP metrics subscriber honors
traces.error_status_threshold(was hardcoded ≥ 500) and the trace subscriber validatesContent-Lengthwith the samectype_digitguard as the metrics side. Cancelled HTTP client requests now record their span/duration witherror.type: cancelledinstead of vanishing. http.client.request.body.sizerecords with the final attribute set — it was recorded pre-flight withouthttp.response.status_code/error.type, so its dimensions never aligned with the duration histogram.server.addressfor IPv6 hosts is emitted bare (::1, not[::1]) across HTTP client spans and metrics.MeterRegistrycaches instruments by name + unit — requesting a same-named instrument with a different unit no longer silently returns the first one.TraceContextProcessorusesSpanContext::isValid()instead of comparing against an all-zero trace id, so invalid-but-nonzero propagation input is not injected into logs.
- Config:
excluded_pathsentries that are numeric scalars are now cast to path prefixes instead of silently dropped;traces.enabledinfo text documents master-switch behavior; using%env()%placeholders inopen_telemetryconfig now fails with a clear explanation (the config is consumed at compile time) instead of a confusing type error. - Internal: duplicated tracer/meter boilerplate, host-exclusion logic, transport-name extraction, short-class-name and default-port/IPv6 parsing consolidated into shared traits/helpers (
TracerAwareTrait,MeterAwareTrait,HostExclusionTrait,TraceableDbalTrait/MeteredDbalTrait,TransportNameResolver,ClassName,UrlParts); the DBAL 3/4 middleware pairs are now thin signature shims and the cache decorators share onetraced()span wrapper; dead code removed (isConsoleAvailable(),DbMetricRecorder's unreachablereset()). The emittedcache.poolattribute is unchanged: CachePoolPass strips the tag'snamebefore the bundle's pass runs, and thename-attribute fallback is kept (and now test-pinned) for custom pass orderings.
- Tag-aware cache pools stay tag-aware under the Symfony profiler —
TraceableTagAwareCachePoolnow implementsTagAwareAdapterInterface, so Symfony'sCacheCollectorPasswraps it inTraceableTagAwareAdapterrather than the plainTraceableAdapter. Injecting a decorated pool as aTagAwareCacheInterfacepreviously failed with a type error in dev/profiler mode (#59 — thanks @d-mitrofanov-v). - Messenger middleware respects a configured
default_bus— the tracing/metrics middleware is now prepended to the bus named byframework.messenger.default_bus(falling back tomessenger.bus.default), instead of always targetingmessenger.bus.default. Apps using a custom default bus no longer have to wire the middleware manually (#60 — thanks @d-mitrofanov-v). traces.enablednow acts as a master switch — settingtraces.enabled: falsedisables every trace subsystem (console, HTTP client, messenger, doctrine, cache, twig, scheduler, mailer) instead of only the request subscriber, so a single flag turns tracing off (e.g. in the test environment). Metrics and logs are unaffected (#61 — thanks @d-mitrofanov-v).- Streamable traced responses cast to a real stream —
TracedResponsenow implementsStreamableInterface/toStream(), so Symfony'sTraceableResponse::toStream()streams the underlying response instead of falling back to a lazyStreamWrapper; the span is finalized (or the error recorded) when the stream is produced (#62 — thanks @d-mitrofanov-v).
cache:pool:pruneno longer fatals on a traced cache pool —TraceableCachePoolnow implementsPruneableInterfaceand delegatesprune()to the inner pool. The prunablecache.pooltag transfers onto the decorator, soCachePoolPrunerPassselects it and the command called the missingprune()(Call to undefined method ...::prune()) on every scheduled run against a DB-backed pool. Mirrors Symfony'sTraceableAdapter(returnsfalsewhen the pool is not prunable); theTraceableTagAwareCachePool/TraceableNamespacedCachePoolsubclasses inherit it (#57 — thanks @pflueg).- HTTP client spans/metrics finalize when a response is consumed via
stream()— a response read only through the streaming API previously left its span open until__destruct()/GC, producing wildly inflated durations in long-running workers. The stream now ends the span/metrics on the last chunk (and records the error on a transport-error chunk). - Decorators are now wired for
kernel.reset— the HTTP client (trace + metrics), cache pool, and mailer (TraceableMailer/TraceableTransports/MeteredTransports) decorators implementResetInterfacebut were never tagged, so their cached tracer/enabledstate never refreshed between work units in long-running workers (Swoole, RoadRunner, Messenger). - Doctrine span creation can no longer break a query —
DbSpanBuilderdegrades to a non-recording span if the tracer throws (mirroring the metrics recorder), so a misconfigured SDK can no longer propagate an exception out ofquery()/exec()/prepared-statement execution. Tracing must never break the instrumented operation. - Empty
excluded_pathsentries are ignored — an empty or whitespace-only string previously normalized to/, silently excluding all traffic from tracing/metrics. Such entries are now dropped (bothtraces.excluded_pathsandmetrics.http_server.excluded_paths).
This is a conformance major: a full pass aligning every instrumentation with the current stable OpenTelemetry semantic conventions. There are no API changes for application code — the breaking changes are span-name, attribute-key, attribute-value, metric-bucket, and one config-validation change that mostly cause dashboards/alerts to regroup. See UPGRADE-3.0.md for the full migration checklist. Legacy flat config keys still work in 3.0 (removal deferred to 4.0 — see UPGRADE-2.0.md).
traces.doctrine.record_statementsnow defaults tofalse— per the database semconv, non-parameterizedquery()/exec()SQL should not be collected by default because it may carry unsanitized literals. SQL (db.query.text/db.statement) is no longer recorded unless you opt in. Migration: settraces.doctrine.record_statements: trueto restore SQL on spans.url.full/url.queryredaction list updated to the current HTTP semconv — the default sensitive-query-param deny-list is nowX-Amz-Signature,X-Amz-Credential,X-Amz-Security-Token,sig,X-Goog-Signature(was the retiredAWSAccessKeyId/Signaturepair plussig/X-Goog-Signature). Closes a gap where AWS presigned-URL signatures and credentials leaked into client-spanurl.full.QUERYadded to the default known-HTTP-methods list (httpbis safe-method-with-body draft, present in the vendored sem-conv) soQUERYrequests are no longer normalized to_OTHER.- Mailer transport span omits an empty
messaging.message.idinstead of emitting a blank attribute whenSentMessage::getMessageId()returns''. - HTTP client spans/metrics backfill transport-level attributes at finalize —
network.peer.address/network.peer.port(fromprimary_ip/primary_port),network.protocol.version(curl version constant mapped to1.1/2/3), and — closing the last Required-attribute gap — relativebase_urirequests now resolveserver.address/server.portand an absolute, sanitizedurl.fullfrom the effective URL. - Messenger spans carry
messaging.message.idfromTransportMessageIdStampon both producer and consumer sides. error_status_thresholdminimum raised from 400 to 500 — OTel forbidsErrorstatus for 4xx responses on server spans; the config can no longer express a spec violation. Migration: configs using 400–499 fail validation at boot.url.fullremoved from server spans — it is a client-span attribute per semconv; servers carryurl.path/url.query/url.scheme. Migration: queryurl.pathinstead.- Route templates resolve from a warmed cache, not the router — new
RouteTemplateCacheWarmerdumps a route-name → path-template map atcache:warmup;RouteTemplateResolverreads it (opcache-shared, free at runtime) and only falls back toRouter::getRouteCollection()when no warmed cache exists. CallinggetRouteCollection()per request would rebuild the full route collection under PHP-FPM — a measurable regression in the first iteration of this change, caught before release. - Console spans carry the cli-semconv Required attributes —
process.pidandprocess.executable.namewere missing entirely;process.command_argsis now the real process argv as a string array (was a single reconstructed string). Also: nested re-runs of the same Command instance no longer leak the previous span/scope. - Twig extension is now actually wired for
kernel.reset— the DI definition lackedsetAutoconfigured(true), soreset()was never invoked byservices_resetter: a throwing top-level template leaked its span with a permanently-active scope (context-stack corruption) for the life of the worker. Drained orphans now end with status Unset (only exception-proven nested orphans are marked Error). MeteredResponse::__destructno longer blocks on the network — it previously forced the lazy response to complete (up to the full request timeout) when garbage-collecting an unconsumed response; it now reads the non-blockinghttp_codeinfo and skips never-started responses.- Scheduler recurring-message overwrite guard —
RecurringMessagereuses the same message instance per trigger; a missed PostRun no longer leaks the prior span/scope on the next run. - Messenger producer destination only set when unambiguous — multi-transport routing previously recorded just the last
SentStamp's transport; per semconv the attribute is now set only when a single destination applies. OtelLogHandlerskips thetrace_flagsextra key — avoids duplicating the LogRecord's native trace context as a flat attribute.- Twig spans are now exception-safe — Twig's profiler hooks skip
leave()when a template throws, leaving the span open with a leaked scope, a wrong duration (closed only atreset()/destruct), and out-of-order scope detaching.leave()now closes any nested orphans (in reverse order, preserving scope nesting) and marks themErrorwith "Template rendering did not complete";reset()/destruct drains do the same. - Cache spans set
error.typeon failure (exception FQCN viaErrorTypeResolver), matching every other instrumentation in the bundle;cache.tagsis now a native string-array attribute instead of a comma-joined string. - Scheduler spans leave status
Unseton success (and on ignored failures) instead of forcingOk— per OTel trace API guidance,Okis reserved for explicit app-level marking. Ignored failures still record the exception event anderror.type. - Monolog
TraceContextProcessornow also injectstrace_flags(W3C two-hex form, e.g.01) alongsidetrace_id/span_id, so non-OTel log sinks can tell sampled from unsampled traces when joining logs to traces. - Messenger and Mailer instrumentation conform to the current OTel messaging semconv (breaking span-name and operation-type changes) — six fixes:
messaging.operation.type: publish→send:publishwas renamed in the messaging conventions (the vendored sem-conv marks it@deprecated Replaced by 'send').- Required
messaging.operation.namenow set on messenger producer (send) and consumer (process) spans — previously only the metrics middleware and Scheduler satisfied this. - Span names use semconv operation-first ordering:
send {ShortMessageClass}/process {ShortMessageClass}(was{ShortMessageClass} publish/process). The message class is kept as the low-cardinality target — deliberately not the spec's{destination}— so task backends (Traceway) keep grouping Tasks per message type instead of collapsing to one group per transport. Migration: Traceway Task groups rename once (e.g.SendWelcomeEmail process→process SendWelcomeEmail). - Producer spans now carry
messaging.destination.name(conditionally required) resolved from theSentStampafter dispatch;error.typenow set on both messenger failure paths viaErrorTypeResolver. root_spans: trueno longer loses producer correlation: the consumer root span now carries a span link to the dispatching trace context (the spec's default correlation mechanism) instead of dropping it.messaging.client.sent.messagesno longer counts sync-handled messages: dispatches that produce noSentStampnever reached a broker (spec: "MUST NOT count messages that were created but haven't yet been sent"); failed dispatches still recordmessaging.client.operation.durationwitherror.type. Migration: apps with sync-handled buses will see this counter drop — that traffic was never broker traffic.- Mailer: the bus-level span is now
create {transport}withmessaging.operation.type/name: create(PRODUCER, per the spec's operation table) so it no longer duplicates the transport-levelsend {transport}CLIENT span's identity;email.message_idrenamed to the standardmessaging.message.id; success spans leave status Unset instead of forcingOk(also applied to messenger spans), per the trace API guidance thatOkis reserved for explicit user marking.
- Doctrine instrumentation conforms to the stable OTel database semconv (breaking
db.system.namevalue changes for MSSQL/Oracle/unknown drivers) — five fixes:db.system.namecarries real enum values:microsoft.sql_server(wasmssql),oracle.db(wasoracle), newibm.db2, and unknown drivers now fall back toother_sqlinstead of leaking the raw Doctrine driver string. MariaDB is detected via the connection'sserverVersionparam (e.g.10.11.2-MariaDB→mariadb). The dual-emitted deprecateddb.systemattribute keeps its legacy values (mssql,oracle,db2) via the new sharedDbSystemResolver(also de-duplicates resolution between the trace and metric drivers). Migration: dashboards filteringdb.system.nameonmssql/oraclemust switch to the new values.db.response.status_code(conditionally required on failure): SQLSTATE from Doctrine'sDriver\Exception::getSQLState()is now recorded on failed spans and metric points.- DB-specific histogram buckets:
db.client.operation.durationnow uses the database semconv advisory[0.001 … 10](newDurationBoundaries::DB_SECONDS) instead of the HTTP boundaries — sub-millisecond queries no longer collapse into the ≤5ms bucket. Migration: bucket layout of this metric changes. - SQL operation extraction hardened: leading comments (
/* */,--,#), wrapping parentheses, and CTEs (WITH … SELECT) now resolve to the real statement verb instead of garbage tokens like/*,(SELECT, orWITH; unparseable SQL falls back to target → namespace →db.system.namefor the span name (per spec) instead of the inventedUNKNOWNtoken, and no longer emitsdb.operation.name. - Span-side
error.typenow usesErrorTypeResolver(anonymous exception classes resolve to their parent FQCN), matching the metric side; failure handling is centralized inDbSpanBuilder::recordFailure().db.query.summaryis now also emitted on the duration metric (recommended attribute).
- HTTP spans and metrics conform to the stable OTel HTTP semconv (breaking span-name and
http.routechanges) — six fixes across server and client instrumentation:- Method normalization: unknown HTTP methods are normalized to
_OTHERwith the raw value preserved inhttp.request.method_original(spec MUST). The known-method list is RFC 9110 + PATCH, overridable viaOTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS. - Span names: server spans start as bare
GET(previouslyHTTP GET) and becomeGET /api/items/{id}after routing; client spans are named by bare method (previouslyGET api.example.com), both per the spec's{method} {target}rules. Unknown methods useHTTP. http.routecomes from the real route template: resolved from the router's route collection via the newRouteTemplateResolver(cached per route name), with whole-segment parameter substitution as fallback — the old substringstr_replacecould corrupt static segments (param valueamangling/api). Unrouted requests no longer leak the raw URL path intohttp.routeor the span name (spec MUST NOT). Migration: endpoints whose synthesized template differed from the real route path (e.g. routes with.{_format}suffixes) will regroup once under the new string.error.type(spec conditionally required) now set everywhere it was missing: server spans (exception FQCN, or status code string for >= threshold responses), client spans (status string for >= 400, exception FQCN for transport errors), server metrics (5xx without exception), client metrics (>= 400 responses).- Client
server.address/server.port: default ports are inferred from the scheme (443/80) so the spec-requiredserver.portis present on default-port URLs; relative/unparseable URLs omitserver.addressinstead of recording the literalunknown. - URL redaction (spec MUST NOT carry credentials):
url.fullredactsuser:pass@toREDACTED:REDACTED@, and the semconv-listed sensitive query params (AWSAccessKeyId,Signature,sig,X-Goog-Signature) are redacted inurl.fullandurl.query. - Also:
network.protocol.versionreports2/3instead of2.0/3.0, andTracedResponse::getStatusCode()now records transport errors on the span instead of silently ending it in the destructor.
- Method normalization: unknown HTTP methods are normalized to
- Console command spans now conform to OTel CLI semconv and classify as Tasks (breaking attribute changes) —
ConsoleSubscriberemitted the command root span asSERVERwith aprocess.commandattribute. Task-oriented backends (Traceway) classify a span as a Task only when it isCONSUMERor a rootINTERNALspan carryingconsole.command, so everybin/consolerun was silently dropped and its child spans orphaned. The span kind is nowINTERNAL(the CLI span spec says the callee span "SHOULD be INTERNAL"; no other PHP console instrumentation usesSERVER) and the command name is recorded asconsole.command, matching keepsuit's Laravel instrumentation. Attributes renamed to their real semconv keys:process.exit_code→process.exit.code,process.command.args→process.command_args(neither old name exists in the semconv registry). Failed commands now also carryerror.type(exception FQCN viaErrorTypeResolver, or the exit code as a string when no exception was recorded), conditionally required by the CLI spec. Migration: dashboards or alerts queryingprocess.command,process.exit_code, orprocess.command.argsmust switch to the new keys.
- Cache pools declared via
ChildDefinitionnow classify correctly — the default shape offramework.cache.poolspools (aChildDefinitionofcache.adapter.filesystem/cache.adapter.redis/ etc. with no class set on the child) was falling through to plainTraceableCachePool. On Symfony 7.3+ this crashed container compilation viaCheckAliasValidityPassbecauseNamespacedPoolInterfaceis aliased tocache.app.CacheTracingPassnow walks the parent chain to resolve the effective class before classifying (#55 — thanks @d-mitrofanov-v).
grpcprotocol prerequisites documented — README now flags thatprotocol: grpcrequires theext-grpcPHP extension and theopen-telemetry/transport-grpcComposer package (#54).
traceway:doctordiagnostic command — newtraceway:doctor(aliasdebug:traceway) runs a suite of checks across runtime extensions (ext-opentelemetry,ext-protobuf), SDK configuration (service name, OTLP endpoint, protocol, traces exporter, sampler, tracer provider), bundle wiring (Messenger middleware registration, OtelLogHandler registration, X-Ray dependency presence), and OTLP endpoint reachability. Supports--format=jsonwith a stable envelope ({version, summary, checks}) for CI consumption;--skip-networkskips connectivity checks;--only=<names>filters to a comma-separated subset;--fail-on=info|warning|errorcontrols the exit-code threshold (defaulterror);--timeout=<seconds>bounds network checks. Severities areinfo,warning,error(#53).- SDK configuration via bundle config — new
open_telemetry.sdk.*section lets you set OpenTelemetry SDK env variables through Symfony's configuration system rather than the shell or Composer-time env.sdk.autoload_enabled: truetogglesOTEL_PHP_AUTOLOAD_ENABLEDfrom bundle config and re-executes the SDK's_autoload.php— solving the case where Symfony's Dotenv runs too late for autoload to see it (so.env.localand Symfony Secrets now work for the autoload toggle).sdk.resource_attributesandsdk.exporter_otlp_headersaccept key/value maps that merge intoOTEL_RESOURCE_ATTRIBUTESandOTEL_EXPORTER_OTLP_HEADERSrespectively, with bundle values winning over any pre-existing env entries — primarily intended for Symfony Secrets interpolation (e.g.'Authorization': 'Bearer %env(OTEL_API_BEARER_TOKEN)%').sdk.use_putenv(off by default for thread-safety) opts into mirroring writes viaputenv()alongside$_SERVER/$_ENV. The whole section iscanBeEnabled()-style: setting any sub-key implicitly enables it; setsdk.enabled: falseto suppress (#49 — thanks @AndreasA).
- Native AWS X-Ray support — new
traces.propagatorconfig key (w3cdefault,xray,w3c+xray) swaps the globalTextMapPropagatorfor AWS X-Ray'sX-Amzn-Trace-Idheader format; all context injection and extraction across HTTP, HttpClient, and Messenger updates automatically. Newtraces.id_generatorconfig key (default,xray) builds aTracerProviderusing the SDK's env-var auto-configuration factories plusXRayIdGenerator, producing epoch-prefixed trace IDs that X-Ray's UI renders as request timestamps. Both options requireopen-telemetry/contrib-aws(added tosuggest); a clearLogicExceptionis thrown at container boot if the package is absent. Thew3c+xraypropagator mode writes bothtraceparentandX-Amzn-Trace-Idsimultaneously viaMultiTextMapPropagator, useful during a gradual migration (#46 — thanks @FrameAutomata).
- PHP 8.5 deprecations — replaced
SplObjectStorage::contains()/detach()(deprecated in 8.5) withoffsetExists()/offsetUnset()in the Console, Scheduler, and Twig instrumentation (#48 — thanks @MrYamous). RemovedReflectionProperty::setAccessible()/ReflectionMethod::setAccessible()calls from the test suite (no-op since PHP 8.1, deprecated in 8.5).
- Config restructured to nested, signal-grouped shape — every top-level flat key migrates under
traces:,metrics:(unchanged), orlogs:. Aligns with the OpenTelemetry specification, theOTEL_*env-var convention, and the existing nestedmetrics:node we've shipped since v1.7.0. Existing v1.x flat config keeps working with a deprecation per key; flat keys are scheduled for removal in v3.0. Setting both a legacy flat key and its nested equivalent in the same configuration block throwsInvalidConfigurationException. See UPGRADE-2.0.md for the complete flat→nested mapping. log_export_unprefixed_attributesdefault flipped fromfalsetotrue— Monologcontextandextrafields are now emitted as flat OTel attributes by default, matching the cross-ecosystem norm (Java logback, PythonLoggingHandler, .NETOpenTelemetryLogger, JS Winston). The new path islogs.export.unprefixed_attributes. If your dashboards depend on the v1.xmonolog.context.*/monolog.extra.*prefixed shape, set it explicitly tofalse. The knob has existed since v1.8.0 (#39) so users have had a release cycle to migrate before the default change.
- All v1.x flat config keys — every key triggers a Symfony deprecation pointing at its new nested location. Removal scheduled for v3.0. Run
bin/console cache:clearafter upgrading to surface the deprecations in the dev log. Full mapping in UPGRADE-2.0.md.
logs:configuration node — groups Monolog correlation (logs.correlation.enabled, formerlymonolog_enabled) and native OTel log export (logs.export.*, formerlylog_export_*) under one signal-aligned section.- Instrumentation scope version + schema URL — every
Tracer,Meter, andLoggerthe bundle obtains from the OTel SDK now reports the full(name, version, schemaUrl)tuple required by the OTel instrumentation-scope spec (a SHOULD-level recommendation we previously missed). Version is resolved at runtime via Composer'sInstalledVersions::getPrettyVersion()so it always reflects the actual installed release. Schema URL is pinned tohttps://opentelemetry.io/schemas/1.32.0(the version ofopen-telemetry/sem-convwe target). Backends like Tempo, Datadog, and Honeycomb can now filter byotel.scope.versionto slice telemetry per bundle release.
- Mailer tracing — new
TraceableMailerdecorator emits a PRODUCER span aroundMailerInterface::send(), and newTraceableTransportsdecorator emits a CLIENT span around the transport-level send. Attribute shape follows OTel messaging semconv (messaging.system=symfony_mailer, operation name/type, destination fromX-Transportheader,error.typeon failure) plus ECS-aligned email keys (email.to.count,email.message_id, opt-inemail.subject) anticipating semantic-conventions#927. Auto-activates whensymfony/maileris installed; opt out withmailer_enabled: false. Subject capture is opt-in viamailer_record_subject: true(PII-adjacent). Whenframework.mailer.message_busis set, the PRODUCER span correctly scopes to the dispatch only; the CLIENT span later covers the worker-side transport send (#40). - Symfony Scheduler instrumentation — new
SchedulerSubscriberemits a CONSUMER span per scheduled task run with OTel messaging semconv attributes plus a Traceway-specificscheduler.*namespace for trigger metadata. Pre/Post/Failure events drive span lifecycle; cancellations viaPreRunEvent::shouldCancel(true)are recorded asscheduler.cancelledso they remain observable. Because scheduler dispatches flow through the Messenger bus,OpenTelemetryMiddlewareauto-suppresses its own PRODUCER/CONSUMER spans on envelopes carrying Symfony'sScheduledStamp— letting the richer scheduler span own the work unit without duplicate spans. Auto-activates whensymfony/scheduleris installed; opt out withscheduler_enabled: false(#41). - Incoming HTTP server metrics — new
OpenTelemetryMetricsSubscriberemitshttp.server.request.duration(Histogram, semconv Stable),http.server.active_requests(UpDownCounter),http.server.request.body.size, andhttp.server.response.body.size(Histograms) with OTel HTTP server semconv attributes. Only main requests are measured (sub-requests are already covered by the main duration); metric recording failures never mask request handling. Off by default; enable withmetrics.http_server.enabled: true.metrics.http_server.excluded_pathslets you skip routes like/healthand/_profiler(#30 — thanks @srekcud). - Mailer transport metrics — new
MeteredTransportsdecorator emitsmessaging.client.operation.duration(Histogram,s) andmessaging.client.sent.messages(Counter,{message}) on outbound transport sends, with OTel messaging attributes (messaging.system=symfony_mailer, operation name/type, destination fromX-Transportheader,error.typeon failure). Off by default; enable withmetrics.mailer.enabled: true. Decoration priority places it inside the existingTraceableTransportsso metric data points record within the active trace span scope, enabling SDK-level exemplar linkage from metric points back to traces.
- Internal: shared
DurationBoundaries::SECONDSconstant — bucket boundaries for every second-based duration histogram in the bundle are now centralized inTraceway\OpenTelemetryBundle\Metrics\DurationBoundaries. The previously public-but-undocumented per-classDURATION_BUCKET_BOUNDARIESconstants onMeteredHttpClient,OpenTelemetryMetricsMiddleware,OpenTelemetryMetricsSubscriber,DbMetricRecorder, andMeteredTransportshave been removed. If you reference any of them in your own code, switch toDurationBoundaries::SECONDS.
HttpClientMetricsPassdecoration-priority comment — was incorrect about Symfony's priority direction (claimedMeteredHttpClientwrapsTraceableHttpClient; the actual decoration ordering is the inverse, with metrics recorded inside the active trace span scope). No behavior change — runtime ordering was already correct for exemplar linkage; only the explanatory comment was misleading future readers.OpenTelemetryTestKernelcache directory collision under PHPUnit 13 — the test kernel keyed its cache directory onspl_object_id($this), which PHP recycles after garbage collection. Under PHPUnit 13's earlier teardown lifecycle, a second test could be assigned the same object ID as a destroyed first kernel and silently load the previous test's compiled container — masking its own config and producing flaky failures inBundleBootTest. Now usesgetmypid() . '_' . $counterwhere the counter is a monotonic per-process value, so the cache dir is unique both within a process (no ID recycling) and across concurrent PHP processes (paratest, accidental parallelphpunitinvocations).
- PHPUnit 13 compatibility — every
@dataProviderand@groupdocblock annotation acrosstests/Doctrine/Middleware/was migrated to the PHP 8 attribute equivalents (#[DataProvider],#[Group]). PHPUnit 13 removed support for docblock metadata; under it, unmigrated@dataProviderannotations silently fall through to argument-less invocation and throwArgumentCountError.phpunit/phpunitrequire-dev constraint expanded to^10.0 || ^11.0 || ^13.0so the existing CI matrix (PHP 8.1 through 8.4) picks the highest compatible version on each row: PHP 8.1 → PHPUnit 10, PHP 8.2 → PHPUnit 11, PHP 8.4 → PHPUnit 13. PHPUnit 12 is omitted (short-lived release between Feb–Sep 2025, immediately superseded by 13 with overlapping PHP version support, so testing it adds matrix complexity for no coverage gain).
- OTel
code.*log attributes (semconv Stable) —OtelLogHandlernow emitscode.file.path,code.line.number, andcode.function.nameon log records, promoted from Monolog'sIntrospectionProcessorextras. Backends with source-link support (Jaeger, Tempo, Datadog, etc.) render these as clickable links to your log call sites (#36) log_export_capture_code_attributesconfig flag — opt-indebug_backtracefallback to resolve the newcode.*attributes whenMonolog\Processor\IntrospectionProcessoris not installed. Off by default; prefer installing the processor for zero-overhead resolution (#36)log_export_unprefixed_attributesconfig flag — opt into the flat cross-ecosystem attribute shape (Java/Python/.NET/JS all emit user log fields flat). Whentrue,$record->contextand$record->extrakeys are emitted unprefixed instead of undermonolog.context.*/monolog.extra.*. Defaultfalsefor backward compatibility; will flip totruein v2.0 (#39)- Outgoing HTTP client metrics — new
MeteredHttpClientdecorator emitshttp.client.request.duration(Histogram, semconv Stable),http.client.request.body.size, andhttp.client.response.body.size(Development) with OTel HTTP semantic-convention attributes. Off by default; enable withmetrics.http_client.enabled: true(#29 — thanks @srekcud) - Doctrine DBAL metrics — new metered middleware emits
db.client.operation.duration(Histogram) for every DBAL query, exec, prepared statement execution, and transaction control. Off by default; enable withmetrics.doctrine.enabled: true(#31 — thanks @srekcud) TracingimplementsResetInterface— the manual-instrumentation helper now joins every other lazy-tracer class in the bundle, clearing its cached tracer state between Symfonykernel.resetcycles. Closes the lastResetInterfacegap; matters in long-running processes (Messenger workers, FrankenPHP, RoadRunner, Swoole) (#38)
monolog.channelattribute no longer emitted on log records — the Monolog channel is exclusively represented as the OTelInstrumentationScopename (matching Java logback, PythonLoggingHandler, .NETOpenTelemetryLogger, and JS Winston, none of which duplicate the channel/logger name as an attribute). If your dashboards filter bymonolog.channel = "X", switch to filtering by the scope name instead (#37)monolog.extra.{file,line,class,callType,function}no longer emitted whenIntrospectionProcessorextras are present — those keys are promoted to canonicalcode.*attributes (see Added). Users running withoutIntrospectionProcessorare unaffected; users running with it should migrate dashboard queries to the newcode.*keys (#36, #37)
TraceableHttpClient::request()cleanup-ordering bug — if$span->recordException()or$span->setStatus()itself threw inside the catch block (rare, but possible when the OTel SDK or attribute serializer fails),$inFlightwas lefttrueand the scope was not detached, silently suppressing all future HTTP client spans on that instance untilreset()fired. Cleanup is now wrapped in atry { try { ... } catch (...) { record; end; throw; } } finally { detach; inFlight=false; }shape that matchesTracing::trace(). Most users won't have observed the symptom, but the failure mode would have been particularly bad in long-running Messenger workers (#38)
1.7.0 - 2026-05-10
- Metrics foundation — new
MeterRegistryservice andOpenTelemetryMetricsMiddlewarefor Symfony Messenger consumer-side metrics (messaging.process.durationhistogram,messaging.client.consumed.messagescounter) with OTel semantic convention attributes. Off by default; enable withmetrics.enabled: trueandmetrics.messenger.enabled: true(#27) Util\ErrorTypeResolver— shared utility for resolvingerror.typeattribute from exceptions, with anonymous class fallback to parent FQCN (#32)- Unit and functional test coverage improvements (#20)
TraceableHttpClient::stream()breaksRetryableHttpClient— stream chunks were keyed by the inner (unwrapped) response instead of theTracedResponsewrapper, causingUnexpectedValueException: Object not foundinAsyncResponsewhen any decorator usingAsyncResponse(e.g.RetryableHttpClient) sat aboveTraceableHttpClientin the chain. Now re-keys chunks usingSplObjectStorage, mirroring Symfony's ownTraceableResponse::stream()pattern (#34, #35)- Metrics never mask handler exceptions — metric recording failures are swallowed so a broken meter provider cannot interfere with Messenger message handling (#27)
- Second-based histogram buckets —
messaging.process.durationnow uses explicit second-based bucket boundaries aligned with OTel conventions (#27) - Anonymous class
error.type— classes containing@anonymousin their FQCN now fall back to the parent class name (#27) - PHP 8.1 compat — replaced
iterator_to_array()with spread operator where needed (#27)
1.6.1 - 2026-04-16
- Bundle loading order crash with
log_export_enabled: true—OtelLogHandlerandOtelLoggerFlushSubscriberservice definitions are now registered inprepend()instead ofload(), so they exist beforeMonologBundle::load()compiles its handler references regardless of bundle registration order inbundles.php(#17, #18 — thanks @srekcud)
1.6.0 - 2026-04-13
- Monolog log export via OpenTelemetry Logs API — new
OtelLogHandlerbridges Monolog records into the OTel Logs API with native trace correlation, per-channel instrumentation scopes, microsecond timestamp precision, and exception attributes. Off by default; enable withlog_export_enabled: true(also requiressymfony/monolog-bundle) log_export_enabled/log_export_levelconfig keys — opt-in toggle and minimum severity filter for the new OTel log export pipelineOtelLoggerFlushSubscriber— flushes theLoggerProvideronkernel.terminateandconsole.terminateso log records queued inBatchLogRecordProcessorare not lost when a request finishes faster than the batch processor's scheduled flush interval- Re-entrance guard in
OtelLogHandler— prevents infinite loops when the OTel exporter itself emits a log record (e.g. an instrumented HTTP client logging a failed OTLP send), matchingTraceableHttpClient's$inFlightpattern - Loud failure when
log_export_enabled: truebutsymfony/monolog-bundleis missing —OpenTelemetryExtension::prepend()now throwsLogicExceptionwith a clear install hint instead of silently no-op'ing (the previous behavior masked the misconfiguration becauseprependExtensionConfigstores config for nonexistent extensions without error)
1.5.0 - 2026-04-10
- Doctrine DBAL 3 tracing support — version-specific classes (
TraceableConnectionDbal3/Dbal4,TraceableStatementDbal3/Dbal4) with runtime detection viaVersionAwarePlatformDriverinterface existence; DBAL 3 users now get full query tracing instead of auto-disabled Doctrine instrumentation (#8) NamespacedPoolInterfacesupport —CacheTracingPassnow selectsTraceableNamespacedCachePoolfor cache pools implementingNamespacedPoolInterface(Symfony 7.3+), fixing container compilation failures; guarded withinterface_exists()so Symfony < 7.3 is unaffected (#11)
- ConsoleSubscriber orphaned spans —
ConsoleSubscriber::reset()now properly detaches active spans before clearing storage, preventing orphaned spans in long-lived workers (Messenger, Swoole, RoadRunner) (#9)
1.4.4 - 2026-04-04
- Doctrine config default not normalized —
doctrine_enableddefault value now uses a shared$isDbalCompatibleclosure so it evaluates correctly even when no config is set manually, following FrameworkBundle's$enableIfStandalonepattern (#8)
1.4.3 - 2026-04-04
- Doctrine DBAL 3 graceful degradation — removed the
doctrine/dbal: <4.0conflict rule fromcomposer.jsonso the bundle can be installed alongside DBAL 3; Doctrine tracing is now auto-disabled via Symfony config normalization when DBAL < 4.0 is detected, keeping all other instrumentations (HTTP, Console, Messenger, Cache, Twig, Monolog) fully functional (#8)
1.4.2 - 2026-04-03
doctrine/dbal: <4.0conflict rule incomposer.json— Composer now blocks installation with DBAL 3 instead of failing at runtime with TypeErrors- Doctrine DBAL 3 support added to roadmap
1.4.1 - 2026-04-03
- Service version via environment —
service.versionis no longer set automatically from the bundle; configure it viaOTEL_RESOURCE_ATTRIBUTES=service.version=1.0for per-deployment control - Removed
OpenTelemetryBundle::VERSIONconstant (no longer used) - Added Releases section to
CONTRIBUTING.md - Updated
CLAUDE.mdrelease instructions to reflect manual tagging process
1.4.0 - 2026-04-02
- Infinite recursion in UrlGenerator — OTel span/scope objects are now stored in a
WeakMapinstead of$request->attributes, preventing them from leaking into Symfony'sUrlGenerator::doGenerate()wherearray_walk_recursivecaused stack overflow on redirects (e.g. login, access denied) - Cache pool type errors in debug mode —
TraceableCachePoolconstructor no longer requires the inner pool to implementCacheInterfaceandAdapterInterfaceupfront; checks are deferred to methods that need them, fixingTypeErrorwhen Symfony wraps pools withTraceableAdapterin dev mode
DEPLOYMENT.md— step-by-step deployment guide covering PHP extensions, FPM environment configuration, bundle setup, verification, and troubleshooting
1.3.3 - 2026-04-01
- Memory leaks in long-running processes —
ConsoleSubscriber,OpenTelemetrySubscriber,TraceableCachePool, andOpenTelemetryMiddlewarenow implementResetInterface, allowing Symfony'sservices_resetterto clear cached tracer/enabled state between requests in Messenger workers, Swoole, RoadRunner, and FrankenPHP - Orphaned console spans —
ConsoleSubscribernow usesSplObjectStoragefor per-command span storage instead of single instance properties, preventing span overwrites when a command crashes beforeonTerminate - Twig
spl_object_idreuse —OpenTelemetryTwigExtensionnow usesSplObjectStorageinstead ofspl_object_id()keyed arrays, eliminating the theoretical risk of matching a wrong span after garbage collection
1.3.1 - 2026-03-12
http_client_excluded_hostsconfiguration option — exclude specific hostnames from outgoing HTTP client tracing (e.g. your OTLP collector)- OTLP endpoint auto-exclusion —
TraceableHttpClientautomatically skips tracing for calls matchingOTEL_EXPORTER_OTLP_ENDPOINT, preventing instrumentation loops - Re-entrance guard in
TraceableHttpClient— nested HTTP calls made while a traced call is in-flight (e.g. exporter, security token validation) are passed through without creating duplicate spans - 256 unit tests with 649 assertions (up from 250/640)
- HttpClient instrumentation loop — when
traces_enabledandhttp_client_enabledwere both active, outgoing HTTP calls from Symfony internals (security, OTLP export) could create unbounded spans leading to memory exhaustion - Cache
AdapterInterfacecompatibility —TraceableCachePoolnow implementsSymfony\Component\Cache\Adapter\AdapterInterface, fixingTypeErrorwith Symfony'sTraceableAdapter(web profiler) in dev mode - Console scope detach notice —
ConsoleSubscribernow suppressesDebugScopenotices during__destructcleanup (fires whenonTerminatenever runs due to fatal error orexit()) and detaches scope before ending span (correct OTel ordering) - Memory cleanup in
OpenTelemetrySubscriber— span, scope, and exception references are removed from the Request attributes bag inonFinishRequestEndSpanandonTerminate, preventing accumulation in long-running processes or functional tests - Request body size optimization —
onResponsenow uses theContent-Lengthheader for request body size instead of reading the full body viagetContent(), avoiding unnecessary memory allocation for large payloads - Doctrine DBAL 3 conflict — added
conflict: doctrine/dbal: "<4.0"tocomposer.jsonsince DBAL 3's method signatures (execute($params),beginTransaction(): bool) are incompatible with DBAL 4's abstract middleware classes; DBAL 3 is EOL
1.3.0 - 2026-03-12
- Monolog log-trace correlation —
TraceContextProcessorautomatically injectstrace_idandspan_idinto every Monolog log record'sextraarray, enabling one-click navigation from logs to traces in your observability backend monolog_enabledconfiguration option (defaults totrue) — disable withmonolog_enabled: falsewhen Monolog is not usedmonolog/monologadded tosuggestincomposer.json- Auto-detection: processor is only registered when
monolog/monologis installed (no error if absent) - 250 unit tests with 640 assertions (up from 241/622)
1.2.1 - 2026-03-12
traceway.distributed_trace_idspan attribute — captures thetraceway-trace-idHTTP header on request spans, enabling distributed trace correlation across servicesopen-telemetry/exporter-otlpandphp-http/guzzle7-adapteradded tosuggestincomposer.jsonfor clearer onboarding
1.2.0 - 2026-03-12
- Console command auto-instrumentation — SERVER spans for every
bin/consolecommand withprocess.command,process.command.args,process.exit_code, and exception recording console_enabledandconsole_excluded_commandsconfiguration optionssymfony/consoleadded torequiredependenciesConsoleSubscriberwithConsoleEvents::COMMAND,ERROR, andTERMINATEhooks- Cache pool auto-instrumentation — INTERNAL spans for
get()(with hit/miss detection),delete(),clear(), andinvalidateTags()operations on allcache.pooltagged services cache_enabledandcache_excluded_poolsconfiguration optionsCacheTracingPasscompiler pass decorates all non-abstract cache pools; tag-aware pools getTraceableTagAwareCachePool- Twig template auto-instrumentation — INTERNAL spans for every template render with nested template support (includes, extends)
twig_enabledandtwig_excluded_templatesconfiguration options for excluding framework templates (e.g.@WebProfiler/,@Debug/)OpenTelemetryTwigExtensionusing Twig'sProfilerNodeVisitorto hook into template renderingtwig/twigandsymfony/cacheadded tosuggestandrequire-devdependencies- Messenger PRODUCER spans — dispatch side now creates a PRODUCER span with
messaging.system,messaging.operation.type=publish, andmessaging.message.classattributes, giving full lifecycle visibility (publish → process); consume side now also recordsmessaging.destination.namefrom the transport - HttpClient
url.pathandurl.schemeattributes — CLIENT spans now include parsed URL path and scheme for consistent filtering - HttpClient
http.response.body.sizetracking —TracedResponserecords response body size from Content-Length header or actual content - Doctrine
DbSpanBuilder— shared span-building logic extracted fromTraceableConnectionandTraceableStatement, eliminating code duplication - 241 unit tests with 622 assertions (up from 172/419)
OpenTelemetryTwigExtensionnow usesspl_object_id()for span matching instead of stack-based template name matching — eliminates mismatch edge cases with duplicate template namesTraceableCachePoolvalidatesCacheInterfacein constructor instead of at method call time — misconfiguration fails earlyTracing/TracingInterface$kindparameter usesSpanKind::KIND_*PHPDoc type instead of@phpstan-ignoreHttpClientTracingPassadds\assert(\is_string($tracerName))for type safety, consistent withCacheTracingPassOpenTelemetryMiddlewaredispatch path now wraps in a PRODUCER span instead of silently injecting context
ConsoleSubscriberscope leak —__destructguard ensures scope is detached whenTERMINATEevent never fires (e.g. fatal error,exit()in command)TraceableConnectionandTraceableStatementnow cache the tracer instance instead of resolving it on every queryOpenTelemetryTwigExtension__destructguard drains spans in LIFO order on shutdown, preventing scope leaks from unmatchedenter()/leave()callsurl.queryattribute now omitted when query string is absent instead of being set tonull
1.1.0 - 2026-03-16
- Doctrine DBAL auto-instrumentation — CLIENT spans for every database query with current OTel semantic conventions (
db.system.name,db.operation.name,db.namespace,db.query.text,server.address,server.port) - SQL template recording enabled by default (uses
?placeholders, never includes parameter values) - Transaction tracing (
BEGIN,COMMIT,ROLLBACKspans) - Prepared statement tracing via
TraceableStatement doctrine_enabledanddoctrine_record_statementsconfiguration options- Auto-detection of database system (MySQL, PostgreSQL, SQLite, SQL Server, Oracle)
- Exception recording on query failures
- Backward-compatible Datadog attributes (
db.system,db.statement,db.operation,db.name) alongside current OTel conventions url.queryattribute on HTTP spans for query parameter tracing- Code coverage reporting via Codecov in CI
- Codecov badge in README
- Upgraded to PHPStan 2.x level 10 (from 1.x level 9) with proper type narrowing
- Migrated from deprecated
TraceAttributestoAttributes\*/Incubating\Attributes\*interfaces - Updated
messaging.operationtomessaging.operation.type(current OTel spec) TracedResponsenow finalizes span with status code fromgetHeaders(),getContent(), andtoArray()with defensive try/catch aroundgetStatusCode()TraceableHttpClient::reset()clears cached tracer- UTF-8 safe SQL truncation in span names via
mb_substr() - Replaced
strtok()with statelesspreg_match()inSqlOperationExtractor::extract() - Clarified
doctrine_record_statementsconfig description for raw SQL safety - Dedicated
TracedResponseTestcovering getInfo, getInnerResponse, getSpan, throw=false, toArray(false), and __destruct OpenTelemetryBundleTestfor getPath(), build(), and VERSION constantHeadersPropagationSetterTestandResponsePropagationSetterTestunit tests- Sub-request INTERNAL span tests, incoming trace context propagation tests,
service.versionattribute test ConsumedByWorkerStampconsume test, empty trace context stamp test- Doctrine extension registration tests (
doctrine_enabled,doctrine_record_statements, tracer name wiring) - Extension
prepend()tests for Messenger middleware auto-registration - Malformed URL fallback test, REQUEST_TIME_FLOAT start timestamp test
- 172 unit tests with 419 assertions (up from 58/131)
1.0.1 - 2026-03-15
- GitHub Actions CI workflow (PHPStan + PHPUnit across PHP 8.1/8.2/8.4 and Symfony 6.4/7.4/8.0)
- Packagist version and downloads badges in README
.editorconfigfor consistent formattingCONTRIBUTING.mdwith setup instructions and coding standards
- PHPUnit bootstrap path for standalone repo (
vendor/autoload.php) .gitattributesnow excludes CI, changelog, and contributor docs from Composer installs
1.0.0 - 2026-03-13
- Automatic HTTP tracing with SERVER spans, route templates, and semantic conventions
- HttpClient instrumentation with CLIENT spans and W3C Trace Context propagation
- Symfony Messenger instrumentation with CONSUMER spans and trace context across transports
- Response propagation (Server-Timing, traceresponse headers)
Tracinghelper for one-liner manual span creation viaTracingInterface- Body size attributes (
http.request.body.size,http.response.body.size) - Client IP recording (
client.address) with GDPR toggle - Bundle version tracking (
service.version) - Sub-request support (INTERNAL spans)
- Exception recording with status and message
- Configurable excluded paths, error status threshold, and per-feature toggles
- Messenger root spans for task-oriented backends (Traceway, Sentry)
- 58 unit tests with 131 assertions