← Back to Blog
Code Snippets August 1, 2026

Named-Capture Regex for Parsing Log Formats

🔧 Code Snippet — August 1, 2026

Named-Capture Regex for Parsing Log Formats

Stop using numbered backreferences that break when formats change. Named captures (?P...) give you match.group("ip") instead of counting groups.

import re

# Apache Combined Log
COMBINED = re.compile(
    r'(?P<ip>\S+)\s+(?P<ident>\S+)\s+(?P<user>\S+)\s+'
    r'\[(?P<time>[^\]]+)\]\s+'
    r'"(?P<method>\S+)\s+(?P<path>\S+)\s+(?P<proto>\S+)"\s+'
    r'(?P<status>\d{3})\s+(?P<size>\S+)'
    r'(?:\s+"(?P<referer>[^"]*)"\s+"(?P<ua>[^"]*)")?'
)

# Syslog RFC 3164
SYSLOG = re.compile(
    r'(?P<month>\w{3})\s+(?P<day>\d{1,2})\s+(?P<time>\d{2}:\d{2}:\d{2})\s+'
    r'(?P<host>\S+)\s+(?P<program>\S+?)(?:\[(?P<pid>\d+)\])?:\s+(?P<message>.*)'
)

# Kubernetes logs
K8S = re.compile(
    r'(?P<ts>\d{4}-\d{2}-\d{2}T\S+Z)\s+(?P<stream>stdout|stderr)\s+(?P<flag>F|P)\s+(?P<message>.*)'
)

# Parse helper
def parse_line(pat, line):
    m = pat.match(line)
    return m.groupdict() if m else None

# Usage
r = parse_line(COMBINED, '10.0.0.1 - - [01/Aug/2026:12:00:00 +0000] "GET /api HTTP/1.1" 200 1234')
print(r["ip"], r["status"], r["path"])
# → 10.0.0.1 200 /api


Also in full snippet: Apache Common Log, Syslog RFC 5424 (with priority), Nginx upstream (with response time & host), JSON lines via json.loads.

Pro tips:
  • Compile once with re.compile(), reuse across millions of lines

  • Use match.groupdict() for a clean dict output

  • Pipe to csv.DictWriter for instant analysis

  • Use re.finditer() for multiple matches

  • Use re.VERBOSE for multi-line, commented patterns


💡 Bookmark this — when awk and cut can't handle the log format, named-capture regex gives you self-documenting patterns. These 7 patterns cover the most common log formats in production.