mirror of
https://github.com/coredns/coredns.git
synced 2025-12-15 06:45:11 -05:00
Add optional show_first flag to consolidate directive that logs the first error immediately and then consolidates subsequent errors. When show_first is enabled: - The first matching error is logged immediately with full details (rcode, domain, type, error message) using the configured log level - Subsequent matching errors are consolidated during the period - At period end: - If only one error occurred, no summary is printed (already logged) - If multiple errors occurred, summary shows the total count Syntax: consolidate DURATION REGEXP [LEVEL] [show_first] Example with 3 errors: [WARNING] 2 example.org. A: read udp 10.0.0.1:53->8.8.8.8:53: i/o timeout [WARNING] 3 errors like '^read udp .* i/o timeout$' occurred in last 30s Example with 1 error: [WARNING] 2 example.org. A: read udp 10.0.0.1:53->8.8.8.8:53: i/o timeout Implementation details: - Add showFirst bool to pattern struct - Rename inc() to consolidateError(), return false for showFirst case - Use function pointer in ServeDNS to unify log calls with proper level - Simplify logPattern() with single condition (cnt > 1 || !showFirst) - Refactor parseLogLevel() to parseOptionalParams() with map-based dispatch - Validate parameter order: log level must come before show_first - Update README.md with show_first documentation and examples - Add comprehensive test cases for show_first functionality Signed-off-by: cangming <cangming@cangming.app>
This commit is contained in:
@@ -23,6 +23,7 @@ type pattern struct {
|
||||
period time.Duration
|
||||
pattern *regexp.Regexp
|
||||
logCallback func(format string, v ...any)
|
||||
showFirst bool
|
||||
}
|
||||
|
||||
func (p *pattern) timer() *time.Timer {
|
||||
@@ -46,17 +47,24 @@ func newErrorHandler() *errorHandler {
|
||||
|
||||
func (h *errorHandler) logPattern(i int) {
|
||||
cnt := atomic.SwapUint32(&h.patterns[i].count, 0)
|
||||
if cnt > 0 {
|
||||
if cnt == 0 {
|
||||
return
|
||||
}
|
||||
if cnt > 1 || !h.patterns[i].showFirst {
|
||||
h.patterns[i].logCallback("%d errors like '%s' occurred in last %s",
|
||||
cnt, h.patterns[i].pattern.String(), h.patterns[i].period)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *errorHandler) inc(i int) bool {
|
||||
// consolidateError records an error occurrence for pattern i.
|
||||
// Returns false when cnt == 1 and showFirst is configured, so the error
|
||||
// will be printed by the caller using the pattern's logCallback.
|
||||
func (h *errorHandler) consolidateError(i int) bool {
|
||||
if atomic.LoadUint32(&h.stopFlag) > 0 {
|
||||
return false
|
||||
}
|
||||
if atomic.AddUint32(&h.patterns[i].count, 1) == 1 {
|
||||
cnt := atomic.AddUint32(&h.patterns[i].count, 1)
|
||||
if cnt == 1 {
|
||||
ind := i
|
||||
t := time.AfterFunc(h.patterns[ind].period, func() {
|
||||
h.logPattern(ind)
|
||||
@@ -65,6 +73,9 @@ func (h *errorHandler) inc(i int) bool {
|
||||
if atomic.LoadUint32(&h.stopFlag) > 0 && t.Stop() {
|
||||
h.logPattern(ind)
|
||||
}
|
||||
// If showFirst is enabled, return false so the first error
|
||||
// will be printed by the caller using the pattern's logCallback
|
||||
return !h.patterns[i].showFirst
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -85,16 +96,26 @@ func (h *errorHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dn
|
||||
|
||||
if err != nil {
|
||||
strErr := err.Error()
|
||||
state := request.Request{W: w, Req: r}
|
||||
|
||||
// Default to error logging
|
||||
logFunc := log.Errorf
|
||||
|
||||
for i := range h.patterns {
|
||||
if h.patterns[i].pattern.MatchString(strErr) {
|
||||
if h.inc(i) {
|
||||
if h.consolidateError(i) {
|
||||
// Error is consolidated, no need to log
|
||||
return rcode, err
|
||||
}
|
||||
// consolidateError returned false (showFirst case)
|
||||
// Use the pattern's configured log level
|
||||
logFunc = h.patterns[i].logCallback
|
||||
break
|
||||
}
|
||||
}
|
||||
state := request.Request{W: w, Req: r}
|
||||
log.Errorf("%d %s %s: %s", rcode, state.Name(), state.Type(), strErr)
|
||||
|
||||
// Log with the appropriate log level
|
||||
logFunc("%d %s %s: %s", rcode, state.Name(), state.Type(), strErr)
|
||||
}
|
||||
|
||||
return rcode, err
|
||||
|
||||
Reference in New Issue
Block a user