Add opt-in JSON logging with structured DNS query fields (#8553)

* plugin/pkg/log: add opt-in JSON logging backend

Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com>

* plugin/log: emit typed query records in JSON mode

Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com>

* coremain: expose process-wide JSON logging

Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com>

---------

Signed-off-by: houyuwushang <liuluoqianqiu@outlook.com>
This commit is contained in:
houyuwushang
2026-09-17 08:50:39 +08:00
committed by GitHub
parent 84a93a0b89
commit b93e449b2f
20 changed files with 1064 additions and 22 deletions

44
plugin/log/json.go Normal file
View File

@@ -0,0 +1,44 @@
package log
import (
"log/slog"
"strconv"
"time"
"github.com/coredns/coredns/plugin/pkg/dnstest"
clog "github.com/coredns/coredns/plugin/pkg/log"
"github.com/coredns/coredns/request"
"github.com/miekg/dns"
)
func logJSON(msg string, state request.Request, rr *dnstest.Recorder) {
// A missing response is not NOERROR (for example an ACL drop). Deferred
// errors have already been synthesized by ServeDNS, just as in text mode.
rcode := slog.Any("rcode", nil)
if rr.Msg != nil {
rc := dns.RcodeToString[rr.Rcode]
if rc == "" {
rc = strconv.Itoa(rr.Rcode)
}
rcode = slog.String("rcode", rc)
}
port, _ := strconv.Atoi(state.Port())
clog.InfoAttrs(msg,
slog.String("plugin", "log"),
slog.String("client_ip", state.IP()),
slog.Int("client_port", port),
slog.String("qname", state.Name()),
slog.String("qtype", state.Type()),
slog.String("qclass", state.Class()),
slog.String("protocol", state.Proto()),
slog.Int("id", int(state.Req.Id)),
slog.Int("opcode", state.Req.Opcode),
slog.Int("request_size", state.Len()),
slog.Bool("dnssec_ok", state.Do()),
slog.Int("bufsize", state.Size()),
rcode,
slog.Int("response_size", rr.Len),
slog.Float64("duration_seconds", time.Since(rr.Start).Seconds()),
)
}