mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-30 17:53:21 -04:00 
			
		
		
		
	add configurable log level to errors plugin (#4718)
Automatically submitted.
This commit is contained in:
		| @@ -22,12 +22,12 @@ Extra knobs are available with an expanded syntax: | ||||
|  | ||||
| ~~~ | ||||
| errors { | ||||
| 	consolidate DURATION REGEXP | ||||
| 	consolidate DURATION REGEXP [LEVEL] | ||||
| } | ||||
| ~~~ | ||||
|  | ||||
| Option `consolidate` allows collecting several error messages matching the regular expression **REGEXP** during **DURATION**. After the **DURATION** since receiving the first such message, the consolidated message will be printed to standard output, e.g. | ||||
|  | ||||
| Option `consolidate` allows collecting several error messages matching the regular expression **REGEXP** during **DURATION**. After the **DURATION** since receiving the first such message, the consolidated message will be printed to standard output with | ||||
| log level, which is configurable by optional option **LEVEL**. Supported options for **LEVEL** option are `warn`,`error`,`info` and `debug`. | ||||
| ~~~ | ||||
| 2 errors like '^read udp .* i/o timeout$' occurred in last 30s | ||||
| ~~~ | ||||
| @@ -53,7 +53,7 @@ Use the *forward* to resolve queries via 8.8.8.8 and print consolidated error me | ||||
| . { | ||||
|     forward . 8.8.8.8 | ||||
|     errors { | ||||
|         consolidate 5m ".* i/o timeout$" | ||||
|         consolidate 5m ".* i/o timeout$" warn | ||||
|         consolidate 30s "^Failed to .+" | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -22,6 +22,7 @@ type pattern struct { | ||||
| 	count       uint32 | ||||
| 	period      time.Duration | ||||
| 	pattern     *regexp.Regexp | ||||
| 	logCallback func(format string, v ...interface{}) | ||||
| } | ||||
|  | ||||
| func (p *pattern) timer() *time.Timer { | ||||
| @@ -46,7 +47,7 @@ func newErrorHandler() *errorHandler { | ||||
| func (h *errorHandler) logPattern(i int) { | ||||
| 	cnt := atomic.SwapUint32(&h.patterns[i].count, 0) | ||||
| 	if cnt > 0 { | ||||
| 		log.Errorf("%d errors like '%s' occurred in last %s", | ||||
| 		h.patterns[i].logCallback("%d errors like '%s' occurred in last %s", | ||||
| 			cnt, h.patterns[i].pattern.String(), h.patterns[i].period) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -14,6 +14,7 @@ import ( | ||||
|  | ||||
| 	"github.com/coredns/coredns/plugin" | ||||
| 	"github.com/coredns/coredns/plugin/pkg/dnstest" | ||||
| 	clog "github.com/coredns/coredns/plugin/pkg/log" | ||||
| 	"github.com/coredns/coredns/plugin/test" | ||||
|  | ||||
| 	"github.com/miekg/dns" | ||||
| @@ -71,7 +72,40 @@ func TestErrors(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestLogPattern(t *testing.T) { | ||||
| 	type args struct { | ||||
| 		logCallback func(format string, v ...interface{}) | ||||
| 	} | ||||
| 	tests := []struct { | ||||
| 		name string | ||||
| 		args args | ||||
| 		want string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "error log", | ||||
| 			args: args{logCallback: log.Errorf}, | ||||
| 			want: "[ERROR] plugin/errors: 4 errors like '^error.*!$' occurred in last 2s", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "warn log", | ||||
| 			args: args{logCallback: log.Warningf}, | ||||
| 			want: "[WARNING] plugin/errors: 4 errors like '^error.*!$' occurred in last 2s", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "info log", | ||||
| 			args: args{logCallback: log.Infof}, | ||||
| 			want: "[INFO] plugin/errors: 4 errors like '^error.*!$' occurred in last 2s", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "debug log", | ||||
| 			args: args{logCallback: log.Debugf}, | ||||
| 			want: "[DEBUG] plugin/errors: 4 errors like '^error.*!$' occurred in last 2s", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			buf := bytes.Buffer{} | ||||
| 			clog.D.Set() | ||||
| 			golog.SetOutput(&buf) | ||||
|  | ||||
| 			h := &errorHandler{ | ||||
| @@ -79,13 +113,15 @@ func TestLogPattern(t *testing.T) { | ||||
| 					count:       4, | ||||
| 					period:      2 * time.Second, | ||||
| 					pattern:     regexp.MustCompile("^error.*!$"), | ||||
| 					logCallback: tt.args.logCallback, | ||||
| 				}}, | ||||
| 			} | ||||
| 			h.logPattern(0) | ||||
|  | ||||
| 	expLog := "4 errors like '^error.*!$' occurred in last 2s" | ||||
| 	if log := buf.String(); !strings.Contains(log, expLog) { | ||||
| 		t.Errorf("Expected log %q, but got %q", expLog, log) | ||||
| 			if log := buf.String(); !strings.Contains(log, tt.want) { | ||||
| 				t.Errorf("Expected log %q, but got %q", tt.want, log) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -154,6 +190,7 @@ func TestStop(t *testing.T) { | ||||
| 		patterns: []*pattern{{ | ||||
| 			period:  2 * time.Second, | ||||
| 			pattern: regexp.MustCompile("^error.*!$"), | ||||
| 			logCallback: log.Errorf, | ||||
| 		}}, | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -66,7 +66,7 @@ func parseBlock(c *caddy.Controller, h *errorHandler) error { | ||||
| 	} | ||||
|  | ||||
| 	args := c.RemainingArgs() | ||||
| 	if len(args) != 2 { | ||||
| 	if len(args) < 2 || len(args) > 3 { | ||||
| 		return c.ArgErr() | ||||
| 	} | ||||
| 	p, err := time.ParseDuration(args[0]) | ||||
| @@ -77,7 +77,30 @@ func parseBlock(c *caddy.Controller, h *errorHandler) error { | ||||
| 	if err != nil { | ||||
| 		return c.Err(err.Error()) | ||||
| 	} | ||||
| 	h.patterns = append(h.patterns, &pattern{period: p, pattern: re}) | ||||
| 	lc, err := parseLogLevel(c, args) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	h.patterns = append(h.patterns, &pattern{period: p, pattern: re, logCallback: lc}) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func parseLogLevel(c *caddy.Controller, args []string) (func(format string, v ...interface{}), error) { | ||||
| 	if len(args) != 3 { | ||||
| 		return log.Errorf, nil | ||||
| 	} | ||||
|  | ||||
| 	switch args[2] { | ||||
| 	case "warn": | ||||
| 		return log.Warningf, nil | ||||
| 	case "error": | ||||
| 		return log.Errorf, nil | ||||
| 	case "info": | ||||
| 		return log.Infof, nil | ||||
| 	case "debug": | ||||
| 		return log.Debugf, nil | ||||
| 	default: | ||||
| 		return nil, c.Err("unknown log level argument in consolidate") | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,9 +1,13 @@ | ||||
| package errors | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	golog "log" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/coredns/caddy" | ||||
| 	clog "github.com/coredns/coredns/plugin/pkg/log" | ||||
| ) | ||||
|  | ||||
| func TestErrorsParse(t *testing.T) { | ||||
| @@ -67,3 +71,65 @@ func TestErrorsParse(t *testing.T) { | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestProperLogCallbackIsSet(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name             string | ||||
| 		inputErrorsRules string | ||||
| 		wantLogLevel     string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "warn is parsed properly", | ||||
| 			inputErrorsRules: `errors { | ||||
| 		        consolidate 1m .* warn | ||||
| 		    }`, | ||||
| 			wantLogLevel: "[WARNING]", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "error is parsed properly", | ||||
| 			inputErrorsRules: `errors { | ||||
| 		        consolidate 1m .* error | ||||
| 		    }`, | ||||
| 			wantLogLevel: "[ERROR]", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "info is parsed properly", | ||||
| 			inputErrorsRules: `errors { | ||||
| 		        consolidate 1m .* info | ||||
| 		    }`, | ||||
| 			wantLogLevel: "[INFO]", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "debug is parsed properly", | ||||
| 			inputErrorsRules: `errors { | ||||
| 		        consolidate 1m .* debug | ||||
| 		    }`, | ||||
| 			wantLogLevel: "[DEBUG]", | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "default is error", | ||||
| 			inputErrorsRules: `errors { | ||||
| 		        consolidate 1m .* | ||||
| 		    }`, | ||||
| 			wantLogLevel: "[ERROR]", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	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, _ := errorsParse(c) | ||||
|  | ||||
| 			l := h.patterns[0].logCallback | ||||
| 			l("some error happened") | ||||
|  | ||||
| 			if log := buf.String(); !strings.Contains(log, tt.wantLogLevel) { | ||||
| 				t.Errorf("Expected log %q, but got %q", tt.wantLogLevel, log) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user