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

Log Bundle Laravel Package

etsglobal/log-bundle

ETSGlobal LogBundle adds normalized logging and request tracing to Symfony apps: automatic logger injection, token collection and context enrichment, custom exception levels, a Slack handler, and Guzzle/Symfony HttpClient token forwarding.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and register (Symfony-only). This is a Symfony bundle — there is no Laravel service provider, facade, or config publish. If you're coming from Laravel, map it mentally to Log::withContext() + a tracing middleware, then accept that it only runs inside a Symfony kernel.

composer require etsglobal/log-bundle
// config/bundles.php
return [
    // ...
    ETSGlobal\LogBundle\ETSGlobalLogBundle::class => ['all' => true],
];

2. Create the bundle config. The only documented key is app_name, but that value is what makes every log line attributable, so set it first:

# config/packages/ets_global_log.yaml
ets_global_log:
    app_name: my-app

3. Inspect what the bundle actually wired up before touching Monolog. The bundle advertises automatic enrichment and tracing, which means it registers processors/handlers and possibly a compiler pass:

bin/console debug:config ets_global_log
bin/console debug:container --tag=monolog.processor
bin/console debug:container | grep -i ets_global

Where to look first, in order:

Location Why
DependencyInjection/Configuration.php The real list of options — the README is a summary, not a reference
TokenCollection The central abstraction; everything else is plumbing around it
Monolog processors Where app_name and tokens get injected into context
Exception level processor Decide which exceptions get downgraded (e.g. NotFoundHttpException)
Guzzle middleware / HttpClient decorator The two outbound-propagation paths
CHANGELOG / release tags With 0 stars and 0 dependents, tags are your only compatibility contract

First use case: hit one HTTP endpoint, then confirm the log record carries app_name and a request token. Do that against a Monolog test handler or a local file handler before you point it at Slack or a production sink — that single check validates install, config, and processor wiring in one shot.

Implementation Patterns

Constructor-inject LoggerInterface and stop hand-writing context. The bundle's whole value is that app_name and tracing tokens are added for you. If you find yourself doing $logger->info($msg, ['app' => ...]), you're duplicating the processor's job and creating two competing field names.

final class PaymentHandler
{
    public function __construct(private LoggerInterface $logger) {}

    public function charge(Order $order): void
    {
        // app name + global/process tokens arrive automatically
        $this->logger->info('Charging order', ['order_id' => $order->id()]);
    }
}

Read and write tokens through TokenCollection, not raw headers. Treat it as the single source of truth. Set a token once at the edge of your app; let downstream code read it when it needs to attach a correlation id to a domain event, an outbound call, or a queued message.

Inbound HTTP tracing is the default path. Point your ingress (nginx, ALB, API gateway) at the bundle's expected header, and the bundle turns it into the global token for the request lifetime. The practical workflow is: client sends token → you log with it → you forward it on every egress. If a service in the middle drops it, tracing silently dies there, so add a test that asserts the header survives a proxy hop.

Outbound propagation — pick Guzzle or Symfony HttpClient, not both.

// Guzzle: push the bundle's middleware onto the handler stack of the client
// that actually talks to sibling services (not your third-party SDK clients).
$stack = HandlerStack::create();
$stack->push($tokenMiddleware);
$client = new Client(['handler' => $stack]);

For Symfony HttpClient, register the bundle's decorator against the scoped clients that call your own services:

# config/services.yaml
services:
    app.http.internal:
        class: Symfony\Component\HttpClient\ScopingHttpClient
        # arguments: ...
        decorates: 'http_client'

Be deliberate: decorating the global http_client sends your internal tracing token to Stripe, Twilio, and every webhook. Scope it.

Long-running processes need an explicit token lifecycle. For console commands, workers, and daemons, initialize a process token at start, regenerate it per unit of work (per message/job), and clear it at the end. This is the pattern where most real incidents happen: state that leaks between jobs produces log lines attributed to the wrong message.

Slack handler for actionable alerts only. The bundle's Slack handler exists to shape and filter messages. Configure it on a dedicated channel (e.g. alerts) at critical/error, and let the exception-level processor drop 404s and validation failures before they ever reach Slack. Pair it with a normal file/aggregator handler for everything else.

Exception level processor as a noise-reduction layer. Map expected exception classes to lower levels (NotFoundHttpException, AccessDeniedHttpException, domain validation exceptions) so real 500s remain visible.

Gotchas and Tips

This is not a Laravel package, and porting it isn't free. There's no composer require path into a Laravel app. If you're evaluating it for a Laravel codebase, treat it as a design reference: the equivalents are a Context/Log::withContext() call in middleware, a Tap on the Monolog channel, and Guzzle middleware for header forwarding. Re-implementing those three pieces is cheaper than maintaining a Symfony bundle shim.

Adoption risk is the headline. Zero stars, zero dependents, one vendor, and a most-recent-release date that you should verify against the actual tag before trusting it. Pin an exact version, read the diff between tags, and wrap TokenCollection behind a small interface of your own so an upstream abandonment is a one-file swap rather than a hundred call sites.

Worker/swoole/queue state leakage. Anything that keeps a process alive across requests can carry a stale token into the next job. Explicitly reset at job boundaries; don't rely on the bundle noticing.

Async messaging is not covered by the advertised features. HTTP (Guzzle/HttpClient) forwarding is documented; Messenger/queue propagation is not. Serialize the tokens into message headers or a stamp yourself, and reconstruct the TokenCollection on the consumer side before logging anything.

Header survival is where tracing breaks. Proxies strip unknown headers, CORS preflights reject them, and some load balancers normalize them. Verify the exact header name the bundle reads, add it to your proxy/allowlist config, and keep the name stable — renaming it after rollout invalidates every downstream correlation.

Monolog processor ordering matters. Enrichment processors must run before the handlers that format/ship the record. If app_name shows up on some lines and not others, it's an ordering or channel problem, not a bundle bug — check monolog.yaml handler/processor bindings per channel.

app_name isn't optional in practice. Without it, logs from multiple applications land in the same index with no discriminator, and the Slack handler's filters lose their anchor. Set it per-environment if you share a sink between staging and production.

Token cardinality can hurt. High-cardinality context fields (request tokens) inflate log storage and slow queries on some backends. Keep tokens as searchable fields but sample verbose debug-level records that carry them.

Extension points to prefer over forking: a custom Monolog processor for your own domain context, a custom token extractor if your ingress uses a non-standard header, and a decorator around the bundle's HttpClient decorator if you need conditional propagation. Forking the Slack handler is a last resort — its filters are usually sufficient.

Debugging recipe: debug:config for options, debug:container for wiring, then a functional test with a Monolog TestHandler asserting the exact context keys on a record. That test is also your regression guard when you eventually bump the pinned version.

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.
frankyso/ipaymu
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