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

Wide Events Bundle Laravel Package

jblab/wide-events-bundle

Symfony bundle for wide events: accumulate rich contextual data throughout a request and dispatch it as a single structured event. Cuts event noise, simplifies logging and observability, and keeps handlers clean. Integrates with Symfony's event dispatcher and DI container.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle (jblab/wide-events-bundle), not a Laravel package. There is no service provider, no config/wide-events.php, and no auto-discovery. In a Laravel codebase you consume the framework-agnostic core (WideEvent, WideEventContext, emitters, sampling, normalization, OpenTelemetry correlation) and hand-wire the pieces yourself; the http, messenger, monolog, and bundle components are Symfony-specific and will not run under Laravel's container without adaptation.

First steps:

  1. Read the core commits first — WideEvent/WideEventContext, mutable context, emitters, normalization, sampling. That is the entire value proposition; everything else is framework glue you will be rewriting anyway.
  2. Inspect the Symfony HTTP subscriber to understand the intended lifecycle contract (start on request, mutate during handling, finalize + emit on response/exception). Mirror that lifecycle with Laravel middleware.
  3. Look at the Monolog emitter to see the structured payload shape — that is what you will end up logging.
  4. First use case: request-scoped wide event. One event per HTTP request, accumulate context (route, user id, tenant, feature flags, downstream calls), redact and normalize, then emit once at termination via your logging channel. This replaces a scatter of ad-hoc Log::info() calls with a single structured record.

Minimum viable Laravel wiring:

// AppServiceProvider::register()
$this->app->scoped(WideEventContext::class, fn () => new WideEventContext());
$this->app->scoped(WideEvent::class, fn ($app) => new WideEvent($app->make(WideEventContext::class)));
// Middleware
public function handle($request, Closure $next)
{
    $event = app(WideEvent::class);
    $event->context()->set('http.method', $request->method());
    $event->context()->set('http.route', $request->route()?->getName());

    try {
        $response = $next($request);
        $event->context()->set('http.status', $response->getStatusCode());
        return $response;
    } catch (Throwable $e) {
        $event->context()->set('exception.type', $e::class);
        throw $e;
    } finally {
        // emit here or in a terminating callback
    }
}

Implementation Patterns

Request lifecycle middleware. Bind WideEventContext and WideEvent as scoped so each request (and each Octane request, if applicable) gets a fresh instance. Start the event in middleware, mutate it from controllers/services, finalize and emit in finally or via terminating(). Keep emission in terminating() where possible so streamed responses and post-response work still contribute context.

Context accumulation as the primary API. The mutable context is the day-to-day surface: set('db.queries', 12), increment('cache.misses'), set('user.plan', $user->plan). Treat it like a request-scoped array with a well-known key namespace. Establish conventions early (http.*, db.*, queue.*, app.*) or the emitted payload becomes unqueryable.

Exception deferral. The Symfony side defers exception telemetry rather than emitting immediately. Do the same: record the exception into the context, let the event finalize once, and avoid double-emitting when the exception handler also logs. One request should produce one wide event, not one per error.

Tail-based sampling. Sampling is decided from the finished event, not at start. That means you can guarantee retention of errors, slow requests, and specific users regardless of the base rate. Configure the base rate low (e.g. 1–5%) and add retention rules: any event with an exception, any event above a duration threshold, any event tagged with a business-critical key. This is the single biggest cost lever — the sampling config is where you should spend your tuning effort.

Slow-event retention uses outcome duration. The fix in this release means the duration used for the "keep slow events" decision is the measured outcome duration, not a partial/wall-clock guess. Set your threshold against real p95/p99 latency for the route, not a global number; otherwise you either retain everything or nothing.

Normalization and redaction as a pipeline stage. The package normalizes and truncates payloads and redacts sensitive fields before emission. In Laravel, register your own redaction keys (password, token, authorization, card_number, secret, anything from config('app.key')-adjacent settings) and set explicit limits on string length, array depth, and array item count. Do this in one place — a WideEventFactory or a decorator around the context — rather than scattering substr() calls.

Emitters are an interface, not a destination. The in-memory emitter is for tests and local inspection; the Monolog emitter writes structured records to a channel. In Laravel, prefer binding a custom emitter that writes to a dedicated wide_events log channel with a JSON formatter, so these records do not pollute application logs and can be routed to a collector independently.

Queue jobs. The Symfony Messenger middleware plus correlation stamp is the analog you need to reproduce. On dispatch, capture the current event's correlation/request ID and attach it as a job property or payload stamp; on handle, hydrate a new wide event carrying the same correlation ID, plus queue.job, queue.attempt, queue.connection. Without this, async work is invisible and you end up with orphaned events.

OpenTelemetry correlation. The OTel commit correlates wide events with the active trace. If you already run OTel in Laravel (open-telemetry/ext-open-telemetry or keepsuit/laravel-opentelemetry), pull trace_id/span_id from the current span and set them on the context so you can pivot between the wide event and the trace. If you do not, set a generated request_id and propagate it through headers, logs, and queued jobs — the request-ID propagation feature is independently useful.

Testing. The in-memory emitter makes assertions straightforward: bind the in-memory emitter in a test case, run the request with $this->get(...), then assert on emitted events — presence, key values, redaction of a known sensitive field, and the sampling decision for a forced-slow or forced-error event.

Gotchas and Tips

The bundle is not installable as a Laravel package. Do not add it to composer.json expecting auto-discovery. Either depend on the core library directly, or vendor/copy the core classes into your app. Everything in bundle/, http/, and messenger/ assumes Symfony's EventDispatcher, RequestStack, KernelEvents, and DI compiler passes.

Tail sampling forces you to hold the event in memory. The whole record must survive until the response is finalized, which is fine for scalar context but dangerous if you stash large payloads (full request bodies, query result sets). Store IDs and summaries, not objects, and let normalization truncate as a backstop rather than as a primary strategy.

Redaction is not a substitute for discipline. Path-based/field-name redaction misses secrets nested under unexpected keys or embedded in free-text strings. Never put raw request bodies or headers into the context by default; add specific headers to an allowlist.

Normalization silently truncates. Truncation happens at emission, so debugging code that inspects the context before finalization will show data that never reaches your logs. If a field looks empty downstream, check length/depth/item limits before assuming the emitter dropped it.

Sampling decisions happen once, at the end. You cannot "upgrade" to retained mid-request. If an event becomes interesting after a terminal emit (e.g., in a terminating callback that fires late), it is already gone. Ensure emission is the very last step.

Slow-event duration semantics. Because retention now keys off outcome duration, events that failed early may not cross a latency threshold even though they are important. Pair the duration rule with an explicit "always retain on exception" and "always retain on non-2xx/3xx status" rule.

Exception deferral can mask ordering. If you defer telemetry to termination, the context at emit time may include keys set after the exception — sometimes useful, sometimes misleading. Stamp the exception with its own timestamp/duration and record the phase it occurred in.

Scoped bindings, not singletons. Binding WideEvent as a singleton will leak context across requests under Octane and across jobs under the queue worker. Use scoped and reset explicitly in App\Jobs\Middleware or a JobProcessed listener.

Queue correlation must be manual. There is no Laravel equivalent of the Messenger stamp in this release; you own serialization. Attach correlation IDs in the Job's payload via a dispatching helper and rehydrate in the job constructor or handle().

Component isolation is an architectural hint. The isolation-boundaries commit implies the core is meant to stand alone without Symfony. That is good news for Laravel adoption, but it also means no Laravel-side integration layer is coming from this project — budget for it as in-house glue, and keep it thin so you can swap to a first-party Laravel package later.

Extension points worth owning. Emitter interface (swap Monolog for a collector client), normalizer/redactor (add app-specific rules), and sampler (add business-critical retention predicates). These three are the seams most likely to need per-application behavior; wrap each in a Laravel binding rather than forking the core.

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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib