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
java
public class SlidingLogRateLimiter { private final int maxRequests; private final long windowMillis;
How a sliding-log rate limiter works
rate-limiting
concurrency
sliding-window
Advanced
8 steps
java
public final class Slugifier { private static final Pattern NON_LATIN = Pattern.compile("[^\\w-]"); private static final Pattern WHITESPACE = Pattern.compile("[\\s]+");
Building a URL slugifier in Java
regex
unicode-normalization
string-processing
Intermediate
8 steps
rust
use std::cmp::Ordering; use std::str::FromStr; #[derive(Debug, Clone, PartialEq, Eq)]
Parsing and ordering semantic versions in Rust
parsing
trait-implementation
ordering
Intermediate
8 steps
java
@Repository public interface OrderRepository extends JpaRepository<Order, Long> { @Query("""
Keyset pagination with Spring Data JPA
pagination
cursor
jpa
Advanced
8 steps
ruby
class ApacheLogParser LINE_PATTERN = /\A(?<ip>\S+)\s\S+\s\S+\s\[(?<time>[^\]]+)\]\s"(?<method>[A-Z]+)\s(?<path>\S+)\s(?<protocol>[^"]+)"\s(?<status>\d{3})\s(?<bytes>\d+|-)/ TIME_FORMAT = "%d/%b/%Y:%H:%M:%S %z"
Parsing Apache logs with named captures
regex
named-captures
parsing
Intermediate
6 steps
java
public record FixedWidthField(String name, int start, int end) { public String extract(String line) { int from = Math.min(start, line.length());
Parsing fixed-width text in Java
records
parsing
streams
Intermediate
7 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.