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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Named capture groups make a complex regex readable and self-documenting when you extract fields.
- 2Returning Optional signals parse failure without exceptions, forcing callers to handle malformed lines.
- 3Compiling patterns and formatters once as static finals avoids rebuilding them on every call.
Related explainers
ruby
class UserAgentParser BROWSERS = [ [/Edg\/([\d.]+)/, "Edge"], [/OPR\/([\d.]+)/, "Opera"],
Parsing user-agent strings in Ruby
regex
pattern-matching
lookup-tables
Intermediate
8 steps
java
@Component @Converter public class EncryptedStringConverter implements AttributeConverter<String, String> {
Transparent column encryption in Spring & JPA
encryption
aes-gcm
jpa-converter
Advanced
10 steps
javascript
function evaluate(expression) { const tokens = tokenize(expression); let pos = 0;
Building a recursive descent calculator
parsing
recursion
operator-precedence
Intermediate
8 steps
java
package com.acme.billing.config; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties;
Feature-flagged beans with Spring @ConditionalOnProperty
feature-flags
conditional-beans
strategy-pattern
Intermediate
5 steps
rust
use chrono::{Duration, NaiveDate}; #[derive(Debug)] pub struct DateRange {
Parsing and iterating date ranges in Rust
error-handling
iterators
parsing
Intermediate
7 steps
java
public static Map<String, String> parseCookieHeader(String header) { Map<String, String> cookies = new LinkedHashMap<>(); if (header == null || header.isBlank()) { return cookies;
Parsing an HTTP Cookie header in Java
string-parsing
http
url-decoding
Intermediate
6 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/parsing-apache-access-logs-in-java-explained-java-88c3/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.