mirror of
https://github.com/coredns/coredns.git
synced 2025-12-13 13:55: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:
@@ -65,6 +65,27 @@ func TestErrorsParse(t *testing.T) {
|
||||
consolidate 1m error1
|
||||
consolidate 5s error2
|
||||
}`, false, 2, false},
|
||||
{`errors {
|
||||
consolidate 1m error show_first
|
||||
}`, false, 1, false},
|
||||
{`errors {
|
||||
consolidate 1m error warning show_first
|
||||
}`, false, 1, false},
|
||||
{`errors {
|
||||
consolidate 1m error invalid_option
|
||||
}`, true, 0, false},
|
||||
{`errors {
|
||||
consolidate 1m error warning error
|
||||
}`, true, 0, false},
|
||||
{`errors {
|
||||
consolidate 1m error info debug
|
||||
}`, true, 0, false},
|
||||
{`errors {
|
||||
consolidate 1m error show_first warning
|
||||
}`, true, 0, false},
|
||||
{`errors {
|
||||
consolidate 1m error show_first info
|
||||
}`, true, 0, false},
|
||||
}
|
||||
for i, test := range tests {
|
||||
c := caddy.NewTestController("dns", test.inputErrorsRules)
|
||||
@@ -146,3 +167,86 @@ func TestProperLogCallbackIsSet(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShowFirstOption(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
inputErrorsRules string
|
||||
wantShowFirst bool
|
||||
wantLogLevel string
|
||||
}{
|
||||
{
|
||||
name: "show_first without log level",
|
||||
inputErrorsRules: `errors {
|
||||
consolidate 1m .* show_first
|
||||
}`,
|
||||
wantShowFirst: true,
|
||||
wantLogLevel: "[ERROR]", // default
|
||||
},
|
||||
{
|
||||
name: "show_first with warning log level",
|
||||
inputErrorsRules: `errors {
|
||||
consolidate 1m .* warning show_first
|
||||
}`,
|
||||
wantShowFirst: true,
|
||||
wantLogLevel: "[WARNING]",
|
||||
},
|
||||
{
|
||||
name: "show_first with error log level",
|
||||
inputErrorsRules: `errors {
|
||||
consolidate 1m .* error show_first
|
||||
}`,
|
||||
wantShowFirst: true,
|
||||
wantLogLevel: "[ERROR]",
|
||||
},
|
||||
{
|
||||
name: "no show_first",
|
||||
inputErrorsRules: `errors {
|
||||
consolidate 1m .*
|
||||
}`,
|
||||
wantShowFirst: false,
|
||||
wantLogLevel: "[ERROR]",
|
||||
},
|
||||
{
|
||||
name: "no show_first with log level",
|
||||
inputErrorsRules: `errors {
|
||||
consolidate 1m .* info
|
||||
}`,
|
||||
wantShowFirst: false,
|
||||
wantLogLevel: "[INFO]",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
buf := bytes.Buffer{}
|
||||
golog.SetOutput(&buf)
|
||||
clog.D.Set()
|
||||
|
||||
c := caddy.NewTestController("dns", tt.inputErrorsRules)
|
||||
h, err := errorsParse(c)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Failed to parse: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(h.patterns) != 1 {
|
||||
t.Errorf("Expected 1 pattern, got %d", len(h.patterns))
|
||||
return
|
||||
}
|
||||
|
||||
if h.patterns[0].showFirst != tt.wantShowFirst {
|
||||
t.Errorf("Expected showFirst=%v, got %v", tt.wantShowFirst, h.patterns[0].showFirst)
|
||||
}
|
||||
|
||||
// Test log level
|
||||
l := h.patterns[0].logCallback
|
||||
l("test log")
|
||||
|
||||
if log := buf.String(); !strings.Contains(log, tt.wantLogLevel) {
|
||||
t.Errorf("Expected log level %q, but got %q", tt.wantLogLevel, log)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user