SPEC: the one place for behaviour and reasons; docs/ as lookup lists only
check / check (push) Waiting to run
check / check (push) Waiting to run
docs/core-rule-set.md, docs/lookup.md, docs/reputation.md and docs/bans.md restated the spec and are folded back into it and removed. The remaining docs files hold only lists: settings with their defaults and value forms, the request log's fields, the metrics, the endpoints, the state files and their contents, the rule file format and default file, the alert events and messages, the image's contents and pins, and the gitea notes. Every behaviour each of them held moves to the spec, said once. The settings groups have the same names in both files. Stale comments in internal/state and script/example-app point at the right places. Model: fable-5-1
This commit is contained in:
+15
-35
@@ -1,8 +1,8 @@
|
||||
# The request log's fields
|
||||
|
||||
`smallwebwaf` writes one JSON object per line on stdout for every request. The
|
||||
requirements are under "Request log" in [`SPEC.md`](../SPEC.md); this file lists
|
||||
the fields. A field that does not apply to a request is left out.
|
||||
The fields of the JSON object `smallwebwaf` writes for each request. "Request
|
||||
log" in [`SPEC.md`](../SPEC.md) says when a line is written and how the log is
|
||||
sent to a syslog server. A field that does not apply to a request is left out.
|
||||
|
||||
## Example
|
||||
|
||||
@@ -10,6 +10,12 @@ the fields. A field that does not apply to a request is left out.
|
||||
{"type":"request","time":"2026-10-03T12:00:00.123Z","instance":"fsn1app1/gitea","client_ip":"203.0.113.9","method":"GET","scheme":"https","host":"app.example","path":"/","query":"","protocol":"HTTP/1.1","status":200,"request_bytes":0,"response_bytes":5120,"referer":"","user_agent":"curl/8.9.1","request_id":"7Q2NHZ4KJ3VXW5YB6R3MEFTD2A","peer_ip":"172.18.0.2","forwarded_for":"203.0.113.9","client_group":"203.0.113.9/32","asn":"AS64496","as_name":"Example Net","country":"DE","request_headers":{"accept":"*/*"},"response_content_type":"text/html; charset=utf-8","upstream_status":200,"action":"forward","counts":{"minute":1,"hour":12,"day":40,"minute_bytes":5120,"hour_bytes":61440,"day_bytes":204800},"duration_total":3.217,"duration_checks":0.041,"duration_upstream_connect":0.052,"duration_upstream_first_byte":2.874,"duration_upstream_total":3.104}
|
||||
```
|
||||
|
||||
## Every line
|
||||
|
||||
Every line has `type` (`request`; the process's own messages have `process`),
|
||||
the standard web log fields, `request_id`, `peer_ip`, `client_group`, `asn`,
|
||||
`as_name`, `country`, `action` and `duration_total`.
|
||||
|
||||
## Standard web log fields
|
||||
|
||||
- `time`: when the request arrived, RFC 3339 with milliseconds, in UTC;
|
||||
@@ -66,11 +72,12 @@ the fields. A field that does not apply to a request is left out.
|
||||
- `limit_percent` and `limit_percent_setting`: the client's percentage of the
|
||||
rate limits and the setting that lowered it, when one did. `bytes_percent` and
|
||||
`bytes_percent_setting` do the same for the byte limits.
|
||||
- `counts`: the client's requests in the minute, hour and day as rate limiting
|
||||
counts them, this request included, as `minute`, `hour` and `day`; its bytes
|
||||
as `minute_bytes`, `hour_bytes` and `day_bytes`. A count can have a fraction,
|
||||
since each window is counted in two buckets with the earlier one weighted.
|
||||
`counts` is left out when rate limiting did not count the request.
|
||||
- `counts`: the client's totals as rate limiting counts them, this request
|
||||
included. It is left out when rate limiting did not count the request. A count
|
||||
can have a fraction, since each window is counted in two buckets with the
|
||||
earlier one weighted:
|
||||
- `minute`, `hour` and `day`: requests;
|
||||
- `minute_bytes`, `hour_bytes` and `day_bytes`: bytes.
|
||||
- `limit_hit`: the limit broken: `minute`, `hour` or `day` for a rate limit;
|
||||
`minute_bytes`, `hour_bytes` or `day_bytes` for a byte limit; or
|
||||
`error_burst`. A request whose bytes broke a limit was passed on whole, so its
|
||||
@@ -99,30 +106,3 @@ All in milliseconds:
|
||||
connection;
|
||||
- `duration_upstream_first_byte`: until the first byte of the answer;
|
||||
- `duration_upstream_total`: until the end of the answer.
|
||||
|
||||
## Every line
|
||||
|
||||
Every line has `type`, the standard web log fields, `request_id`, `peer_ip`,
|
||||
`client_group`, `asn`, `as_name`, `country`, `action` and `duration_total`. The
|
||||
process's own messages share the stream with `"type":"process"`; request lines
|
||||
carry `"type":"request"`.
|
||||
|
||||
## Sending the log to a syslog server
|
||||
|
||||
While `SWWAF_LOG_REMOTE_URL` is set, every line on stdout is also sent to that
|
||||
server, as the message of an RFC 5424 syslog record. Over UDP each record is one
|
||||
datagram. Over TCP and TLS each record follows its length in bytes and a space.
|
||||
stdout is unchanged. The record carries:
|
||||
|
||||
- the facility from `SWWAF_LOG_REMOTE_FACILITY`;
|
||||
- the severity informational;
|
||||
- the time;
|
||||
- the host's name;
|
||||
- the app name from `SWWAF_LOG_REMOTE_APP_NAME`.
|
||||
|
||||
Lines wait in a buffer of `SWWAF_LOG_REMOTE_BUFFER` lines, so a server that is
|
||||
slow or down never holds up a request. When the buffer is full, the oldest line
|
||||
is dropped. A failed connection is tried again a second later, and twice as long
|
||||
after each further failure, up to a minute. A line too long for one UDP datagram
|
||||
is dropped alone. As `smallwebwaf` stops, it spends at most two seconds sending
|
||||
the lines still waiting, and drops the rest. stdout has carried them.
|
||||
|
||||
Reference in New Issue
Block a user