xakki / laralog
Laravel log drivers
Requires
- php: ^8.3|^8.4|^8.5
- ext-mbstring: *
- laravel/framework: ^10|^11|^12
- psr/log: ^3.0
Requires (Dev)
- ext-sockets: *
- larastan/larastan: *
- phpunit/phpunit: *
- slevomat/coding-standard: *
- squizlabs/php_codesniffer: *
README
Structured production logging for Laravel — a drop-in LogManager, custom drivers, formatters
and Monolog processors that enrich every record and keep it safe and queryable.
Documentation
- Logging Rules (RU) — language-agnostic spec for production logging; LaraLog as the reference PHP/Laravel implementation
- Graylog integration
- Python logging via fluent-bit — shipping Python stdlib
logginginto the same Graylog GELF pipeline (direct GELF + fluent-bit text-parse paths)
What you get
The custom LogManager enriches every record (see appendContext):
log_type— who emitted it:logger(code) /trigger(PHP error) /exception/fatal(spec §4.3.1)request_id— per-request correlation id (fromX-Request-IDor generated), reset per queue jobfile+trace— call site and a stack trace whose depth scales with level (vendor/Monolog/Illuminate frames stripped)message_len, andmemory_usage/memory_peakwhenallow_memoryis on- type coercion of context values (
*_id→int,is_*→bool, …) and optionalsnake_caseof keys - credential redaction —
password/token/api_key/… masked to***by field name, on by default
Plus the ExtraProcessor adds stable per-process fields (app_name, app_env, app_ver,
log_ver, tier, release_*, host_*, …) from config('logger.extra').
Requires
- php: ^8.3 | ^8.4 | ^8.5
- laravel/framework: ^10 | ^11 | ^12
- psr/log: ^3.0
- ext-mbstring
Install
composer require xakki/laralog
Configure
- Add
Providers/AppServiceProvider.php
class AppServiceProvider extends ServiceProvider { public function register(): void { ... $this->app->singleton('log', fn ($app) => new \Xakki\LaraLog\LogManager($app)); ... } }
or bootstrap/app.php
$app->singleton( \Illuminate\Log\LogManager::class, \Xakki\LaraLog\LogManager::class );
Configuration (config/logger.php)
Recommended: register the provider. It merges config defaults, resets request_id per
queue job, and (opt-in) installs the log_type capture handlers:
$this->app->register(\Xakki\LaraLog\LaraLogServiceProvider::class);
Without it (and without a published config/logger.php) ExtraProcessor has no
config('logger.extra') to read, so the per-process extra fields are omitted.
Publish the config to tune it:
php artisan vendor:publish --tag=laralog-config
Read env() inside config/logger.php, never at log-time — config values are baked in by
php artisan config:cache, while a runtime env() returns null once config is cached.
| Key | Default | What |
|---|---|---|
message_limit |
3024 |
max message length kept |
allow_memory |
false |
attach memory_usage / memory_peak |
extra |
app_name/app_env/app_ver/log_ver + tier/release_*/ from env |
stable per-process fields (§4.2); ExtraProcessor copies this whole array onto every record (empty values dropped). Add your own keys here. |
trace.excluded_partials |
['Monolog','Illuminate/Log/','vendor/'] |
frames stripped from file/trace |
trace.depth |
warning:5, error:10, critical:20 |
stack-trace frames by level (§3.7) |
trace.arg_limit |
128 |
max chars per stringified trace arg |
redact |
[] |
extra secret needles, merged with the built-in denylist (§2) |
snake_case |
false |
lowercase + snake_case context keys (§4.7); breaking — opt-in |
capture_handlers |
false |
install chained error/exception/shutdown handlers to set log_type (§4.3.1) |
capture_handlersinstalls global PHP handlers chained to the framework's. Enable it only after exercising your error / exception / fatal paths — seedocs/LoggingRules.md§4.3.1. Credential redaction (redact+ built-ins) is on by default;password,token,api_key,authorization,cookie, … are masked to***by field name.
Slow SQL query collect
- Add
Providers/AppServiceProvider.php
class AppServiceProvider extends ServiceProvider { public function register(): void { ... $this->app->register(\Xakki\LaraLog\SqlLogServiceProvider::class); ... } }
- Add into config/logging.php (these are the exact keys
SqlLogServiceProviderreads — the oldsqlSlowLogMsexample was wrong, leaving the threshold at 0 → every query logged):
'dailySqlStack' => env('LOG_DAILY_SQL'), 'sqlSlowLogAll' => (int) env('SQL_SLOW_LOG_ALL', 500), // ms — all statements 'sqlSlowLogForSelect' => (int) env('SQL_SLOW_LOG_FOR_SELECT', 200), // ms — SELECT only
Logged fields (spec §6.2): db_table, db_time_ms, db_bindings (only when APP_DEBUG,
≤20 bindings, strings >512 chars elided), sql_type, tag: sql.
Syslog UDP channel
- Add into config/logging.php (channels)
'syslog-udp' => [ 'driver' => 'monolog', 'handler' => \Monolog\Handler\SyslogUdpHandler::class, 'handler_with' => [ 'host' => env('SYSLOG_JSON_HOST', '127.0.0.1'), 'port' => (int) env('SYSLOG_JSON_PORT', 5140), 'facility' => LOG_LOCAL0, 'level' => env('LOG_LEVEL', 'info'), ], 'formatter' => \Xakki\LaraLog\Formatters\CustomFormatter::class, 'formatter_with' => [ 'dateFormat' => 'Y-m-d\TH:i:s.uP', ], 'processors' => [ \Xakki\LaraLog\Processor\ExtraProcessor::class, \Monolog\Processor\LoadAverageProcessor::class, \Monolog\Processor\WebProcessor::class ], ],
By default, the log size is limited to 1400 characters - a longer log will be lost.
Json channel
- Add into config/logging.php (channels)
'json' => [ 'driver' => 'monolog', 'handler' => \Monolog\Handler\StreamHandler::class, 'handler_with' => [ 'stream' => storage_path('logs/laravel.json'), 'level' => env('LOG_LEVEL', 'info'), ], 'formatter' => \Monolog\Formatter\JsonFormatter::class, 'processors' => [ \Xakki\LaraLog\Processor\ExtraProcessor::class, \Monolog\Processor\LoadAverageProcessor::class, \Monolog\Processor\WebProcessor::class ], ],
Redis channel
- Add into config/logging.php (channels)
'redis' => [ 'driver' => 'custom', 'via' => \Xakki\LaraLog\Drivers\RedisLogger::class, 'connection' => env('LOG_REDIS_CONNECTION', 'default'), 'level' => env('LOG_LEVEL', 'debug'), 'capSize' => env('REDIS_LOG_CAP_SIZE', 10000), 'processors' => [ \Xakki\LaraLog\Processor\ExtraProcessor::class, \Monolog\Processor\LoadAverageProcessor::class, \Monolog\Processor\WebProcessor::class ], ],
If u want log to another redis connection
- Add config into config/database.php (redis)
'log-redis' => [ 'url' => env('LOG_REDIS_URL'), 'host' => env('LOG_REDIS_HOST', '127.0.0.1'), 'username' => env('LOG_REDIS_USERNAME'), 'password' => env('LOG_REDIS_PASSWORD'), 'port' => env('LOG_REDIS_PORT', '6379'), 'database' => env('LOG_REDIS_DB', '0'), 'prefix' => env('LOG_REDIS_PREFIX', 'common:'), ],
- Add to .env
LOG_REDIS_CONNECTION=log-redis
U can add custom options, like LOG_REDIS_PREFIX, LOG_REDIS_PORT and etc
Telegram channel
- Add into config/logging.php (channels)
'telegram' => [ // https://api.telegram.org/bot[BOT_TOKEN]/sendMessage?chat_id=@[USERNAME_CHANNEL]&text=тест 'driver' => 'monolog', 'formatter' => Monolog\Formatter\LineFormatter::class, 'formatter_with' => [ 'format' => env('APP_ENV') . ". %message%", // 'format' => "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n", ], 'handler' => \Monolog\Handler\TelegramBotHandler::class, 'handler_with' => [ 'apiKey' => env('TELEGRAM_API_KEY'), 'channel' => env('TELEGRAM_CHANNEL'), 'parseMode' => 'Markdown', ], ],
Recommended stack
'stack' => [ 'driver' => 'stack', 'channels' => ['stderr'], //'ignore_exceptions' => true, ],