java 46 lines · 7 steps

Parsing Apache access logs in Java

A named-group regex turns each Common Log Format line into a typed, immutable record.

Explained by highlit
1public final class AccessLogParser {
2 
3 private static final Pattern LINE = Pattern.compile(
4 "^(?<ip>\\S+) \\S+ \\S+ \\[(?<time>[^\\]]+)\\] "
5 + "\"(?<method>[A-Z]+) (?<path>\\S+) (?<protocol>[^\"]+)\" "
6 + "(?<status>\\d{3}) (?<size>\\d+|-) "
7 + "\"(?<referer>[^\"]*)\" \"(?<agent>[^\"]*)\"$");
8 
9 private static final DateTimeFormatter CLF_TIME =
10 DateTimeFormatter.ofPattern("dd/MMM/yyyy:HH:mm:ss Z", Locale.ENGLISH);
11 
12 public Optional<AccessLogEntry> parse(String line) {
13 Matcher m = LINE.matcher(line);
14 if (!m.matches()) {
15 return Optional.empty();
16 }
17 
18 String rawSize = m.group("size");
19 long size = "-".equals(rawSize) ? 0L : Long.parseLong(rawSize);
20 
21 AccessLogEntry entry = new AccessLogEntry(
22 m.group("ip"),
23 OffsetDateTime.parse(m.group("time"), CLF_TIME),
24 m.group("method"),
25 m.group("path"),
26 m.group("protocol"),
27 Integer.parseInt(m.group("status")),
28 size,
29 m.group("referer"),
30 m.group("agent"));
31 
32 return Optional.of(entry);
33 }
34 
35 public record AccessLogEntry(
36 String ip,
37 OffsetDateTime timestamp,
38 String method,
39 String path,
40 String protocol,
41 int status,
42 long responseBytes,
43 String referer,
44 String userAgent) {
45 }
46}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Named capture groups make a complex regex readable and self-documenting when you extract fields.
  2. 2Returning Optional signals parse failure without exceptions, forcing callers to handle malformed lines.
  3. 3Compiling patterns and formatters once as static finals avoids rebuilding them on every call.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Parsing Apache access logs in Java — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code