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

svc/log-bundle

svc/log-bundle offers a lightweight logging integration for Laravel and PHP apps, with configurable handlers for capturing, formatting, and routing application logs. A simple way to add consistent logging without extra boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check before you start: svc/log-bundle is a Symfony bundle (DI Extension, Resources/config, likely Doctrine ORM/DBAL persistence), not a Laravel package. There is no ServiceProvider, no auto-discovery, no Eloquent model, and no publishable config/*.php. As a Laravel developer your two realistic paths are:

  1. Fork the pattern, not the dependency — copy the Entity/, Repository/, and the write path into an Eloquent model + service behind your own interface. This is what most Laravel teams should do.
  2. Consume it only if you already run Symfony (a sidecar service, a legacy microservice) and you are logging into that service's DB.

Do not composer require it into a Laravel app and assume it wires itself up. It won't.

Step 1 — read the source in this order before writing a single line:

  • composer.json — the single most important file. Check every symfony/* and doctrine/* constraint against your Laravel's resolved Symfony set (composer why-not svc/log-bundle <version>). Laravel pins Symfony components; a bundle pinning conflicting ranges is the most common failure here.
  • DependencyInjection/Configuration.php — the actual config tree. This is your contract.
  • Resources/config/services.* — service ids and aliases you'd inject (or mirror).
  • Entity/ — the column set. Read this before designing any dashboard or query.
  • Repository/ — the aggregation queries that already exist. Do not rewrite them.
  • README.md — last, and trust it least. The description.md ("PSR-3/Monolog channel wiring") and the assessment ("DB table event/stat logger") describe two different packages. Resolve this by reading the source.

Step 2 — time-box a spike (≤ 1 day). In a branch:

composer require svc/log-bundle --no-scripts
# If this explodes, stop. That is your answer.

Then, if you're forking into Laravel, stand up the minimum:

// database/migrations/xxxx_create_event_logs_table.php
Schema::create('event_logs', function (Blueprint $t) {
    $t->id();
    $t->string('name', 100)->index();
    $t->json('context')->nullable();
    $t->string('user_id', 64)->nullable()->index();
    $t->string('ip', 45)->nullable();
    $t->timestamp('occurred_at')->useCurrent()->index();
    // composite index for the queries you actually plan to run:
    $t->index(['name', 'occurred_at']);
});

First use case — auth events. Log login.success, login.failed, password.reset, then build a "last 30 days per event" count. This exercises the entire loop in an afternoon: write path → schema → aggregate query → retention. If it survives that, it's worth keeping; if not, you've lost a day.


Implementation Patterns

1. Never let Svc\LogBundle\... leak into controllers or domain code. With 2 stars and 0 dependents the public API is not a contract — it's a suggestion. Couple through exactly one file:

interface EventLogger {
    public function log(string $name, array $context = []): void;
}

final class DbEventLogger implements EventLogger
{
    public function __construct(
        private Connection $db,
        private LoggerInterface $fallback,
    ) {}

    public function log(string $name, array $context = []): void
    {
        try {
            $this->db->table('event_logs')->insert([
                'name'        => $name,
                'context'     => json_encode($context),
                'user_id'     => auth()->id(),
                'ip'          => request()->ip(),
                'occurred_at' => now(),
            ]);
        } catch (\Throwable $e) {
            // Logging must never break the request.
            $this->fallback->warning('event_log write failed', ['e' => $e]);
        }
    }
}

Bind it in a ServiceProvider: $this->app->bind(EventLogger::class, DbEventLogger::class);. One file to change on the day you swap the implementation.

2. Log from middleware, not from scattered call sites. Log one row per request in a terminable middleware (route, status, duration, user id) and keep explicit business events few and intentional. Scattering $logger->log() through controllers gets you 40 rows per page load and a table nobody can reason about.

3. Get the write off the request path. dispatchAfterResponse() (Laravel) or a queued job (Symfony Messenger / kernel.terminate) — the point is that the insert happens after the response is sent and outside the request transaction.

class LogEventMiddleware
{
    public function handle($request, Closure $next)
    {
        $response = $next($request);
        dispatch(function () use ($request, $response) {
            app(EventLogger::class)->log('http.request', [
                'route'   => $request->route()?->getName(),
                'status'  => $response->getStatusCode(),
                'ms'      => (int) ((microtime(true) - LARAVEL_START) * 1000),
            ]);
        })->afterResponse();

        return $response;
    }
}

4. Aggregate with SQL, not PHP. Statistics belong in the database — that is literally the value proposition of this package. selectRaw('name, count(*) c')->groupBy('name'), not Model::all()->countBy('name'). If you're forking, copy whatever is in the bundle's Repository/ folder; it's the part most worth stealing.

5. Wrap the call site in a feature flag. Config-driven (config('features.event_log')) so you can kill the write during an incident without a deploy.

6. Fake it in tests. EventLogger being one interface means EventLogger::fake()-style test doubles are trivial and application tests never touch a log row.


Gotchas and Tips

The description and the assessment disagree about what the package is. description.md describes PSR-3/Monolog channel and handler config; the assessment describes DB-backed event/stat logging. These are different products. Read composer.json and src/ before believing either. This single discrepancy is the biggest risk in the whole evaluation.

No auto-discovery. Even on Symfony, the bundle must be registered in config/bundles.php; on Laravel there is no registration path at all. There is no extra.laravel key — Composer will silently install it and nothing will happen.

Doctrine is likely baked in. If the write path uses EntityManager rather than Connection, an Eloquent port is a rewrite of the write path, not an adapter. Scope doctrine/dbal narrowly if you try to bridge it — full ORM adoption into a Laravel app is a trap.

Logging inside a transaction gets rolled back with it. If you log order.created inside the DB transaction and it later rolls back, the log lies. Log after commit (DB::afterCommit(...)).

Fail-open, always. If the log table is missing, the DB is down, or the connection is exhausted, the application must keep serving. Every write goes through a try/catch with a file-log fallback. A logging package that becomes an availability risk is worse than no logging.

Watch the primary connection. Event rows grow linearly with traffic and writes compete with application queries. Put logs on a dedicated connection in config/database.php so log volume cannot starve the app, and plan retention (partition by month, or a scheduled delete where occurred_at < now() - 90 days) before the table hits a few million rows.

Schema is yours, not the bundle's. Do not run doctrine:schema:update against production and do not rely on any shipped migration. Own the migration as a first-class Laravel migration with the composite indexes your queries need — you'll be querying (name, occurred_at) almost immediately and a missing index on a growing table is a slow-motion outage.

Debugging "no rows written": work backwards — (1) is the service bound in the container? (2) is the middleware registered on the route in Kernel/bootstrap/app.php? (3) did the queued job actually run (check failed_jobs)? (4) is the write swallowing an exception in the catch block? Temporarily re-throw in local to see it.

Treat upstream as dead on arrival. One maintainer, 2 stars, no releases guarantee. Fork to a pinned commit, name an internal owner, document the "upstream is gone" contingency, and keep the swap surface at the EventLogger interface. MIT makes this trivially legal; the cost is engineering time, not licensing.

Do not route audit/compliance events through this. No evidence of security review, schema stability, or a disclosure process. For SOC 2-style audit trails use spatie/laravel-activitylog or owen-it/laravel-auditing, both of which have the maintenance you're implicitly relying on here.

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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect