Skip to content

Logging

EasyWAF writes three separate things, and keeps them separate on purpose.

Where Switch
Flow logs — one line per proxied request syslog, to a collector Off by default
Audit log — every change made through the interface /var/log/easywaf/audit.log Always on
Operational logging — what the service itself is doing stdout, so the journal journalctl -u easywaf

Flow logs

One line per proxied request, sent to a syslog collector over UDP: the site, the client, the method and full path, the verdict, and the score and rules behind it.

Turn it on under Settings → Logging with the collector's address and port. The change applies to the running proxy — there is no restart and no file to edit.

ts=2026-09-09T12:50:42Z site=cloud host=localhost client=127.0.0.1 method=GET
path="/etc/passwd?evil=%27+OR+1%3D1" status=403 ms=0 verdict=blocked score=10
rules=930001 reason="WAF score 10 ≥ block threshold 10"

The format is logfmt: key=value, quoted only when a value could otherwise split the line. Values that arrive from the wire — the path, the host — are escaped, so nothing a client sends can forge a field.

Nothing is written locally. Every request is already in Traffic Monitor with the same detail, so a file beside the database would be the same data in a worse format. Syslog exists to get the stream off the box — into a collector that can hold more history than an appliance should, and chart several appliances together.

Lines are sent without waiting. If the collector is slow or gone, lines are dropped and the count is reported in the journal, rather than the proxy slowing down behind them. A WAF that stalls because a log collector is unreachable has turned its logging into an outage.

EasyLog

EasyLog parses this format natively, with dashboards for blocked and challenged traffic over time, top rules, top countries and top clients.

Audit log

Every state-changing action through the management interface, written to /var/log/easywaf/audit.log, one file per day.

ts=2026-09-09T13:09:16Z user=admin role=admin client=10.0.0.5 method=POST
path=/settings/update status=303 result=ok ms=12

It records the account, the client address, what was done and how it came out — including sign-ins, failed sign-ins and sign-outs, and including refusals: a viewer's attempt at an administrator's page is a line saying result=refused, which is the line somebody reviewing the trail is looking for. A change the appliance rejected carries the reason it showed on screen.

It records what was done, never what it was done with: method, path, account, address and outcome — no bodies and no query strings, so a password or a private key cannot reach the file by being an argument to something.

There is no switch for it. An appliance that cannot say who changed it is not one to run, and it is a local file whose only cost is disk. It stays on the machine rather than going to a collector: evidence about an appliance is least useful when it can only be read from the machine it accuses.

Recording is done by a layer on the router rather than by each page, so a page added later cannot forget to be in the trail.

Rotation

A file per day, named audit-YYYY-MM-DD.log, with audit.log as a symlink to today's — so tail -F audit.log keeps working across a rotation.

Files older than keep_days (14 by default) are deleted. There is nothing to set up in logrotate.

What is configured where

The collector is a setting in the interface, because it is a thing operators change — a collector moves, a port changes — and that should not mean editing a file on the appliance and restarting the proxy.

The directory and the retention are in config.toml, because the directory has to be known before the database is open, and because a container needs to set it from outside the image:

[logging]
dir       = "/var/log/easywaf"   # audit.log, one file per day
keep_days = 14                   # then deleted; 0 keeps everything

The systemd unit declares LogsDirectory=easywaf, so the directory is created and owned before the service starts.