Bootcamp
A 60-minute regex bootcamp: build a small log analyser that parses production-style logs and answers "how many 5xx in the last hour?" — applying the patterns you actually need.
A 60-minute regex bootcamp
EXAMPLE
# ===== Objectives =====
# 1. Compile patterns at module scope
# 2. Use named groups for readability
# 3. Use verbose mode for long patterns
# 4. Handle multi-line records
# 5. Detect + avoid ReDoS
# 6. Build a tiny CLI that summarises errors
# ===== 0-5 min: pick a log sample =====
# Apache combined or nginx access log; or a JSON-lines app log.
# Sample line:
# 203.0.113.5 - - [11/Jun/2026:10:32:14 +1000] "GET /api/users HTTP/1.1" 200 1532 "-" "curl/8.6"
# ===== 5-15 min: build the parser =====
import re
LOG = re.compile(r'''
^
(?P<ip>\d{1,3}(?:\.\d{1,3}){3})\s+
\S+\s+\S+\s+
\[(?P<ts>[^\]]+)\]\s+
\"(?P<method>[A-Z]+)\s+(?P<path>[^ ]+)\s+HTTP/[\d.]+\"\s+
(?P<status>\d{3})\s+
(?P<bytes>\d+|-)\s+
\"(?P<referer>[^\"]*)\"\s+
\"(?P<ua>[^\"]*)\"
$
''', re.VERBOSE)
def parse(line: str):
m = LOG.match(line)
if not m: return None
d = m.groupdict()
d['status'] = int(d['status'])
d['bytes'] = 0 if d['bytes'] == '-' else int(d['bytes'])
return d
# ===== 15-25 min: count 5xx by path =====
from collections import Counter
c = Counter()
with open('access.log') as f:
for line in f:
rec = parse(line)
if rec and rec['status'] >= 500:
c[rec['path']] += 1
for path, n in c.most_common(10):
print(f'{n:>6} {path}')
# ===== 25-35 min: filter by time window =====
from datetime import datetime, timedelta, timezone
def parse_ts(ts: str) -> datetime:
# 11/Jun/2026:10:32:14 +1000
return datetime.strptime(ts, '%d/%b/%Y:%H:%M:%S %z')
cutoff = datetime.now(tz=timezone.utc) - timedelta(hours=1)
recent_5xx = 0
with open('access.log') as f:
for line in f:
rec = parse(line)
if rec and rec['status'] >= 500 and parse_ts(rec['ts']) >= cutoff:
recent_5xx += 1
print(f'5xx in last hour: {recent_5xx}')
# ===== 35-45 min: multi-line stack traces =====
# App logs often span multiple lines. Combine until the next 'leading line'
# pattern, then parse the leader.
LEADER = re.compile(r'^\[(?P<ts>\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2})\]\s+(?P<level>INFO|WARN|ERROR|FATAL)\s+')
def iter_events(lines):
buf = []
for line in lines:
if LEADER.match(line) and buf:
yield ''.join(buf); buf = []
buf.append(line)
if buf: yield ''.join(buf)
for event in iter_events(open('app.log')):
head = LEADER.match(event)
if head and head.group('level') in ('ERROR', 'FATAL'):
print(event[:200])
# ===== 45-55 min: avoid ReDoS =====
# Pattern shapes to refuse on untrusted input:
# - (a+)+$
# - (\w+\s+)+$
# - (.*a){10}
#
# If you must run untrusted regex, use a linear-time engine:
# - Go's regexp (RE2)
# - Python re2 (google/re2) package
# - .NET 7+ has NonBacktracking mode
# Safe-regex check
# pip install safe-regex
# import safe_regex
# safe_regex.is_safe(r'^(a+)+$') -> False
# ===== 55-60 min: package =====
# Wrap the parser into a CLI tool:
# python summarise.py access.log --since 1h --top 5
# Use argparse / click; tests with the sample log lines above.
# ===== Bonus =====
# - Generate a Prometheus exporter from the same parser
# - Write a small Grafana dashboard for 5xx by path
# - Tail -f mode: continuously parse new lines
# ===== Decision rules =====
# Use regex for: line-oriented text, log parsing, extraction, validation
# Avoid regex for: HTML, XML, JSON, YAML, CSV with quoting, code parsing
# Compile ONCE; use everywhere
# Verbose mode for any pattern > ~40 chars
# Named groups for any output you display
# ===== Pitfalls =====
# - Recompiling inside a hot loop
# - re.match vs re.search (match is anchored at start)
# - greedy quantifiers eating across newlines (use re.DOTALL deliberately)
# - leading anchors omitted -> partial matches accepted
# - timezones in timestamps ignored
Why it matters
A small log analyser is the project that exercises every important regex pattern — compile-once, named groups, verbose mode, multi-line records, time-window filtering — at a scale that fits a Saturday. Build it, host the dashboard, and you have proof you can use regex on real production data.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Discussion
Loading…