bureaupartners / laravel-loki-logging
Zero-config Grafana Loki log driver for Laravel. Real-time, fault-tolerant, with basic auth support.
Installs: 342
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/bureaupartners/laravel-loki-logging
Requires
- php: ^8.2
- ext-curl: *
- ext-json: *
- illuminate/support: ^10.0|^11.0|^12.0
- monolog/monolog: ^3.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0
- pestphp/pest: ^2.0|^3.0
README
Zero-config Grafana Loki log driver for Laravel. Sends logs in real time to Loki via HTTP, with basic auth and built-in fault tolerance.
Features
- Zero-config — just set
.env, done - Real-time — no cron job or scheduled command needed
- Fault-tolerant — if Loki is down, your app keeps running
- Basic Auth — supports authentication via reverse proxy
- Batching — buffers logs and sends them efficiently in batches
- Auto-labels — app, environment, server, level, channel are added automatically
- No dependencies — uses PHP's built-in cURL, no Guzzle required
Requirements
- PHP 8.2+
- Laravel 10, 11, or 12
- PHP
ext-curl - A Grafana Loki server
Installation
composer require bureaupartners/laravel-loki-logging
The service provider is loaded automatically (Laravel auto-discovery).
Configuration
1. Add the Loki channel to config/logging.php
'channels' => [ 'stack' => [ 'driver' => 'stack', 'channels' => explode(',', env('LOG_STACK', 'daily,loki')), 'ignore_exceptions' => false, ], 'loki' => [ 'driver' => 'loki', 'level' => env('LOG_LEVEL', 'debug'), ], // ... your other channels ],
2. Configure your .env
LOG_CHANNEL=stack LOG_STACK=daily,loki LOG_LEVEL=debug # Loki server URL LOKI_URL=http://10.0.0.50:3100 # Unique name for this server LOKI_SERVER=web-01 # Optional: basic auth (if you have a proxy with auth) LOKI_AUTH_USER= LOKI_AUTH_PASSWORD=
Done! All Log:: calls now go to Loki and to your daily log file.
3. Optional: publish the config
php artisan vendor:publish --tag=loki-config
This creates config/loki.php where you can set extra labels, batch size, and timeouts.
Usage
Just use Laravel's standard logging:
use Illuminate\Support\Facades\Log; Log::info('User logged in', ['user_id' => 123]); Log::warning('Low stock', ['product' => 'SKU-456', 'stock' => 3]); Log::error('Payment failed', ['order_id' => 789, 'gateway' => 'mollie']); Log::debug('SQL query', ['query' => $sql, 'time_ms' => 45]);
Context is sent automatically as key=value pairs, so you can filter on it in Grafana.
Querying in Grafana
Open Grafana → Explore → select Loki as the datasource.
# All logs from a server
{server="web-01"}
# Only errors from your app
{app="my-app", level="error"}
# Search by text
{app="my-app"} |= "Payment failed"
# Errors from a specific server
{server="web-01", level="error"}
# Filter by multiple levels
{app="my-app", level=~"error|warning"}
# Search by context values
{app="my-app"} |= "order_id=789"
# Count errors per 5 minutes (for dashboards/alerts)
count_over_time({app="my-app", level="error"}[5m])
Advanced configuration
Extra labels
In config/loki.php:
'labels' => [ 'team' => 'backend', 'version' => '2.1.0', ],
Or per channel in config/logging.php:
'loki' => [ 'driver' => 'loki', 'level' => 'debug', 'labels' => [ 'service' => 'api', ], ],
Multiple Loki channels
'loki-errors' => [ 'driver' => 'loki', 'level' => 'error', 'url' => 'http://loki-production:3100', ], 'loki-debug' => [ 'driver' => 'loki', 'level' => 'debug', 'url' => 'http://loki-staging:3100', ],
Channel config options
| Option | Env variable | Default | Description |
|---|---|---|---|
url |
LOKI_URL |
http://localhost:3100 |
Loki server URL |
level |
LOG_LEVEL |
debug |
Minimum log level |
auth_user |
LOKI_AUTH_USER |
|
Basic auth username |
auth_password |
LOKI_AUTH_PASSWORD |
|
Basic auth password |
labels |
— | [] |
Extra labels |
batch_size |
LOKI_BATCH_SIZE |
10 |
Logs per batch |
timeout |
LOKI_TIMEOUT |
5 |
HTTP timeout (sec) |
connect_timeout |
LOKI_CONNECT_TIMEOUT |
2 |
Connect timeout (sec) |
How it works
Log::info()calls the Loki handler- The handler buffers the log line (until
batch_sizeis reached) - When the batch is full OR at the end of the PHP request → it sends everything to Loki via cURL
WhatFailureGroupHandlercatches all errors — your app always keeps running- Labels (app, server, level, etc.) are added automatically
License
MIT