laravel-axiom-log maintained by devtime-ltd
Laravel Axiom Log
Batched log handler for Axiom in Laravel. Buffers log records in memory and flushes them as a single POST to Axiom's ingest API at end of request (or when batch size threshold is reached).
Also comes with an optional request logging middleware - see Request logging middleware below.
Installation
composer require devtime-ltd/laravel-axiom-log
The service provider is auto-discovered. It registers an axiom log channel automatically.
Configuration
Set environment variables:
AXIOM_LOG_TOKEN=your-api-token
AXIOM_LOG_DATASET=your-dataset
Optional:
AXIOM_LOG_CHANNEL_NAME=axiom
AXIOM_LOG_HOST=https://api.axiom.co
AXIOM_LOG_BATCH_SIZE=50
If desired, config file can be published via the following:
php artisan vendor:publish --tag=axiom-log-config
Using the Axiom log channel
Add axiom to your LOG_STACK:
LOG_STACK=single,axiom
Or use it directly:
Log::channel('axiom')->info('something happened', ['key' => 'value']);
Request logging middleware (optional)
LogRequest middleware that logs structured request data (method, URL, status, duration, query stats, memory). Independent of the log channel above, either or both can be used, and LogRequest can log to an alternative channel.
Setup
Register the middleware in bootstrap/app.php:
use DevtimeLtd\LaravelAxiomLog\LogRequest;
->withMiddleware(function (Middleware $middleware) {
$middleware->prepend(LogRequest::class);
})
Set the channel to log to:
LOG_REQUESTS_CHANNEL=axiom
Can also pass multiple channels (e.g. axiom,betterstack) for logging to multiple providers. Leave unset to disable, the middleware is a no-op without this env var.
Note: Logging happens inline in
handle()rather thanterminate(). We foundterminate()didn't fire in all hosting setups. The overhead should be minimal since the actual Axiom POST is batched and sent when the handler closes during PHP shutdown.
Logged fields
| Field | Description |
|---|---|
method |
HTTP method |
url |
Full URL |
path |
Request path |
route |
Named route (if any) |
route_params |
Route parameters (raw values, pre-binding) |
status |
Response status code |
content_type |
Response Content-Type |
response_size |
Response body size in bytes |
user_id |
Authenticated user ID (null if guest) |
ip |
Client IP (supports obfuscation, see below) |
user_agent |
User agent string |
referer |
Referer header |
duration_ms |
Total request time in milliseconds |
memory_peak_mb |
Peak memory usage |
query_count |
Number of database queries |
query_total_ms |
Total time spent in database queries |
slow_queries |
Queries exceeding the slow query threshold |
Request logging options
These options are set in the published config/axiom.php under request_logging:
Database query collection
Disable query measurement to skip the DB::listen() overhead:
'collect_queries' => false,
This omits query_count, query_total_ms, and slow_queries from the log entry. Default: true.
Slow query threshold
Threshold in milliseconds for capturing slow queries:
'slow_query_threshold' => 500,
Set to null to disable slow query collection while still tracking query_count and query_total_ms. Default: 100.
IP obfuscation
Mask client IPs using the built-in ObfuscateIp class. Supports four levels.
| Level | IPv4 example (198.51.100.123) |
IPv6 |
|---|---|---|
| 1 | 198.51.100.0 |
/96 |
| 2 | 198.51.0.0 |
/64 |
| 3 | 198.0.0.0 |
/32 |
| 4 | 0.0.0.0 |
:: |
use DevtimeLtd\LaravelAxiomLog\ObfuscateIp;
'obfuscate_ip' => ObfuscateIp::level(2),
You can also pass any callable for custom masking:
'obfuscate_ip' => fn (?string $ip) => 'redacted',
Default: false (no masking).
Extending log entries
Use LogRequest::extend() to add project-specific fields, or overwrite default ones. Call this in your AppServiceProvider::boot():
use DevtimeLtd\LaravelAxiomLog\LogRequest;
LogRequest::extend(function ($request, $response, $entry) {
$entry['tenant_id'] = $request->header('X-Tenant-ID');
return $entry;
});
Custom log entry
If you wish to completely replace the default log entries fields with your own, you can use LogRequest::using(). The callback receives the request, response, and a measurements array:
use DevtimeLtd\LaravelAxiomLog\LogRequest;
LogRequest::using(function ($request, $response, $measurements) {
return [
'method' => $request->method(),
'path' => $request->path(),
'status' => $response?->getStatusCode(),
'duration_ms' => $measurements['duration_ms'],
];
});
$measurements contains the collected metrics based on config: duration_ms, memory_peak_mb, and when query collection is enabled, query_count, query_total_ms, slow_queries.
Note that extend() runs after using(), so you can utilise both in a request lifecycle.
Testing
composer test
License
MIT