0034. Scope Sentry to error tracking, not tracing¶
Date: 2026-09-23 Status: Accepted
Context¶
Conflux already has an observability tool for distributed tracing: the OpenTelemetry PHP SDK,
adopted in ADR-0011, instruments every
HTTP request, queue job, and CLI command with a span, propagates trace context across process
boundaries via the W3C Trace Context standard, and exports to VictoriaTraces. What Conflux has
never had is dedicated exception tracking: uncaught exceptions are rendered (via
App\Exceptions\ApiProblemRenderer for API requests) and logged, but there is no aggregation, no
grouping of recurring errors, no alerting, and no UI for triaging them beyond reading log files.
Sentry is being added to close that gap. Sentry's Laravel SDK (sentry/sentry-laravel), however,
does not only capture exceptions — by default it also instruments performance tracing (HTTP
requests, queue jobs, outgoing HTTP calls, database queries) via its own traces_sample_rate
setting, using its own span model layered on top of the same events OpenTelemetry already spans.
Enabling both would mean two SDKs independently creating spans for the same request, with no
shared trace ID between them and no benefit over the tracing Conflux already has.
Decision¶
Sentry is scoped to error capture only. config/sentry.php's traces_sample_rate and
profiles_sample_rate are left null (Sentry's disabled state) rather than set from an env
default, so a deployment cannot enable Sentry tracing by simply setting SENTRY_LARAVEL_DSN
without a deliberate, separate decision to also set SENTRY_TRACES_SAMPLE_RATE. OpenTelemetry
remains the sole tracer.
bootstrap/app.php's withExceptions() registers two reportable callbacks:
App\Exceptions\SentryContext::register($exceptions) runs first, followed by
Sentry\Laravel\Integration::handles($exceptions). SentryContext enriches the Sentry scope with
the acting principal before Sentry's own handler captures the exception, so every reported issue
carries who was affected:
- If the request was authenticated via the
connectionguard (anApp\Models\SystemConnection, Conflux's API caller), that connection becomes the Sentry "user" (id), tagged withconnection_idand, when the connection has one,tenant_id— connections can be tenant-less pre-assignment, so the tag is conditional rather than always present. - Otherwise, if there is an authenticated
App\Models\User(the Filament admin-panel guard), that user (id,email) becomes the Sentry "user". - Neither applies to queue-worker or console exceptions: there is no request and no authenticated
actor to attribute them to, the same limitation
ApiProblemRenderer::context()already accepts for its own logging.
Consequences¶
Conflux now runs two observability SDKs side by side with a deliberately non-overlapping split: OpenTelemetry for tracing (spans, trace propagation, VictoriaTraces), Sentry for exception capture and triage. Neither instruments the other's concern, so there is no double span overhead and no risk of the two producing conflicting trace data for the same request.
The trade-off is that Sentry's own performance-monitoring features (transaction traces, slow-query
detection tied to Sentry's UI) are unavailable, since traces_sample_rate stays disabled. If
Sentry-side tracing is wanted later — its UI or alerting on slow transactions turns out to be
worth having alongside VictoriaTraces — that is a new decision, not a silent flip of this one, since
it reopens the double-instrumentation question this ADR closes.
Context enrichment is HTTP-only. An exception thrown inside a queue job or a scheduled command
still reaches Sentry, but without a connection_id, tenant_id, or user tag — triaging those
requires the job's own log context (correlated via trace_id/span_id, per
Tracing) rather than the Sentry issue's "user" field.