diff --git a/middleware/errors/README.md b/middleware/errors/README.md index aa3b170af..461cdfec8 100644 --- a/middleware/errors/README.md +++ b/middleware/errors/README.md @@ -6,35 +6,21 @@ TODO: what are errors. ## Syntax ~~~ -errors [LOGFILE] +errors [FILE] ~~~ -* **LOGFILE** is the path to the error log file to create (or append to), relative to the current - working directory. It can also be `stdout` or `stderr` to write to the console, syslog to write to the - system log (except on Windows), or visible to write the error (including full stack trace, if - applicable) to the response. Writing errors to the response is NOT advised except in local debug - situations. The default is stderr. The above syntax will simply enable error reporting on the - server. To specify custom error pages, open a block: - -~~~ -errors { - what where -} -~~~ - -* `what` can only be `log`. -* `where` is the path to the log file (as described above) and you can enable rotation to manage the log files. +* **FILE** is the log file to create (or append to). The *only* valid name for **FILE** is *stdout* ## Examples -Log errors into a file in the parent directory: +Log errors to *stdout*. ~~~ -errors ../error.log +errors ~~~ -Make errors visible to the client (for debugging only): +Log errors to *stdout*. ~~~ -errors visible +errors stdout ~~~ diff --git a/middleware/errors/errors.go b/middleware/errors/errors.go index d2b6f3a22..057e65089 100644 --- a/middleware/errors/errors.go +++ b/middleware/errors/errors.go @@ -20,7 +20,6 @@ type errorHandler struct { Next middleware.Handler LogFile string Log *log.Logger - Debug bool // if true, errors are written out to client rather than to a log } // ServeDNS implements the middleware.Handler interface. @@ -33,15 +32,6 @@ func (h errorHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns 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) - if h.Debug { - // Write error to response as a txt message instead of to log - answer := debugMsg(rcode, r) - txt, _ := dns.NewRR(". IN 0 TXT " + errMsg) - answer.Answer = append(answer.Answer, txt) - state.SizeAndDo(answer) - w.WriteMsg(answer) - return 0, err - } h.Log.Println(errMsg) } @@ -56,7 +46,6 @@ func (h errorHandler) recovery(ctx context.Context, w dns.ResponseWriter, r *dns return } - state := request.Request{W: w, Req: r} // Obtain source of panic // From: https://gist.github.com/swdunlop/9629168 var name, file string // function name, file name @@ -83,20 +72,8 @@ func (h errorHandler) recovery(ctx context.Context, w dns.ResponseWriter, r *dns } 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) - if h.Debug { - // Write error and stack trace to the response rather than to a log - var stackBuf [4096]byte - stack := stackBuf[:runtime.Stack(stackBuf[:], false)] - answer := debugMsg(dns.RcodeServerFailure, r) - // add stack buf in TXT, limited to 255 chars for now. - txt, _ := dns.NewRR(". IN 0 TXT " + string(stack[:255])) - answer.Answer = append(answer.Answer, txt) - state.SizeAndDo(answer) - w.WriteMsg(answer) - } else { - // Currently we don't use the function name, since file:line is more conventional - h.Log.Printf(panicMsg) - } + // Currently we don't use the function name, since file:line is more conventional + h.Log.Printf(panicMsg) } // debugMsg creates a debug message that gets send back to the client. diff --git a/middleware/errors/errors_test.go b/middleware/errors/errors_test.go index 1d389ba47..5e565964c 100644 --- a/middleware/errors/errors_test.go +++ b/middleware/errors/errors_test.go @@ -66,30 +66,6 @@ func TestErrors(t *testing.T) { } } -func TestVisibleErrorWithPanic(t *testing.T) { - const panicMsg = "I'm a panic" - eh := errorHandler{ - Debug: true, - Next: middleware.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { - panic(panicMsg) - }), - } - - ctx := context.TODO() - req := new(dns.Msg) - req.SetQuestion("example.org.", dns.TypeA) - - rec := dnsrecorder.New(&test.ResponseWriter{}) - - code, err := eh.ServeDNS(ctx, rec, req) - if code != 0 { - t.Errorf("Expected error handler to return 0 (it should write to response), got status %d", code) - } - if err != nil { - t.Errorf("Expected error handler to return nil error (it should panic!), but got '%v'", err) - } -} - func genErrorHandler(rcode int, err error) middleware.Handler { return middleware.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { return rcode, err diff --git a/middleware/errors/setup.go b/middleware/errors/setup.go index 787b89724..af9a7bbff 100644 --- a/middleware/errors/setup.go +++ b/middleware/errors/setup.go @@ -1,14 +1,13 @@ package errors import ( - "io" + "fmt" "log" "os" "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/middleware" - "github.com/hashicorp/go-syslog" "github.com/mholt/caddy" ) @@ -25,34 +24,7 @@ func setup(c *caddy.Controller) error { return middleware.Error("errors", err) } - var writer io.Writer - - switch handler.LogFile { - case "visible": - handler.Debug = true - case "stdout": - writer = os.Stdout - case "stderr": - writer = os.Stderr - case "syslog": - writer, err = gsyslog.NewLogger(gsyslog.LOG_ERR, "LOCAL0", "coredns") - if err != nil { - return middleware.Error("errors", err) - } - default: - if handler.LogFile == "" { - writer = os.Stderr // default - break - } - - var file *os.File - file, err = os.OpenFile(handler.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) - if err != nil { - return middleware.Error("errors", err) - } - writer = file - } - handler.Log = log.New(writer, "", 0) + handler.Log = log.New(os.Stdout, "", 0) dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler { handler.Next = next @@ -65,47 +37,19 @@ func setup(c *caddy.Controller) error { func errorsParse(c *caddy.Controller) (errorHandler, error) { handler := errorHandler{} - optionalBlock := func() (bool, error) { - var hadBlock bool - - for c.NextBlock() { - hadBlock = true - - what := c.Val() - if !c.NextArg() { - return hadBlock, c.ArgErr() - } - where := c.Val() - - if what == "log" { - if where == "visible" { - handler.Debug = true - } else { - handler.LogFile = where - } - } - } - return hadBlock, nil - } - for c.Next() { - // Configuration may be in a block - hadBlock, err := optionalBlock() - if err != nil { - return handler, err - } - - // Otherwise, the only argument would be an error log file name or 'visible' - if !hadBlock { - if c.NextArg() { - if c.Val() == "visible" { - handler.Debug = true - } else { - handler.LogFile = c.Val() - } + 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/middleware/errors/setup_test.go b/middleware/errors/setup_test.go index adae0009c..bae85da32 100644 --- a/middleware/errors/setup_test.go +++ b/middleware/errors/setup_test.go @@ -13,18 +13,19 @@ func TestErrorsParse(t *testing.T) { expectedErrorHandler errorHandler }{ {`errors`, false, errorHandler{ + LogFile: "stdout", + }}, + {`errors stdout`, false, errorHandler{ + LogFile: "stdout", + }}, + {`errors errors.txt`, true, errorHandler{ LogFile: "", }}, - {`errors errors.txt`, false, errorHandler{ - LogFile: "errors.txt", - }}, - {`errors visible`, false, errorHandler{ + {`errors visible`, true, errorHandler{ LogFile: "", - Debug: true, }}, - {`errors { log visible }`, false, errorHandler{ - LogFile: "", - Debug: true, + {`errors { log visible }`, true, errorHandler{ + LogFile: "stdout", }}, } for i, test := range tests { @@ -40,9 +41,5 @@ func TestErrorsParse(t *testing.T) { t.Errorf("Test %d expected LogFile to be %s, but got %s", i, test.expectedErrorHandler.LogFile, actualErrorsRule.LogFile) } - if actualErrorsRule.Debug != test.expectedErrorHandler.Debug { - t.Errorf("Test %d expected Debug to be %v, but got %v", - i, test.expectedErrorHandler.Debug, actualErrorsRule.Debug) - } } } diff --git a/middleware/log/README.md b/middleware/log/README.md index 812fa20bb..e4808433f 100644 --- a/middleware/log/README.md +++ b/middleware/log/README.md @@ -1,6 +1,6 @@ # log -*log* enables query logging. +*log* enables query logging to standard output. ## Syntax @@ -8,20 +8,20 @@ log ~~~ -* With no arguments, a query log entry is written to query.log in the common log format for all requests +* With no arguments, a query log entry is written to *stdout* in the common log format for all requests ~~~ txt log FILE ~~~ -* **FILE** is the log file to create (or append to). +* **FILE** is the log file to create (or append to). The *only* valid name for **FILE** is *stdout*. ~~~ txt log [NAME] FILE [FORMAT] ~~~ * `NAME` is the name to match in order to be logged -* `FILE` is the log file to create (or append to) +* `FILE` is the log file (again only *stdout* is allowed here). * `FORMAT` is the log format to use (default is Common Log Format) You can further specify the class of responses that get logged: @@ -45,9 +45,8 @@ If no class is specified, it defaults to *all*. ## Log File -The log file can be any filename. It could also be *stdout* or *stderr* to write the log to the console, -or *syslog* to write to the system log (except on Windows). If the log file does not exist beforehand, -CoreDNS will create it before appending to it. +The "log file" can only be *stdout*. CoreDNS expects another service to pick up this output and deal +with it, i.e. journald when using systemd or Docker's logging capabilities. ## Log Format @@ -80,22 +79,22 @@ The default Common Log Format is: ## Examples -Log all requests to a file: +Log all requests to stdout ~~~ -log /var/log/query.log +log stdout ~~~ -Custom log format: +Custom log format, for all zones (`.`) ~~~ -log . ../query.log "{proto} Request: {name} {type} {>id}" +log . stdout "{proto} Request: {name} {type} {>id}" ~~~ Only log denials for example.org (and below to a file) ~~~ -log example.org example-query-log { +log example.org stdout { class denial } ~~~ diff --git a/middleware/log/log.go b/middleware/log/log.go index 456ed9aba..aacffa663 100644 --- a/middleware/log/log.go +++ b/middleware/log/log.go @@ -78,8 +78,8 @@ type Rule struct { } const ( - // DefaultLogFilename is the default log filename. - DefaultLogFilename = "query.log" + // DefaultLogFilename is the default output name. + DefaultLogFilename = "stdout" // CommonLogFormat is the common log format. CommonLogFormat = `{remote} ` + CommonLogEmptyValue + ` [{when}] "{type} {class} {name} {proto} {size} {>do} {>bufsize}" {rcode} {rsize} {duration}` // CommonLogEmptyValue is the common empty log value. diff --git a/middleware/log/setup.go b/middleware/log/setup.go index ce4f5c44d..498829888 100644 --- a/middleware/log/setup.go +++ b/middleware/log/setup.go @@ -1,7 +1,7 @@ package log import ( - "io" + "fmt" "log" "os" @@ -9,7 +9,6 @@ import ( "github.com/coredns/coredns/middleware" "github.com/coredns/coredns/middleware/pkg/response" - "github.com/hashicorp/go-syslog" "github.com/mholt/caddy" "github.com/miekg/dns" ) @@ -30,25 +29,10 @@ func setup(c *caddy.Controller) error { // Open the log files for writing when the server starts c.OnStartup(func() error { for i := 0; i < len(rules); i++ { - var err error - var writer io.Writer - - if rules[i].OutputFile == "stdout" { - writer = os.Stdout - } else if rules[i].OutputFile == "stderr" { - writer = os.Stderr - } else if rules[i].OutputFile == "syslog" { - writer, err = gsyslog.NewLogger(gsyslog.LOG_INFO, "LOCAL0", "coredns") - if err != nil { - return middleware.Error("log", err) - } - } else { - var file *os.File - file, err = os.OpenFile(rules[i].OutputFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) - if err != nil { - return middleware.Error("log", err) - } - writer = file + // We only support stdout + writer := os.Stdout + if rules[i].OutputFile != "stdout" { + return middleware.Error("log", fmt.Errorf("invalid log file: %s", rules[i].OutputFile)) } rules[i].Log = log.New(writer, "", 0) @@ -78,7 +62,7 @@ func logParse(c *caddy.Controller) ([]Rule, error) { Format: DefaultLogFormat, }) } else if len(args) == 1 { - // Only an output file specified + // Only an output file specified. rules = append(rules, Rule{ NameScope: ".", OutputFile: args[0], diff --git a/middleware/log/setup_test.go b/middleware/log/setup_test.go index 48bfbfa04..596239312 100644 --- a/middleware/log/setup_test.go +++ b/middleware/log/setup_test.go @@ -45,7 +45,7 @@ func TestLogParse(t *testing.T) { Format: CombinedLogFormat, }}}, {`log example.org. log.txt - log example.net accesslog.txt {combined}`, false, []Rule{{ + log example.net accesslog.txt {combined}`, false, []Rule{{ NameScope: "example.org.", OutputFile: "log.txt", Format: DefaultLogFormat,