Specification
$flags: 'FLEXIBLE_WHITESPACE'
Date:
$fields:
year: { $patterns: '\d{4}' }
month: { $patterns: '\d{2}' }
day: { $patterns: '\d{2}' }
ISO: '#{year}-#{month}-#{day}'
Time:
$fields:
hour: { $patterns: '\d{2}' }
minute: { $patterns: '\d{2}' }
second: { $patterns: '\d{2}' }
HMS: '#{hour}:#{minute}:#{second}'
DateTime:
$fields: { date: Date, time: Time }
$patterns: '#{date} #{time}'
LogLevel: [ 'INFO', 'WARN', 'ERROR' ]
Thread:
$types: { ThreadName: '[A-Za-z0-9-]+' }
$fields: { name: ThreadName }
$patterns: 'thread=#{name}'
Error:
$types: { ErrorCode: '\d{3}' }
$fields: { code: ErrorCode }
$patterns: 'code=#{code}'
Message:
$types: { Text: '[^"]*' }
$fields: { text: Text }
$patterns: 'msg="#{text}"'
LogEntry:
$fields:
datetime: DateTime
level: LogLevel
thread: Thread
error: Error
message: Message
$patterns: '\[#{datetime}] \[#{level}]( #{thread})?( #{error})? #{message}'Sample input
[2026-05-11 21:04:33] [ERROR] thread=worker-7 code=500 msg="Disk full"
[2026-05-11 21:05:01] [INFO] msg="Retry scheduled"
Result
[
{
"type": "LogEntry",
"text": "[2026-05-11 21:04:33] [ERROR] thread=worker-7 code=500 msg=\"Disk full\"",
"start": 0,
"end": 70,
"fields": [
{
"type": "DateTime",
"field": "datetime",
"text": "2026-05-11 21:04:33",
"start": 1,
"end": 20,
"fields": [
{
"type": "Date::ISO",
"field": "date",
"text": "2026-05-11",
"start": 1,
"end": 11,
"fields": [
{
"field": "year",
"text": "2026",
"start": 1,
"end": 5
},
{
"field": "month",
"text": "05",
"start": 6,
"end": 8
},
{
"field": "day",
"text": "11",
"start": 9,
"end": 11
}
]
},
{
"type": "Time::HMS",
"field": "time",
"text": "21:04:33",
"start": 12,
"end": 20,
"fields": [
{
"field": "hour",
"text": "21",
"start": 12,
"end": 14
},
{
"field": "minute",
"text": "04",
"start": 15,
"end": 17
},
{
"field": "second",
"text": "33",
"start": 18,
"end": 20
}
]
}
]
},
{
"type": "LogLevel",
"field": "level",
"text": "ERROR",
"start": 23,
"end": 28
},
{
"type": "Thread",
"field": "thread",
"text": "thread=worker-7",
"start": 30,
"end": 45,
"fields": [
{
"type": "ThreadName",
"field": "name",
"text": "worker-7",
"start": 37,
"end": 45
}
]
},
{
"type": "Error",
"field": "error",
"text": "code=500",
"start": 46,
"end": 54,
"fields": [
{
"type": "ErrorCode",
"field": "code",
"text": "500",
"start": 51,
"end": 54
}
]
},
{
"type": "Message",
"field": "message",
"text": "msg=\"Disk full\"",
"start": 55,
"end": 70,
"fields": [
{
"type": "Text",
"field": "text",
"text": "Disk full",
"start": 60,
"end": 69
}
]
}
]
},
{
"type": "LogEntry",
"text": "[2026-05-11 21:05:01] [INFO] msg=\"Retry scheduled\"",
"start": 71,
"end": 121,
"fields": [
{
"type": "DateTime",
"field": "datetime",
"text": "2026-05-11 21:05:01",
"start": 72,
"end": 91,
"fields": [
{
"type": "Date::ISO",
"field": "date",
"text": "2026-05-11",
"start": 72,
"end": 82,
"fields": [
{
"field": "year",
"text": "2026",
"start": 72,
"end": 76
},
{
"field": "month",
"text": "05",
"start": 77,
"end": 79
},
{
"field": "day",
"text": "11",
"start": 80,
"end": 82
}
]
},
{
"type": "Time::HMS",
"field": "time",
"text": "21:05:01",
"start": 83,
"end": 91,
"fields": [
{
"field": "hour",
"text": "21",
"start": 83,
"end": 85
},
{
"field": "minute",
"text": "05",
"start": 86,
"end": 88
},
{
"field": "second",
"text": "01",
"start": 89,
"end": 91
}
]
}
]
},
{
"type": "LogLevel",
"field": "level",
"text": "INFO",
"start": 94,
"end": 98
},
{
"type": "Message",
"field": "message",
"text": "msg=\"Retry scheduled\"",
"start": 100,
"end": 121,
"fields": [
{
"type": "Text",
"field": "text",
"text": "Retry scheduled",
"start": 105,
"end": 120
}
]
}
]
}
]Notes
# Log Entry
Parse structured log lines into timestamp, level, thread, error, and message fields.
## Notice
- `Date::ISO` and `Time::HMS` compose `DateTime`.
- `LogLevel` is a compact list of alternatives.
- `Thread`, `Error`, and `Message` use scoped `$types` for their internal values.
- `thread` and `error` are optional fields in `LogEntry`.
- `LogEntry` gives each part a semantic role.
Try removing `thread=...` or adding another log level.