2018-10-27 17:37:09 +03:00
|
|
|
// Package errors implements an error handling plugin.
|
2016-03-18 20:57:35 +00:00
|
|
|
package errors
|
|
|
|
|
|
|
|
|
|
import (
|
2018-04-22 08:34:35 +01:00
|
|
|
"context"
|
2018-10-27 17:37:09 +03:00
|
|
|
"regexp"
|
|
|
|
|
"sync/atomic"
|
|
|
|
|
"time"
|
|
|
|
|
"unsafe"
|
2016-03-18 20:57:35 +00:00
|
|
|
|
2017-09-14 09:36:06 +01:00
|
|
|
"github.com/coredns/coredns/plugin"
|
2018-05-10 03:04:46 +01:00
|
|
|
clog "github.com/coredns/coredns/plugin/pkg/log"
|
2017-02-21 22:51:47 -08:00
|
|
|
"github.com/coredns/coredns/request"
|
2016-04-11 07:56:38 +01:00
|
|
|
|
2016-03-18 20:57:35 +00:00
|
|
|
"github.com/miekg/dns"
|
|
|
|
|
)
|
|
|
|
|
|
2018-10-27 17:37:09 +03:00
|
|
|
var log = clog.NewWithPlugin("errors")
|
|
|
|
|
|
|
|
|
|
type pattern struct {
|
2021-07-09 16:23:02 +02:00
|
|
|
ptimer unsafe.Pointer
|
|
|
|
|
count uint32
|
|
|
|
|
period time.Duration
|
|
|
|
|
pattern *regexp.Regexp
|
2025-09-10 23:08:27 +03:00
|
|
|
logCallback func(format string, v ...any)
|
2025-12-10 10:15:49 +08:00
|
|
|
showFirst bool
|
2018-10-27 17:37:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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))
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-14 09:36:06 +01:00
|
|
|
// errorHandler handles DNS errors (and errors from other plugin).
|
2018-10-27 17:37:09 +03:00
|
|
|
type errorHandler struct {
|
|
|
|
|
patterns []*pattern
|
|
|
|
|
stopFlag uint32
|
|
|
|
|
Next plugin.Handler
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func newErrorHandler() *errorHandler {
|
2019-03-07 23:14:09 +03:00
|
|
|
return &errorHandler{}
|
2018-10-27 17:37:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *errorHandler) logPattern(i int) {
|
|
|
|
|
cnt := atomic.SwapUint32(&h.patterns[i].count, 0)
|
2025-12-10 10:15:49 +08:00
|
|
|
if cnt == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if cnt > 1 || !h.patterns[i].showFirst {
|
2021-07-09 16:23:02 +02:00
|
|
|
h.patterns[i].logCallback("%d errors like '%s' occurred in last %s",
|
2019-03-07 23:14:09 +03:00
|
|
|
cnt, h.patterns[i].pattern.String(), h.patterns[i].period)
|
2018-10-27 17:37:09 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-10 10:15:49 +08:00
|
|
|
// 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 {
|
2018-10-27 17:37:09 +03:00
|
|
|
if atomic.LoadUint32(&h.stopFlag) > 0 {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2025-12-10 10:15:49 +08:00
|
|
|
cnt := atomic.AddUint32(&h.patterns[i].count, 1)
|
|
|
|
|
if cnt == 1 {
|
2018-10-27 17:37:09 +03:00
|
|
|
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)
|
|
|
|
|
}
|
2025-12-10 10:15:49 +08:00
|
|
|
// 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
|
2018-10-27 17:37:09 +03:00
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-18 20:57:35 +00:00
|
|
|
|
2017-09-14 09:36:06 +01:00
|
|
|
// ServeDNS implements the plugin.Handler interface.
|
2018-10-27 17:37:09 +03:00
|
|
|
func (h *errorHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
2017-09-14 09:36:06 +01:00
|
|
|
rcode, err := plugin.NextOrFailure(h.Name(), h.Next, ctx, w, r)
|
2016-03-18 20:57:35 +00:00
|
|
|
|
|
|
|
|
if err != nil {
|
2018-10-27 17:37:09 +03:00
|
|
|
strErr := err.Error()
|
2025-12-10 10:15:49 +08:00
|
|
|
state := request.Request{W: w, Req: r}
|
|
|
|
|
|
|
|
|
|
// Default to error logging
|
|
|
|
|
logFunc := log.Errorf
|
|
|
|
|
|
2018-10-27 17:37:09 +03:00
|
|
|
for i := range h.patterns {
|
|
|
|
|
if h.patterns[i].pattern.MatchString(strErr) {
|
2025-12-10 10:15:49 +08:00
|
|
|
if h.consolidateError(i) {
|
|
|
|
|
// Error is consolidated, no need to log
|
2018-10-27 17:37:09 +03:00
|
|
|
return rcode, err
|
|
|
|
|
}
|
2025-12-10 10:15:49 +08:00
|
|
|
// consolidateError returned false (showFirst case)
|
|
|
|
|
// Use the pattern's configured log level
|
|
|
|
|
logFunc = h.patterns[i].logCallback
|
2018-10-27 17:37:09 +03:00
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-10 10:15:49 +08:00
|
|
|
|
|
|
|
|
// Log with the appropriate log level
|
|
|
|
|
logFunc("%d %s %s: %s", rcode, state.Name(), state.Type(), strErr)
|
2016-03-18 20:57:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rcode, err
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-27 17:37:09 +03:00
|
|
|
// Name implements the plugin.Handler interface.
|
|
|
|
|
func (h *errorHandler) Name() string { return "errors" }
|