EN
  • English
  • Deutsch
  • Polski
  • Italiano
  • Español

Query log

Work in progress — content may be incomplete.

Warden logs one line per DNS query it handles — including why it answered the way it did.

What each outcome means

The result field on every query-log line isn’t the DNS RCODE — it’s Warden’s own record of which code path answered. Several outcomes can share one RCODE, and one outcome (BLOCKED) can produce four different RCODEs depending on config.

OutcomeFires whenWire RCODEWhat to do
ALLOWEDlogged the instant the upstream fetch returns, before Warden inspects the answernot fixed — depends on what upstream returnedmeans “forwarding attempted”, not “clean” — see the note below
BLOCKEDprofile/admin-rule/blocklist match, CNAME-chain block, or a post-fetch IP-blocklist re-checkdepends on the profile’s block_response: zero→NoError+0.0.0.0, nxdomain→NXDOMAIN, refused→REFUSED, soa_nodata→NoError+SOAexpected if policy is working as intended; check the line’s rule field, and the profile’s list policy, if it shouldn’t have blocked
REFUSED[anti_bypass] pre-query refusal, no profile matched the client (L5 miss on the resolver ladder), or DNS-tunneling rate exceededalways REFUSEDunexpected for a known client → check its device/group/subnet mapping, or whether default_profile is set
RRL_DROPrate-limit hard dropnone at all — the packet is never sent, a true silent dropfrequent for a legit client → check [security] rate-limit thresholds
RRL_SLIPrate-limit anti-spoof slipNoError, TC=1 (forces the client to retry over TCP)normal RRL behaviour, not an error
LOCALa per-profile or global local_records hit, or NODATA synthesis for a locally-authoritative nameNoErrorexpected for local DNS config
CACHEDcache hitpassthrough of whatever RCODE the cached entry storednothing to do
HINFORFC 8482 — a QTYPE=ANY query was interceptedNoErrorexpected hardening, not an error
STALEthe upstream fetch failed and Warden served an expired cache entry insteadpassthrough of the stale entry’s RCODErecurring → check upstream health and [[forwarding]]

A single lookup can produce two log lines. ALLOWED is stamped the moment the upstream fetch returns — before Warden checks the answer for a CNAME-chain or IP-blocklist hit. If that check fires, an immediate BLOCKED line follows for the same query. ALLOWED means “forwarding was attempted,” not “traffic passed through clean.”

TUI colour coding. The query-log tab buckets these nine values into three severities for the RESULT cell’s colour: red for BLOCKED (and any CNAME-chain block, even on a line whose result string isn’t literally BLOCKED), amber for STALE and REFUSED, grey for everything else — including RRL_DROP and RRL_SLIP, which the colouring doesn’t distinguish from a clean serve even though RRL_DROP is a genuine silent drop.

Names in the file. Warden writes each name as ASCII. An internationalised name stays in its xn-- (punycode) form, so münchen.de is logged as xn--mnchen-3ya.de. A control character, a space or a non-ASCII byte in a name is escaped as a backslash and three octal digits (for example \033 for the escape character, which dig writes in decimal as \027); other punctuation gets a backslash in front. The query log tab in the dashboard also replaces any such character with � before drawing it.

This page is about the file. To read the same lines inside Warden — filtered by domain, client or time, with one key to hold an allow or block rule for the name you are looking at — see the query log tab in the dashboard.

How much the log keeps

The log starts a new file every day at UTC midnight and keeps the past week by default. A size cap protects the disk on a busy day, at the cost of that day’s oldest lines.

The log is query.log in /var/lib/purge-warden (see File paths). Three settings in [tracking] decide how much of it stays on disk:

SettingDefaultWhat it does
retention_days7How many past days to keep, from 1 to 365.
query_log_max_size_mb100The size in MiB at which Warden moves the day’s current file aside and starts a new one.
query_log_max_files7How many moved-aside files one day can have. 0 turns the size cap off.

Over one day, this is what happens:

  1. Warden appends each line to query.log.
  2. When query.log reaches query_log_max_size_mb, Warden renames it to query.log.YYYY-MM-DD.1 (then .2, .3 and so on) and starts a new query.log.
  3. When the day already has query_log_max_files of these files, Warden deletes the oldest one, .1, renumbers the others, and moves the current file to the last number. Nothing records this deletion: no log line, no counter. The qlog drops line of warden status counts queries and batches that Warden could not write, not files that the cap removed.
  4. At UTC midnight, query.log becomes query.log.YYYY-MM-DD, and Warden deletes the files whose date is more than retention_days days in the past.

So one day keeps at most query_log_max_files + 1 files of about query_log_max_size_mb each: 8 × 100 MiB = 800 MiB with the defaults. A busier day keeps only its last 800 MiB. On disk, the log holds the past retention_days days plus today, so with the defaults it uses at most (7 + 1) × 800 MiB = 6,400 MiB, about 6.25 GiB. Warden checks the size before it writes each batch of lines, so a file can end a little over the cap.

Work out what your network needs

How many queries fit in 800 MiB depends on your network: long names and CNAME chains make longer lines. Measure your own log after a normal day:

bash
D=$(date -u -d yesterday +%F)
sudo sh -c "ls -l /var/lib/purge-warden/query.log.$D*"
sudo sh -c "cat /var/lib/purge-warden/query.log.$D* | wc -lc"
sudo head -n 1 /var/lib/purge-warden/query.log.$D.1

The first command lists yesterday’s files. If there are files that end in .1, .2 and so on, the day reached the size cap. The second command prints yesterday’s number of lines (one per logged query) and their size in bytes. Divide the bytes by the lines to get your average line size, then divide 800 MiB (838,860,800 bytes) by it: that is the number of queries one day can keep with the defaults.

If the day has query_log_max_files files with a number at the end, it may have lost its oldest lines. The third command prints the first line of .1: if its timestamp (in UTC) is well after 00:00, the lines before it were deleted.

Keep more, or write less

  • To keep more of each day, raise query_log_max_size_mb or query_log_max_files. The disk use grows with them: plan for (retention_days + 1) × (query_log_max_files + 1) × query_log_max_size_mb MiB.
  • To write less, set log_mode = "blocked_only" to log only blocked queries, or log_mode = { sampled = { allowed_rate = 0.1 } } to log every blocked query and one in ten of the others.
  • To keep fewer days, lower retention_days. The older files go at the next UTC midnight.
  • query_log_max_files = 0 removes the cap within a day: nothing is deleted before midnight, and a very busy day can fill the disk.
bash
sudo warden config edit      # change the settings in [tracking]
sudo systemctl restart purge-warden

A change to these settings needs a restart. warden reload does not apply it: the journal shows reload requires daemon restart for runtime sections: tracking runtime, and the previous settings keep running. For the other limits of a busy network, see Host tuning.

Verify it in the code

Every claim on this page is one function away in the source. Where to look:

Where outcomes are recorded
src/dnshandler.rs → record_outcome()
src/trackingengine.rs → log_query_event()
CNAME-chain / IP-blocklist block dispatch
src/dnshandler.rs → dispatch_cname_block()
src/dnshandler.rs → dispatch_ip_block()
REFUSED dispatch
src/dnshandler.rs → send_refused()
RRL slip (TC=1)
src/dnshandler.rs → send_truncated()
RFC 8482 (HINFO)
src/dnshandler.rs → send_rfc8482()
TUI severity bucket
src/tui/tabsquery_log.rs → result_severity()
Size cap and daily rotation
src/trackingquery_log.rs → rotate_on_size_backstop()
src/trackingquery_log.rs → rotate_daily()
src/trackingquery_log.rs → prune_old_files()
↑↓ to navigate · Enter to open · Esc to close See all results