log-laravel maintained by xakki
log-laravel
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.
Repository: Xakki/log-laravel · Composer: xakki/log-laravel
(formerly xakki/laralog). PHP namespace: Xakki\Log\Laravel (formerly Xakki\LaraLog).
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/log-laravel
Configure
- Add
Providers/AppServiceProvider.php
class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
...
$this->app->singleton('log', fn ($app) => new \Xakki\Log\Laravel\LogManager($app));
...
}
}
or bootstrap/app.php
$app->singleton(
\Illuminate\Log\LogManager::class,
\Xakki\Log\Laravel\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\Log\Laravel\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\Log\Laravel\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\Log\Laravel\Formatters\CustomFormatter::class,
'formatter_with' => [
'dateFormat' => 'Y-m-d\TH:i:s.uP',
],
'processors' => [
\Xakki\Log\Laravel\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\Log\Laravel\Processor\ExtraProcessor::class,
\Monolog\Processor\LoadAverageProcessor::class,
\Monolog\Processor\WebProcessor::class
],
],
Redis channel
- Add into config/logging.php (channels)
'redis' => [
'driver' => 'custom',
'via' => \Xakki\Log\Laravel\Drivers\RedisLogger::class,
'connection' => env('LOG_REDIS_CONNECTION', 'default'),
'level' => env('LOG_LEVEL', 'debug'),
'capSize' => env('REDIS_LOG_CAP_SIZE', 10000),
'processors' => [
\Xakki\Log\Laravel\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,
],
Related projects
Sibling logging libraries sharing one field contract (Logging Rules):
- log-symfony —
xakki/log-symfony. PSR-3 / Symfony / Monolog 3: context enrichment, type coercion, redaction. - log-php —
xakki/log-php. Plain PHP (no framework, PSR-3; PHP 5.6+ on branchmaster56): error/exception catcher with pluggable storages. - log-python —
xakki-log-python(PyPI). Python: structured JSON logging with the same field/type contract. - log-fluent —
xakki/log-fluent. Fluent Bit config bundle that ships these logs to Graylog (GELF) and Prometheus.