Functional Weave
Code in Python

monitor.parse-access-log@1.0.0

README.md

2,769 bytes · view raw

# monitor.parse-access-log

Parses one line of a web server access log in the NCSA Common Log Format or
the Combined Log Format, the defaults of Apache httpd and nginx (nginx's
`combined` is the same layout):

```
Common    %h %l %u %t "%r" %>s %b
Combined  %h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i"

127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 "http://www.example.com/start.html" "Mozilla/4.08 [en] (Win98; I ;Nav)"
```

Feed it `monitor.status-code-summary` for error rates, or `monitor.rollup`
for requests per minute.

## Decisions

- **Junk is null, not an error.** Real logs hold truncated lines, lines from
  another format and binary noise; a log reader should skip them and count
  them, not stop. So anything not in either format returns null: a bad month,
  30 February, a status outside 100 to 599, stray text after the byte count.
- **"-" is null** in every field that uses it (ident, user, bytes, referer,
  user agent). Apache's `%b` writes "-" rather than 0 when no body was sent
  (a 304, say): treat a null `bytes` as 0 if you are summing.
- **The request line.** `"-"` (the client sent nothing before timing out) and
  anything that is not `METHOD path HTTP/x` (a TLS handshake sent to a plain
  HTTP port logs as `"\x16\x03\x01..."`) keep the line, with method, path and
  protocol null: the status (typically 400 or 408) is still worth counting.
  `METHOD path` with no protocol is an HTTP/0.9 request: protocol null.
- **Escapes are kept as logged.** Apache writes `"` as `\"`, `\` as `\\` and
  non-printable bytes as `\xhh` inside quoted fields. The parser honours `\"`
  when finding the end of a field but returns the text as it appears in the
  log, so no information is lost and nothing is decoded twice.
- **Extra fields after the user agent are ignored**, since many sites append
  response time or a request id to Combined. After a Common line's byte count
  nothing may follow except the two quoted Combined fields.
- **Time** `[10/Oct/2000:13:55:36 -0700]` is checked field by field (English
  month abbreviations, real calendar dates, leap years, a numeric offset) and
  converted with `time.iso-to-unix`, so `at` is exact Unix seconds (UTC).
  A leap second (`:60`) is not a Unix time and makes the line null.
- A trailing `\n` or `\r\n` is stripped, so lines straight from a file work.

## Sources

- Apache HTTP Server 2.4, mod_log_config: "Common Log Format" and "Combined
  Log Format", the `%b` "-" for no bytes, the `%t` format and the escaping of
  `%r`, `%i` and `%u`, https://httpd.apache.org/docs/2.4/mod/mod_log_config.html
- Apache HTTP Server 2.4, Log Files (the example lines and field-by-field
  explanation), https://httpd.apache.org/docs/2.4/logs.html#accesslog