Custom log parsers
The node ships parsers for php_error_log, monolog and json. To support another format, drop
a parser class into src/Watchers/LogParsers/. It is discovered automatically; no registration
needed.
Requirements
- File name ends in
Parser.php; class in namespaceMata\Watchers\LogParsers. - Extends
AbstractLogParser, which supplies the streaminganalyzeLog()used for/appscounts and the fatal cursor. - Constructible with no arguments (discovery instantiates each class once to read its name).
getName()returns a unique name. That name is thetypevalue inapps.json.
Example
A format like 2026-10-09T12:00:00Z ERROR Payment failed:
<?php
declare(strict_types=1);
namespace Mata\Watchers\LogParsers;
use Mata\Services\Command\ShellCommands;
class PlainLevelParser extends AbstractLogParser
{
private const LINE = '/^(\S+Z) (ERROR|WARN|INFO|DEBUG) (.*)$/';
public function getName(): string
{
return 'plain_level';
}
// Auto-detection: claim the file if most of its first lines match.
public function detect(string $logFile): bool
{
$head = $this->shell(ShellCommands::head($logFile, 10));
if ($head === null || trim($head) === '') {
return false;
}
$lines = explode("\n", trim($head));
$matches = count(preg_grep(self::LINE, $lines));
return $matches / count($lines) >= 0.5;
}
// Used by /{app}/logs on the tail of the file.
public function parseLines(array $lines): array
{
$entries = [];
foreach ($lines as $line) {
if (!preg_match(self::LINE, $line, $m)) {
continue;
}
$entries[] = [
'timestamp' => $this->normalizeTimestamp($m[1]),
'type' => match ($m[2]) {
'ERROR' => 'Error',
'WARN' => 'Warning',
default => 'Other',
},
'message' => $m[3],
];
}
return $entries;
}
protected function fatalLinePattern(): string
{
return '/^\S+Z ERROR /';
}
protected function warningLinePattern(): string
{
return '/^\S+Z WARN /';
}
protected function severityPatternsMatchLineStartOnly(): bool
{
return true; // both patterns are anchored with ^
}
}
Use it in apps.json:
"logs": [{ "type": "plain_level", "paths": ["shop/logs/app.log"] }]
Contract
| Method | Used by | Must |
|---|---|---|
parseLines() |
/{app}/logs |
Return entries with timestamp (Y-m-d H:i:s or ""), type (Error, Warning or Other; the errors/warnings filters match on it) and message. Extra keys are passed through. |
fatalLinePattern(), warningLinePattern() |
/apps status counts, fatal cursor |
Match one physical line. |
entryStartPattern() |
fatal cursor | Optional. Match the first line of a multiline entry so stack traces belong to it. Default: every line is an entry. |
canonicalizeEntryLine() |
fatal cursor | Optional. Normalize single-line entries whose serialization varies (see JsonParser). |
detect() |
auto-detection | Return true only for files in this format. |
Parsers are tried in file name order during auto-detection and the first match wins; files no
parser claims fall back to php_error_log. Give a strict detect() so it cannot claim other
formats, or set type explicitly in apps.json.
Add a unit test next to the existing ones in src/Tests/Unit/ (see MonologParserTest).