diff options
author | 2017-09-14 09:36:06 +0100 | |
---|---|---|
committer | 2017-09-14 09:36:06 +0100 | |
commit | d8714e64e400ef873c2adc4d929a07d7890727b9 (patch) | |
tree | c9fa4c157e6af12eb1517654f8d23ca5d5619513 /plugin/errors | |
parent | b984aa45595dc95253b91191afe7d3ee29e71b48 (diff) | |
download | coredns-d8714e64e400ef873c2adc4d929a07d7890727b9.tar.gz coredns-d8714e64e400ef873c2adc4d929a07d7890727b9.tar.zst coredns-d8714e64e400ef873c2adc4d929a07d7890727b9.zip |
Remove the word middleware (#1067)
* Rename middleware to plugin
first pass; mostly used 'sed', few spots where I manually changed
text.
This still builds a coredns binary.
* fmt error
* Rename AddMiddleware to AddPlugin
* Readd AddMiddleware to remain backwards compat
Diffstat (limited to 'plugin/errors')
-rw-r--r-- | plugin/errors/README.md | 22 | ||||
-rw-r--r-- | plugin/errors/errors.go | 79 | ||||
-rw-r--r-- | plugin/errors/errors_test.go | 73 | ||||
-rw-r--r-- | plugin/errors/setup.go | 55 | ||||
-rw-r--r-- | plugin/errors/setup_test.go | 45 |
5 files changed, 274 insertions, 0 deletions
diff --git a/plugin/errors/README.md b/plugin/errors/README.md new file mode 100644 index 000000000..21b8f4848 --- /dev/null +++ b/plugin/errors/README.md @@ -0,0 +1,22 @@ +# errors + +*errors* enables error logging. + +Any errors encountered during the query processing will be printed to standard output. + +## Syntax + +~~~ +errors +~~~ + +## Examples + +Use the *whoami* to respond to queries and Log errors to standard output. + +~~~ corefile +. { + whoami + errors +} +~~~ diff --git a/plugin/errors/errors.go b/plugin/errors/errors.go new file mode 100644 index 000000000..a313f2e0d --- /dev/null +++ b/plugin/errors/errors.go @@ -0,0 +1,79 @@ +// Package errors implements an HTTP error handling plugin. +package errors + +import ( + "fmt" + "log" + "runtime" + "strings" + "time" + + "github.com/coredns/coredns/plugin" + "github.com/coredns/coredns/request" + + "github.com/miekg/dns" + "golang.org/x/net/context" +) + +// errorHandler handles DNS errors (and errors from other plugin). +type errorHandler struct { + Next plugin.Handler + LogFile string + Log *log.Logger +} + +// ServeDNS implements the plugin.Handler interface. +func (h errorHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + defer h.recovery(ctx, w, r) + + rcode, err := plugin.NextOrFailure(h.Name(), h.Next, ctx, w, r) + + if err != nil { + state := request.Request{W: w, Req: r} + errMsg := fmt.Sprintf("%s [ERROR %d %s %s] %v", time.Now().Format(timeFormat), rcode, state.Name(), state.Type(), err) + + h.Log.Println(errMsg) + } + + return rcode, err +} + +func (h errorHandler) Name() string { return "errors" } + +func (h errorHandler) recovery(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) { + rec := recover() + if rec == nil { + return + } + + // Obtain source of panic + // From: https://gist.github.com/swdunlop/9629168 + var name, file string // function name, file name + var line int + var pc [16]uintptr + n := runtime.Callers(3, pc[:]) + for _, pc := range pc[:n] { + fn := runtime.FuncForPC(pc) + if fn == nil { + continue + } + file, line = fn.FileLine(pc) + name = fn.Name() + if !strings.HasPrefix(name, "runtime.") { + break + } + } + + // Trim file path + delim := "/coredns/" + pkgPathPos := strings.Index(file, delim) + if pkgPathPos > -1 && len(file) > pkgPathPos+len(delim) { + file = file[pkgPathPos+len(delim):] + } + + panicMsg := fmt.Sprintf("%s [PANIC %s %s] %s:%d - %v", time.Now().Format(timeFormat), r.Question[0].Name, dns.Type(r.Question[0].Qtype), file, line, rec) + // Currently we don't use the function name, since file:line is more conventional + h.Log.Printf(panicMsg) +} + +const timeFormat = "02/Jan/2006:15:04:05 -0700" diff --git a/plugin/errors/errors_test.go b/plugin/errors/errors_test.go new file mode 100644 index 000000000..039562a56 --- /dev/null +++ b/plugin/errors/errors_test.go @@ -0,0 +1,73 @@ +package errors + +import ( + "bytes" + "errors" + "fmt" + "log" + "strings" + "testing" + + "github.com/coredns/coredns/plugin" + "github.com/coredns/coredns/plugin/pkg/dnsrecorder" + "github.com/coredns/coredns/plugin/test" + + "github.com/miekg/dns" + "golang.org/x/net/context" +) + +func TestErrors(t *testing.T) { + buf := bytes.Buffer{} + em := errorHandler{Log: log.New(&buf, "", 0)} + + testErr := errors.New("test error") + tests := []struct { + next plugin.Handler + expectedCode int + expectedLog string + expectedErr error + }{ + { + next: genErrorHandler(dns.RcodeSuccess, nil), + expectedCode: dns.RcodeSuccess, + expectedLog: "", + expectedErr: nil, + }, + { + next: genErrorHandler(dns.RcodeNotAuth, testErr), + expectedCode: dns.RcodeNotAuth, + expectedLog: fmt.Sprintf("[ERROR %d %s] %v\n", dns.RcodeNotAuth, "example.org. A", testErr), + expectedErr: testErr, + }, + } + + ctx := context.TODO() + req := new(dns.Msg) + req.SetQuestion("example.org.", dns.TypeA) + + for i, tc := range tests { + em.Next = tc.next + buf.Reset() + rec := dnsrecorder.New(&test.ResponseWriter{}) + code, err := em.ServeDNS(ctx, rec, req) + + if err != tc.expectedErr { + t.Errorf("Test %d: Expected error %v, but got %v", + i, tc.expectedErr, err) + } + if code != tc.expectedCode { + t.Errorf("Test %d: Expected status code %d, but got %d", + i, tc.expectedCode, code) + } + if log := buf.String(); !strings.Contains(log, tc.expectedLog) { + t.Errorf("Test %d: Expected log %q, but got %q", + i, tc.expectedLog, log) + } + } +} + +func genErrorHandler(rcode int, err error) plugin.Handler { + return plugin.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + return rcode, err + }) +} diff --git a/plugin/errors/setup.go b/plugin/errors/setup.go new file mode 100644 index 000000000..19bdcdb80 --- /dev/null +++ b/plugin/errors/setup.go @@ -0,0 +1,55 @@ +package errors + +import ( + "fmt" + "log" + "os" + + "github.com/coredns/coredns/core/dnsserver" + "github.com/coredns/coredns/plugin" + + "github.com/mholt/caddy" +) + +func init() { + caddy.RegisterPlugin("errors", caddy.Plugin{ + ServerType: "dns", + Action: setup, + }) +} + +func setup(c *caddy.Controller) error { + handler, err := errorsParse(c) + if err != nil { + return plugin.Error("errors", err) + } + + handler.Log = log.New(os.Stdout, "", 0) + + dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { + handler.Next = next + return handler + }) + + return nil +} + +func errorsParse(c *caddy.Controller) (errorHandler, error) { + handler := errorHandler{} + + for c.Next() { + args := c.RemainingArgs() + switch len(args) { + case 0: + handler.LogFile = "stdout" + case 1: + if args[0] != "stdout" { + return handler, fmt.Errorf("invalid log file: %s", args[0]) + } + handler.LogFile = args[0] + default: + return handler, c.ArgErr() + } + } + return handler, nil +} diff --git a/plugin/errors/setup_test.go b/plugin/errors/setup_test.go new file mode 100644 index 000000000..bae85da32 --- /dev/null +++ b/plugin/errors/setup_test.go @@ -0,0 +1,45 @@ +package errors + +import ( + "testing" + + "github.com/mholt/caddy" +) + +func TestErrorsParse(t *testing.T) { + tests := []struct { + inputErrorsRules string + shouldErr bool + expectedErrorHandler errorHandler + }{ + {`errors`, false, errorHandler{ + LogFile: "stdout", + }}, + {`errors stdout`, false, errorHandler{ + LogFile: "stdout", + }}, + {`errors errors.txt`, true, errorHandler{ + LogFile: "", + }}, + {`errors visible`, true, errorHandler{ + LogFile: "", + }}, + {`errors { log visible }`, true, errorHandler{ + LogFile: "stdout", + }}, + } + for i, test := range tests { + c := caddy.NewTestController("dns", test.inputErrorsRules) + actualErrorsRule, err := errorsParse(c) + + if err == nil && test.shouldErr { + t.Errorf("Test %d didn't error, but it should have", i) + } else if err != nil && !test.shouldErr { + t.Errorf("Test %d errored, but it shouldn't have; got '%v'", i, err) + } + if actualErrorsRule.LogFile != test.expectedErrorHandler.LogFile { + t.Errorf("Test %d expected LogFile to be %s, but got %s", + i, test.expectedErrorHandler.LogFile, actualErrorsRule.LogFile) + } + } +} |