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

Logger Laravel Package

alt-three/logger

Alt Three Logger is a context-aware logging package for Laravel that extends the standard PSR-3 logger with fluent helpers like withContext, withTags, and withUser, so every log entry can carry user IDs, tags, and custom metadata without repeating yourself at each call site.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: the repository is archived (last release January 2018) and targets Laravel 5.x only. It still installs and runs on a legacy L5 app, but treat it as frozen code you may have to patch, not a dependency you can upgrade. If you are starting a new project, skip to the migration note in Gotchas and Tips — Laravel's own Log facade covers the same ground.

Assuming you are maintaining an L5 app:

composer require alt-three/logger

On Laravel 5.5+ package discovery registers AltThree\Logger\LoggerServiceProvider automatically. On 5.0–5.4 add it by hand:

// config/app.php
'providers' => [
    // ...
    AltThree\Logger\LoggerServiceProvider::class,
],

'aliases' => [
    // ...
    'Logger' => AltThree\Logger\Facades\Logger::class,
],

Where to look first: the package is small and the README is thinner than you'd like, so read the source before you wire anything up — specifically src/Logger.php (the wrapper's public surface) and src/LoggerServiceProvider.php (what it binds into the container and under which keys). Confirm two things: whether the class is bound as a concrete Logger type or as a Psr\Log\LoggerInterface, and whether a config file ships that needs php artisan vendor:publish --provider="AltThree\Logger\LoggerServiceProvider".

First use case: inject the wrapper into a service and log one structured line, then verify it lands in storage/logs/laravel.log.

use AltThree\Logger\Logger;

class InvoiceSync
{
    public function __construct(private Logger $logger) {}

    public function handle(array $invoice): void
    {
        $this->logger->info('invoice.synced', [
            'invoice_id' => $invoice['id'],
            'customer_id' => $invoice['customer_id'],
            'amount' => $invoice['total'],
        ]);
    }
}

Smoke test it from php artisan tinker:

app(AltThree\Logger\Logger::class)->info('logger.wired', ['ok' => true]);

If nothing appears, the problem is almost always binding resolution (see Gotchas) rather than the wrapper itself.

Implementation Patterns

Constructor injection over the facade. The wrapper exists so you can depend on a narrow, mockable type instead of the global Log facade. Inject it into controllers, jobs, console commands, and listeners. Reserve the Logger facade for one-off calls inside closures or legacy code you can't constructor-inject.

PSR-3 style calls with context arrays. Prefer ->info($message, $context) over string interpolation. Context arrays survive JSON formatters, are filterable in Logstash/Datadog, and don't blow up when a value is null. Use a dotted event-name convention (invoice.synced, webhook.received) so you can grep/query by event rather than by prose.

Log-and-rethrow in the exception handler. The wrapper's real day-to-day value is a single seam for error reporting. Wrap the handler:

public function report(Throwable $e)
{
    $this->logger->error('unhandled.exception', [
        'exception' => get_class($e),
        'message' => $e->getMessage(),
        'file' => $e->getFile().':'.$e->getLine(),
    ]);

    parent::report($e);
}

Keep the raw exception object out of the context array — most formatters will try to serialize it and produce megabytes or recursion.

Request-scoped context via a middleware. Attach request id / user id once, then log freely downstream:

$this->logger->info('http.request', [
    'method' => $request->method(),
    'path' => $request->path(),
    'status' => $response->getStatusCode(),
    'ms' => round((microtime(true) - $start) * 1000, 2),
]);

Note that the wrapper in this package is stateless — it does not carry a persistent context bag the way Monolog's pushProcessor does. If you want sticky context, keep it in your own class and merge it in, or add a processor to the underlying Monolog instance.

Testing: swap the wrapper, assert the calls. Because it's a container binding, tests are clean:

$mock = Mockery::mock(AltThree\Logger\Logger::class);
$mock->shouldReceive('warning')->once()->with('stock.low', ['sku' => 'ABC']);
$this->app->instance(AltThree\Logger\Logger::class, $mock);

This is the main reason to prefer the wrapper over Log::spy() — you get a typed double and don't have to assert against facade internals.

Backend swapping without touching call sites. Bind your own implementation of the wrapper (or a subclass) in a service provider for staging/production differences — e.g. a version that also forwards to Sentry or a Slack webhook. That is the package's advertised extension point and the cleanest way to add behaviour without forking.

Gotchas and Tips

  • It is abandoned, and psr/log will bite you. Later psr/log releases (v2/v3) changed LoggerInterface method signatures to log($level, string|\Stringable $message, array $context = []). If any dependency pulls in a modern psr/log, the wrapper's untyped methods may still satisfy the interface at runtime (PHP contravariance is lenient here until you hit Stringable), but static analysis and strict tooling will complain. Pin psr/log in composer.json if you must keep it, and expect to patch the class.

  • Double registration. On 5.5+ auto-discovery plus a manual config/app.php entry produces two provider registrations and, depending on how the binding is written, an inconsistent container state. Pick one.

  • Two competing bindings. Laravel already binds Psr\Log\LoggerInterface and Illuminate\Contracts\Logging\Log. The package adds its own. Be explicit everywhere — app(AltThree\Logger\Logger::class) — or you'll get the base logger in some classes and the wrapper in others and wonder why your test double isn't hit. Audit with php artisan tinker and app()->make(...) on each type.

  • The wrapper does not filter levels. LOG_LEVEL / config('app.log_level') filters at the Monolog handler. Calling ->debug() in production is cheap, but it isn't dropped by the wrapper — it's dropped downstream. Don't build logic on "the wrapper swallows it."

  • File handler throughput. Default single/daily file channels take a lock per write. If you're logging per-request-plus-per-query on a hot endpoint, switch to daily with a reasonable days retention, and never log inside a tight loop — batch the context and emit one line.

  • Context is serialized; secrets are not safe. Arrays get json_encoded or var_exportd by the formatter. Strip Authorization headers, passwords, and tokens in middleware before they reach the logger. This is the single most common production incident with log wrappers.

  • Placeholder interpolation is the handler's job. ->info('user {id} logged in', ['id' => 5]) only interpolates if the underlying formatter does PSR-3 substitution; a plain LineFormatter won't. Test one line end-to-end on a fresh environment rather than assuming.

  • Debugging silence. If nothing is logged, check in this order: (1) is the provider registered at all, (2) does app(Logger::class) resolve, (3) is the wrapped handler's level high enough, (4) file permissions on storage/logs. Nine times out of ten it's step 2.

  • Migration path. The wrapper's value is thin. When you're ready to leave it: replace use AltThree\Logger\Logger with Psr\Log\LoggerInterface, swap the constructor property, drop the provider, and delete the dependency. Laravel's Log facade is already PSR-3 compatible, and Log::channel('x')/Log::stack([...]) (6.x+) give you multi-handler routing the wrapper never had. Budget an afternoon for a mid-size app.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings