diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2018-10-03 14:58:09 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2018-10-16 22:10:56 +0200 |
commit | 35fbfb19a173b01bc881f2bbc5d104136633a7ec (patch) | |
tree | 636d0d51fa262dc808eb3c5cc9cf92ad977a0c6a /hugolib/site.go | |
parent | 3a3089121b852332b5744d1f566959c8cf93cef4 (diff) |
commands: Show server error info in browser
The main item in this commit is showing of errors with a file context when running `hugo server`.
This can be turned off: `hugo server --disableBrowserError` (can also be set in `config.toml`).
But to get there, the error handling in Hugo needed a revision. There are some items left TODO for commits soon to follow, most notable errors in content and config files.
Fixes #5284
Fixes #5290
See #5325
See #5324
Diffstat (limited to 'hugolib/site.go')
-rw-r--r-- | hugolib/site.go | 105 |
1 files changed, 51 insertions, 54 deletions
diff --git a/hugolib/site.go b/hugolib/site.go index 1196496d3..687c6338c 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -15,7 +15,6 @@ package hugolib import ( "context" - "errors" "fmt" "html/template" "io" @@ -29,6 +28,9 @@ import ( "strings" "time" + _errors "github.com/pkg/errors" + + "github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/publisher" "github.com/gohugoio/hugo/resource" @@ -754,8 +756,6 @@ func (s *Site) processPartial(events []fsnotify.Event) (whatChanged, error) { return whatChanged{}, err } - s.TemplateHandler().PrintErrors() - for i := 1; i < len(sites); i++ { site := sites[i] var err error @@ -861,7 +861,7 @@ func (s *Site) handleDataFile(r source.ReadableFile) error { f, err := r.Open() if err != nil { - return fmt.Errorf("Failed to open data file %q: %s", r.LogicalName(), err) + return _errors.Wrapf(err, "Failed to open data file %q:", r.LogicalName()) } defer f.Close() @@ -942,7 +942,7 @@ func (s *Site) handleDataFile(r source.ReadableFile) error { func (s *Site) readData(f source.ReadableFile) (interface{}, error) { file, err := f.Open() if err != nil { - return nil, fmt.Errorf("readData: failed to open data file: %s", err) + return nil, _errors.Wrap(err, "readData: failed to open data file") } defer file.Close() content := helpers.ReaderToBytes(file) @@ -1558,26 +1558,52 @@ func (s *Site) preparePages() error { } } - if len(errors) != 0 { - return fmt.Errorf("Prepare pages failed: %.100q…", errors) + return s.pickOneAndLogTheRest(errors) +} + +func (s *Site) errorCollator(results <-chan error, errs chan<- error) { + var errors []error + for e := range results { + errors = append(errors, e) } - return nil + errs <- s.pickOneAndLogTheRest(errors) + + close(errs) } -func errorCollator(results <-chan error, errs chan<- error) { - errMsgs := []string{} - for err := range results { - if err != nil { - errMsgs = append(errMsgs, err.Error()) +func (s *Site) pickOneAndLogTheRest(errors []error) error { + if len(errors) == 0 { + return nil + } + + var i int + + for j, err := range errors { + // If this is in server mode, we want to return an error to the client + // with a file context, if possible. + if herrors.UnwrapErrorWithFileContext(err) != nil { + i = j + break } } - if len(errMsgs) == 0 { - errs <- nil - } else { - errs <- errors.New(strings.Join(errMsgs, "\n")) + + // Log the rest, but add a threshold to avoid flooding the log. + const errLogThreshold = 5 + + for j, err := range errors { + if j == i { + continue + } + + if j >= errLogThreshold { + break + } + + s.Log.ERROR.Println(err) } - close(errs) + + return errors[i] } func (s *Site) appendThemeTemplates(in []string) []string { @@ -1650,8 +1676,7 @@ func (s *Site) renderAndWriteXML(statCounter *uint64, name string, targetPath st renderBuffer.WriteString("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n") if err := s.renderForLayouts(name, d, renderBuffer, layouts...); err != nil { - helpers.DistinctWarnLog.Println(err) - return nil + return err } var path string @@ -1684,8 +1709,8 @@ func (s *Site) renderAndWritePage(statCounter *uint64, name string, targetPath s defer bp.PutBuffer(renderBuffer) if err := s.renderForLayouts(p.Kind, p, renderBuffer, layouts...); err != nil { - helpers.DistinctWarnLog.Println(err) - return nil + + return err } if renderBuffer.Len() == 0 { @@ -1735,46 +1760,18 @@ func (s *Site) renderAndWritePage(statCounter *uint64, name string, targetPath s func (s *Site) renderForLayouts(name string, d interface{}, w io.Writer, layouts ...string) (err error) { var templ tpl.Template - defer func() { - if r := recover(); r != nil { - templName := "" - if templ != nil { - templName = templ.Name() - } - s.DistinctErrorLog.Printf("Failed to render %q: %s", templName, r) - s.DistinctErrorLog.Printf("Stack Trace:\n%s", stackTrace(1200)) - - // TOD(bep) we really need to fix this. Also see below. - if !s.running() && !testMode { - os.Exit(-1) - } - } - }() - templ = s.findFirstTemplate(layouts...) if templ == nil { - return fmt.Errorf("[%s] Unable to locate layout for %q: %s\n", s.Language.Lang, name, layouts) + s.Log.WARN.Printf("[%s] Unable to locate layout for %q: %s\n", s.Language.Lang, name, layouts) + return nil } if err = templ.Execute(w, d); err != nil { - // Behavior here should be dependent on if running in server or watch mode. if p, ok := d.(*PageOutput); ok { - if p.File != nil { - s.DistinctErrorLog.Printf("Error while rendering %q in %q: %s", name, p.File.Dir(), err) - } else { - s.DistinctErrorLog.Printf("Error while rendering %q: %s", name, err) - } - } else { - s.DistinctErrorLog.Printf("Error while rendering %q: %s", name, err) - } - if !s.running() && !testMode { - // TODO(bep) check if this can be propagated - os.Exit(-1) - } else if testMode { - return + return p.errorf(err, "render of %q failed", name) } + return _errors.Wrapf(err, "render of %q failed", name) } - return } |