mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-25 22:00:58 +02:00
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
This commit is contained in:
109
hugolib/site.go
109
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 nil
|
||||
return s.pickOneAndLogTheRest(errors)
|
||||
}
|
||||
|
||||
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) errorCollator(results <-chan error, errs chan<- error) {
|
||||
var errors []error
|
||||
for e := range results {
|
||||
errors = append(errors, e)
|
||||
}
|
||||
|
||||
errs <- s.pickOneAndLogTheRest(errors)
|
||||
|
||||
close(errs)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user