🔧
Code Snippet of the Day — July 25, 2026Parse Unstructured Log Lines into Structured Data with Named-Capture Regex (Python)Stop using a dozen fragile
split()/
strip()/
index() calls. Use named capture groups for clean, extensible log parsing:
import re
from datetime import datetime
NGINX = re.compile(
r'^(?P<ip>\d+\.\d+\.\d+\.\d+)\s+'
r'\[(?P<timestamp>[^\]]+)\]\s+'
r'"(?P<method>\w+)\s+(?P<path>\S+)\s+[^"]+"\s+'
r'(?P<status>\d{3})\s+(?P<bytes>\d+|-)'
)
def parse_line(line):
m = NGINX.match(line.strip())
if not m:
return None
r = m.groupdict()
r["status"] = int(r["status"])
r["bytes"] = 0 if r["bytes"] == "-" else int(r["bytes"])
try:
r["timestamp"] = datetime.strptime(
r["timestamp"], "%d/%b/%Y:%H:%M:%S %z"
)
except ValueError:
pass
return r
Multi-format registry pattern — try nginx → Apache → app log, first match wins:
LOG_PARSERS = [
(NGINX_REGEX, nginx_coerce),
(APACHE_REGEX, apache_coerce),
(APP_LOG_REGEX, app_coerce),
]
def parse_line(line):
for pattern, coerce in LOG_PARSERS:
m = pattern.match(line.strip())
if m:
return coerce(m.groupdict())
return None
Streaming file parser — 10GB logs, ~1MB RAM:
def parse_log_file(path):
with open(path) as f:
for line in f:
r = parse_line(line)
if r:
yield r
💡
Adding a new log format is one line: LOG_PARSERS.append((regex, coerce_fn)). Pipe to csv.DictWriter for analysis, or stream into SQLite for querying.