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

Laravel Log To Db Laravel Package

danielme85/laravel-log-to-db

Laravel Log to DB adds a Monolog handler that writes application logs to a database table instead of files. Configure levels, channels, and a custom model, then query or browse logs with Eloquent for easier searching and monitoring.

Deep Wiki
Context7

Getting Started

composer require danielme85/laravel-log-to-db
php artisan vendor:publish --provider="Danielme85\LogToDb\LogToDbServiceProvider"
php artisan migrate

Publishing gives you two things: config/logtodb.php (connection, table/collection, retention, queue behaviour) and the migration that creates the log_events table. Read the config file first — it is the whole API surface of the package. Then open config/logging.php and register a channel:

'channels' => [
    'logtodb' => [
        'driver'  => 'monolog',
        'level'   => env('LOG_LEVEL', 'debug'),
        'handler' => \Danielme85\LogToDb\LogToDbHandler::class,
    ],
],

First useful use case: point your production stack channel at both daily and logtodb, so files stay as the durable fallback while the DB copy becomes greppable from any machine, an admin UI, or a php artisan tinker session:

'stack' => [
    'driver'   => 'stack',
    'channels' => ['daily', 'logtodb'],
],

Verify with Log::channel('logtodb')->info('hello', ['foo' => 'bar']); and then select * from log_events order by id desc limit 5;. If nothing lands, check storage/logs/laravel.log for a handler exception before touching the package config — Monolog swallows handler errors into the fallback channel.

Implementation Patterns

Isolate writes to their own connection. Logging is bursty and should never compete with your app queries for connections or slow down a web request. Add a logging connection in config/database.php pointing at a separate host/database, and set it in config/logtodb.php. This also makes retention trivial: you can truncate or drop partitions without touching application data.

Queue the writes for anything above low traffic. The synchronous path adds an INSERT to every request that logs. Enable the queue option in logtodb.php and point it at a dedicated logs queue so a backlog of log jobs never starves real work. Pair it with a supervisor worker: php artisan queue:work --queue=logs.

Use a stack to split by level. Keep debug/info in files and only persist warning and above to the DB. Two channels, two levels, one stack:

'channels' => [
    'db_errors' => [
        'driver'  => 'monolog',
        'level'   => 'warning',
        'handler' => \Danielme85\LogToDb\LogToDbHandler::class,
    ],
],

Enrich records with context, not string concatenation. The handler persists Monolog's context and extra arrays as JSON. Use Log::withContext(['order_id' => $id, 'tenant' => $tenant]) once per request/job, then log freely — every record carries the correlation fields and you can query them later. This is the single biggest day-to-day win over file logs.

Query it like data, not like text. Write a small read-side model or repository around the table:

LogEvent::where('level_name', 'ERROR')
    ->where('datetime', '>=', now()->subHour())
    ->where('context->tenant', $tenant)
    ->latest('datetime')
    ->paginate(50);

Use this for a support dashboard, an alerting cron, or a Slack notifier — far cheaper than parsing log files.

Retention from day one. Add a scheduled command that deletes rows older than N days (LogEvent::where('datetime', '<', now()->subDays(30))->delete() in chunks) and index datetime and level_name. Without this, the table becomes the largest thing in your database within months.

Wrap it for testability. Don't call Log::channel('logtodb') scattered through the codebase. Log through the default channel and configure the stack per environment; in phpunit.xml set LOG_CHANNEL=null or single, and use Log::spy() in tests rather than asserting on database rows.

Gotchas and Tips

  • Silent failures. If the database is unreachable, the handler throws inside Monolog and the record is lost. Always keep daily in the stack before the DB channel so there is a durable copy; ordering matters.
  • Queued logging hides errors. A failed log job disappears unless you monitor the failed_jobs table. Add an alert on failed jobs in the logs queue, or you'll discover the gap during an incident.
  • Context serialization. context and extra go through json_encode. Objects, closures, resources, and Eloquent models with circular relations will either throw or be silently lossy. Pass scalars and arrays; convert models to ['id' => $model->id] explicitly.
  • Sensitive data leakage. The DB copy is often read by more people than the log file was. Passwords, tokens, and full request payloads in context now live in a queryable table. Sanitize before logging or add a redaction layer.
  • Double level filtering. The channel level and the package's own configured level both apply. If records vanish, check both — the more restrictive wins, and it's a common source of "logging is broken" confusion.
  • Config caching. After editing logtodb.php or logging.php, run php artisan config:cache. Stale cached config is the usual reason a new channel appears to be ignored.
  • MongoDB is opt-in. SQL is the default path; the Mongo driver requires the mongodb/laravel-mongodb package installed and configured, plus type => 'mongodb' and a collection instead of a table. Don't assume the migration applies there — it doesn't.
  • Request context is absent outside HTTP. user_id and IP are only populated when there is an authenticated request. Artisan commands, queue workers, and scheduled tasks log null there — inject the actor yourself via withContext if you need traceability.
  • Exception stack traces are stored as text. They're searchable but not structured. Don't expect to filter by exception class unless you put it in context yourself: Log::error($e->getMessage(), ['exception_class' => get_class($e)]).
  • Handler class is an extension point. Because it's a plain Monolog handler, you can subclass it to add redaction, sampling, or a shouldHandle override for rate limiting — cleaner than post-processing the table.
  • Pin the version. This is a small, single-maintainer package (133 stars). Lock the minor version in composer.json and re-test the handler after Laravel major upgrades; the Monolog handler signature and Laravel's monolog driver wiring have changed across versions.
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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource