diff options
Diffstat (limited to 'plugin/errors/errors.go')
-rw-r--r-- | plugin/errors/errors.go | 93 |
1 files changed, 88 insertions, 5 deletions
diff --git a/plugin/errors/errors.go b/plugin/errors/errors.go index 88027b191..aa4b384b8 100644 --- a/plugin/errors/errors.go +++ b/plugin/errors/errors.go @@ -1,8 +1,12 @@ -// Package errors implements an HTTP error handling plugin. +// Package errors implements an error handling plugin. package errors import ( "context" + "regexp" + "sync/atomic" + "time" + "unsafe" "github.com/coredns/coredns/plugin" clog "github.com/coredns/coredns/plugin/pkg/log" @@ -11,19 +15,98 @@ import ( "github.com/miekg/dns" ) +var log = clog.NewWithPlugin("errors") + +type pattern struct { + ptimer unsafe.Pointer + count uint32 + period time.Duration + pattern *regexp.Regexp +} + +func (p *pattern) timer() *time.Timer { + return (*time.Timer)(atomic.LoadPointer(&p.ptimer)) +} + +func (p *pattern) setTimer(t *time.Timer) { + atomic.StorePointer(&p.ptimer, unsafe.Pointer(t)) +} + // errorHandler handles DNS errors (and errors from other plugin). -type errorHandler struct{ Next plugin.Handler } +type errorHandler struct { + patterns []*pattern + eLogger func(int, string, string, string) + cLogger func(uint32, string, time.Duration) + stopFlag uint32 + Next plugin.Handler +} + +func newErrorHandler() *errorHandler { + return &errorHandler{eLogger: errorLogger, cLogger: consLogger} +} + +func errorLogger(code int, qName, qType, err string) { + log.Errorf("%d %s %s: %s", code, qName, qType, err) +} + +func consLogger(cnt uint32, pattern string, p time.Duration) { + log.Errorf("%d errors like '%s' occured in last %s", cnt, pattern, p) +} + +func (h *errorHandler) logPattern(i int) { + cnt := atomic.SwapUint32(&h.patterns[i].count, 0) + if cnt > 0 { + h.cLogger(cnt, h.patterns[i].pattern.String(), h.patterns[i].period) + } +} + +func (h *errorHandler) inc(i int) bool { + if atomic.LoadUint32(&h.stopFlag) > 0 { + return false + } + if atomic.AddUint32(&h.patterns[i].count, 1) == 1 { + ind := i + t := time.AfterFunc(h.patterns[ind].period, func() { + h.logPattern(ind) + }) + h.patterns[ind].setTimer(t) + if atomic.LoadUint32(&h.stopFlag) > 0 && t.Stop() { + h.logPattern(ind) + } + } + return true +} + +func (h *errorHandler) stop() { + atomic.StoreUint32(&h.stopFlag, 1) + for i := range h.patterns { + t := h.patterns[i].timer() + if t != nil && t.Stop() { + h.logPattern(i) + } + } +} // ServeDNS implements the plugin.Handler interface. -func (h errorHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { +func (h *errorHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { rcode, err := plugin.NextOrFailure(h.Name(), h.Next, ctx, w, r) if err != nil { + strErr := err.Error() + for i := range h.patterns { + if h.patterns[i].pattern.MatchString(strErr) { + if h.inc(i) { + return rcode, err + } + break + } + } state := request.Request{W: w, Req: r} - clog.Errorf("%d %s %s: %v", rcode, state.Name(), state.Type(), err) + h.eLogger(rcode, state.Name(), state.Type(), strErr) } return rcode, err } -func (h errorHandler) Name() string { return "errors" } +// Name implements the plugin.Handler interface. +func (h *errorHandler) Name() string { return "errors" } |