A plugin that exports DNS query logs from Technitium DNS Server to external sinks such as standard output, files, HTTP endpoints, and Syslog servers.
It uses a bounded asynchronous pipeline: query logs are captured, optionally processed through enrichment stages, and then exported in batches to the configured sinks.
NOTE: This app is not included in the main Technitium DNS Server repository as of v15.
- Captures DNS queries and responses through the Technitium DNS Server
IDnsQueryLoggerinterface. - Uses bounded processing stages and an independent bounded ingress queue for each sink, preventing one slow sink from blocking the others or causing unbounded memory growth.
- Exports logs through pluggable sinks: console, file, HTTP POST, and Syslog.
- Supports optional pipeline processors before export:
- domain normalization with Public Suffix List based metadata;
- static tag injection for downstream routing or tenant identification.
- Includes client address, nameserver, query, answers, response metadata, RTT, and optional EDNS Extended DNS Error data.
- Supports newline-delimited JSON for HTTP export.
- Drops new entries when the pipeline is full and periodically logs the number of dropped records.
- Drains pending logs during shutdown.
Note that the configuration differs from the
LogExporterAppin the Technitium DNS Server App Store. Therefore, you cannot use the same configuration.The top-level
sinksandpipelineobjects are required. Individual sink and pipeline sections are optional: if a section is omitted, that feature is treated as disabled. If a section is present,enableddefaults totrueunless explicitly set tofalse.
Provide JSON configuration similar to the following:
{
"sinks": {
"maxQueueSize": 50000,
"enableEdnsLogging": true,
"console": {
"enabled": true
},
"file": {
"enabled": false,
"path": "/var/log/technitium/dns.log"
},
"http": {
"enabled": true,
"endpoint": "https://collector.example.com/dns",
"headers": {
"Authorization": "Bearer token"
}
},
"syslog": {
"enabled": false,
"address": "syslog.example.com",
"port": 6514,
"protocol": "TLS"
}
},
"pipeline": {
"normalize": {
"enabled": true
},
"tagging": {
"enabled": false,
"tags": [
"tenant:alpha"
]
}
}
}maxQueueSizesets the capacity, in log entries, of the bounded processing stages and of each sink's independent ingress queue. A sink worker may additionally hold one in-flight batch while exporting or retrying. When a queue is full, new entries for that queue are dropped instead of allowing memory usage to grow without limit.enableEdnsLoggingcontrols whether EDNS Extended DNS Error data is included in exported logs.consolewrites logs to standard output, which is useful for containerized deployments and debugging.filewrites logs to the configured local file path.httpsends batches to the configured endpoint using HTTP POST as newline-delimited JSON (NDJSON). HTTP records include the responding server's hostname.syslogexports logs through Syslog. Supported protocols areUDP,TCP,TLS, andLOCAL.- For normal host installations, prefer
LOCALand let the operating system's Syslog daemon, such as rsyslog or an equivalent implementation, handle remote forwarding, buffering, retry, and transport policy. The app intentionally does not try to duplicate those responsibilities.LOCALis available on supported Unix-like hosts and ignoresaddress. - In containers, prefer
consolewhen your platform already collects standard output. A sidecar or the platform's logging infrastructure can then handle forwarding and delivery policy. If that is not available or not desired, configureUDP,TCP, orTLSto send directly to a remote Syslog target. - For remote Syslog,
addressaccepts an IP address or an FQDN. Use an FQDN forTLS, because the server certificate is validated against it. - An FQDN is resolved once the DNS server starts answering queries, not while the app loads. If resolution is temporarily unavailable, the Syslog sink retries in the background with capped exponential backoff and jitter until resolution succeeds or the sink is disposed. With
UDP, the successfully resolved address is kept for the lifetime of the sink. If the Syslog server's IP address changes, restart Technitium DNS Server or reload the Log Exporter configuration so the target is resolved again.TCPandTLSresolve the name again whenever they reconnect. - For
TCPandTLS, an ambiguous write failure may cause the current record to be retried even if the remote peer already received it. Direct remote Syslog therefore favors avoiding loss over avoiding duplicates.
- For normal host installations, prefer
Individual sink sections may be omitted entirely. An omitted sink is disabled. When a sink section is present, its sink-specific required values are validated only if that sink is enabled. For example, a disabled or omitted HTTP sink does not require an endpoint.
At least one sink must be enabled for logs to be exported. If no sink is enabled, logging remains disabled.
A minimal console-only configuration is therefore valid:
{
"sinks": {
"console": {
"enabled": true
}
},
"pipeline": {}
}normalizeadds parsed domain metadata undermeta.domainInfo.taggingadds the configured static tags undermeta.tags.
Pipeline sections may also be omitted. An omitted processor is disabled.
The normalization stage uses the Public Suffix List to derive domain structure. PSL loading is best-effort: if the list cannot be obtained, logging continues and the normalization output is simply unavailable for affected entries.
dig '@127.0.0.1' technitium.com
; <<>> DiG 9.16.25 <<>> @127.0.0.1 technitium.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62685
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;technitium.com. IN A
;; ANSWER SECTION:
technitium.com. 8218 IN A 206.189.140.177
;; Query time: 24 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Dec 08 22:50:37 FLE Standard Time 2025
;; MSG SIZE rcvd: 59Raw log:
{"answers":[{"dnssecStatus":"Disabled","name":"technitium.com","recordClass":"IN","recordData":"206.189.140.177","recordTtl":8218,"recordType":"A"}],"clientIp":"127.0.0.1","edns":[],"nameServer":"127.0.0.1","protocol":"Udp","question":{"questionClass":"IN","questionName":"technitium.com","questionType":"A"},"responseCode":"NoError","responseType":"Cached","timestamp":"2025-12-08T20:50:37.321Z","meta":{"domainInfo":{"domain":"technitium","topLevelDomain":"com","registrableDomain":"technitium.com","fullyQualifiedDomainName":"technitium.com","topLevelDomainRule":{"name":"com","type":"Normal","labelCount":1,"division":"ICANN"}}}}Formatted for easier review:
{
"answers": [
{
"dnssecStatus": "Disabled",
"name": "technitium.com",
"recordClass": "IN",
"recordData": "206.189.140.177",
"recordTtl": 8218,
"recordType": "A"
}
],
"clientIp": "127.0.0.1",
"edns": [],
"nameServer": "127.0.0.1",
"protocol": "Udp",
"question": {
"questionClass": "IN",
"questionName": "technitium.com",
"questionType": "A"
},
"responseCode": "NoError",
"responseType": "Cached",
"timestamp": "2025-12-08T20:50:37.321Z",
"meta": {
"domainInfo": {
"domain": "technitium",
"topLevelDomain": "com",
"registrableDomain": "technitium.com",
"fullyQualifiedDomainName": "technitium.com",
"topLevelDomainRule": {
"name": "com",
"type": "Normal",
"labelCount": 1,
"division": "ICANN"
}
}
}
}- The app uses bounded channels rather than unbounded queues. Each sink has its own ingress queue and worker, so a slow or unavailable sink does not block the other sinks. Under sustained overload, only the full queue drops new entries and reports the drop count periodically.
- The normalization cache uses Public Suffix List based parsing and is optimized for DNS-style query patterns where many domains may be seen only once.
- EDNS logging records Extended DNS Error data when enabled and parses malformed EDE payloads defensively so they do not break the logging pipeline.
- Static tags are intended for downstream processing, for example tenant labels, environment labels, or collector-side routing keys.
- On Linux host installations, prefer
LOCALSyslog and delegate remote forwarding and delivery policy to rsyslog or another local Syslog daemon. For containers, preferconsolewith the platform's logging stack or a sidecar when available; direct remote Syslog is an alternative when container logging infrastructure is not used. - For UDP Syslog targets configured by FQDN, the resolved IP address is intentionally not refreshed periodically after the sink has started. Restart Technitium DNS Server or reload the Log Exporter configuration after the Syslog server's IP address changes.
Configuration reload validates the replacement configuration before stopping the active pipeline, then drains the old generation before rebuilding sinks and processors. During the brief cutover interval, query logs that arrive while no ingestion generation is published are intentionally ignored. Those entries are not included in the queue-drop counters. This is an accepted lifecycle trade-off: adding a dedicated reload-state accounting path would increase control-plane complexity for a short, bounded transition.

