ddtraceweb/monolog-parser
Laravel-friendly Monolog parser that reads and normalizes Monolog log records/lines for easier searching, filtering, and display. Useful for building log viewers, dashboards, or importing Monolog output into your own storage or analysis pipeline.
Installation
composer require ddtraceweb/monolog-parser
Add to composer.json if not auto-loaded:
"autoload": {
"psr-4": {
"App\\": "app/",
"Vendor\\Datadog\\Monolog\\": "vendor/ddtraceweb/monolog-parser/src/"
}
}
Run composer dump-autoload.
First Use Case
Parse a Monolog log file (e.g., storage/logs/laravel.log) to extract structured data:
use Datadog\Monolog\Parser;
$parser = new Parser();
$logs = $parser->parse(file_get_contents('storage/logs/laravel.log'));
Where to Look First
src/Parser.php: Core parsing logic.tests/: Example use cases and edge cases.README.md or inline PHPDoc comments (limited but functional).Log File Processing Parse logs in bulk (e.g., for analytics or debugging):
$filePath = 'storage/logs/laravel-*.log';
$logFiles = glob($filePath);
foreach ($logFiles as $file) {
$logs = $parser->parse(file_get_contents($file));
// Process $logs (array of structured log entries)
}
Integration with Laravel Logging Use alongside Monolog’s built-in handlers to enrich logs:
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$log = new Logger('app');
$log->pushHandler(new StreamHandler(storage_path('logs/laravel.log')));
$log->info('Test log', ['context' => 'key' => 'value']);
// Later, parse the log file:
$parsed = $parser->parse(file_get_contents(storage_path('logs/laravel.log')));
Filtering Logs Extract specific log levels or contexts:
$errorLogs = array_filter($logs, fn($log) => $log['level'] === 'ERROR');
Laravel Service Provider: Bind the parser to the container for dependency injection:
$this->app->singleton(Parser::class, function ($app) {
return new Parser();
});
Then inject Parser into controllers/services.
Artisan Command: Create a custom command to parse logs on demand:
use Illuminate\Console\Command;
class ParseLogsCommand extends Command
{
protected $signature = 'logs:parse {file?}';
protected $description = 'Parse Monolog log files';
public function handle(Parser $parser)
{
$file = $this->argument('file') ?? storage_path('logs/laravel.log');
$logs = $parser->parse(file_get_contents($file));
$this->line(print_r($logs, true));
}
}
Queue Log Processing: Offload parsing to a queue job for large log files:
ParseLogsJob::dispatch($filePath)->onQueue('logs');
File Encoding:
Log files may contain UTF-8 or non-UTF-8 characters. Use file_get_contents() with FILE_USE_INCLUDE_PATH or mb_convert_encoding() if parsing fails:
$content = mb_convert_encoding(file_get_contents($file), 'UTF-8', 'auto');
Log Format Variations:
The parser assumes standard Monolog JSON formatting. Custom handlers (e.g., LineHandler with non-JSON formatting) may break parsing. Validate output with:
$log = $parser->parse($content);
if (empty($log)) {
throw new \RuntimeException('Failed to parse log file. Check format.');
}
Memory Limits: Large log files may exceed PHP’s memory limit. Process line-by-line:
$handle = fopen($file, 'r');
while (($line = fgets($handle)) !== false) {
$parser->parseLine($line); // Hypothetical method; may need custom logic.
}
fclose($handle);
Missing Context Data:
Logs without context arrays (e.g., logger->info('Message')) will return null for the context key. Handle gracefully:
$context = $log['context'] ?? [];
Validate Input:
Ensure log files are not corrupted or truncated. Use tail -n 100 storage/logs/laravel.log to inspect manually.
Parser Output: The parsed logs are arrays with keys:
[
'message' => string,
'level' => string (e.g., 'INFO', 'ERROR'),
'level_name' => int (Monolog level constant),
'channel' => string,
'datetime' => \DateTimeInterface,
'context' => array|null,
'extra' => array|null,
]
Log the output to debug:
$this->info('Parsed logs:', ['logs' => $logs]);
Custom Parsing Logic:
Extend the Parser class to handle non-standard formats:
class CustomParser extends Parser
{
protected function parseLine($line)
{
// Override or extend parsing logic.
}
}
Post-Processing:
Use Laravel’s collect() to chain operations:
collect($logs)
->filter(fn($log) => $log['level'] === 'ERROR')
->each(fn($log) => $this->handleError($log));
Integration with Datadog: If using Datadog APM, forward parsed logs to their API:
foreach ($logs as $log) {
Datadog::log([
'message' => $log['message'],
'level' => $log['level'],
'service' => 'laravel-app',
'context' => $log['context'],
]);
}
config/ddtrace-monolog-parser.php exists. All logic is in Parser.php.datetime objects use the system timezone. Normalize if needed:
$log['datetime']->setTimezone(new \DateTimeZone('UTC'));
How can I help you explore Laravel packages today?