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 | |
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
73 files changed, 1886 insertions, 640 deletions
diff --git a/commands/commandeer.go b/commands/commandeer.go index c55806980..2b76462fe 100644 --- a/commands/commandeer.go +++ b/commands/commandeer.go @@ -14,6 +14,15 @@ package commands import ( + "bytes" + "errors" + + "github.com/gohugoio/hugo/common/herrors" + + "io/ioutil" + + jww "github.com/spf13/jwalterweatherman" + "os" "path/filepath" "regexp" @@ -21,13 +30,13 @@ import ( "sync" "time" + "github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/config" "github.com/spf13/cobra" - "github.com/spf13/afero" - "github.com/gohugoio/hugo/hugolib" + "github.com/spf13/afero" "github.com/bep/debounce" "github.com/gohugoio/hugo/common/types" @@ -46,6 +55,8 @@ type commandeerHugoState struct { type commandeer struct { *commandeerHugoState + logger *loggers.Logger + // Currently only set when in "fast render mode". But it seems to // be fast enough that we could maybe just add it for all server modes. changeDetector *fileChangeDetector @@ -69,9 +80,45 @@ type commandeer struct { serverPorts []int languagesConfigured bool languages langs.Languages + doLiveReload bool + fastRenderMode bool + showErrorInBrowser bool configured bool paused bool + + // Any error from the last build. + buildErr error +} + +func (c *commandeer) errCount() int { + return int(c.logger.ErrorCounter.Count()) +} + +func (c *commandeer) getErrorWithContext() interface{} { + errCount := c.errCount() + + if errCount == 0 { + return nil + } + + m := make(map[string]interface{}) + + m["Error"] = errors.New(removeErrorPrefixFromLog(c.logger.Errors.String())) + m["Version"] = hugoVersionString() + + fe := herrors.UnwrapErrorWithFileContext(c.buildErr) + if fe != nil { + m["File"] = fe + } + + if c.h.verbose { + var b bytes.Buffer + herrors.FprintStackTrace(&b, c.buildErr) + m["StackTrace"] = b.String() + } + + return m } func (c *commandeer) Set(key string, value interface{}) { @@ -105,6 +152,8 @@ func newCommandeer(mustHaveConfigFile, running bool, h *hugoBuilderCommon, f fla doWithCommandeer: doWithCommandeer, visitedURLs: types.NewEvictingStringQueue(10), debounce: rebuildDebouncer, + // This will be replaced later, but we need something to log to before the configuration is read. + logger: loggers.NewLogger(jww.LevelError, jww.LevelError, os.Stdout, ioutil.Discard, running), } return c, c.loadConfig(mustHaveConfigFile, running) @@ -236,6 +285,11 @@ func (c *commandeer) loadConfig(mustHaveConfigFile, running bool) error { c.languages = l } + // Set some commonly used flags + c.doLiveReload = !c.h.buildWatch && !c.Cfg.GetBool("disableLiveReload") + c.fastRenderMode = c.doLiveReload && !c.Cfg.GetBool("disableFastRender") + c.showErrorInBrowser = c.doLiveReload && !c.Cfg.GetBool("disableBrowserError") + // This is potentially double work, but we need to do this one more time now // that all the languages have been configured. if c.doWithCommandeer != nil { @@ -244,12 +298,13 @@ func (c *commandeer) loadConfig(mustHaveConfigFile, running bool) error { } } - logger, err := c.createLogger(config) + logger, err := c.createLogger(config, running) if err != nil { return err } cfg.Logger = logger + c.logger = logger createMemFs := config.GetBool("renderToMemory") diff --git a/commands/commands.go b/commands/commands.go index 54eb03b5b..8670d4983 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -14,12 +14,10 @@ package commands import ( - "os" - + "github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/helpers" "github.com/spf13/cobra" - jww "github.com/spf13/jwalterweatherman" "github.com/spf13/nitro" ) @@ -242,7 +240,7 @@ func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) { _ = cmd.Flags().SetAnnotation("theme", cobra.BashCompSubdirsInDir, []string{"themes"}) } -func checkErr(logger *jww.Notepad, err error, s ...string) { +func checkErr(logger *loggers.Logger, err error, s ...string) { if err == nil { return } @@ -255,25 +253,3 @@ func checkErr(logger *jww.Notepad, err error, s ...string) { } logger.ERROR.Println(err) } - -func stopOnErr(logger *jww.Notepad, err error, s ...string) { - if err == nil { - return - } - - defer os.Exit(-1) - - if len(s) == 0 { - newMessage := err.Error() - // Printing an empty string results in a error with - // no message, no bueno. - if newMessage != "" { - logger.CRITICAL.Println(newMessage) - } - } - for _, message := range s { - if message != "" { - logger.CRITICAL.Println(message) - } - } -} diff --git a/commands/convert.go b/commands/convert.go index 8de155e9b..dc6b8fe15 100644 --- a/commands/convert.go +++ b/commands/convert.go @@ -14,10 +14,10 @@ package commands import ( - "fmt" "time" src "github.com/gohugoio/hugo/source" + "github.com/pkg/errors" "github.com/gohugoio/hugo/hugolib" @@ -187,7 +187,7 @@ func (cc *convertCmd) convertAndSavePage(p *hugolib.Page, site *hugolib.Site, ma } if err = newPage.SaveSourceAs(newFilename); err != nil { - return fmt.Errorf("Failed to save file %q: %s", newFilename, err) + return errors.Wrapf(err, "Failed to save file %q:", newFilename) } return nil diff --git a/commands/hugo.go b/commands/hugo.go index 2e7353d51..6cb2ec012 100644 --- a/commands/hugo.go +++ b/commands/hugo.go @@ -18,16 +18,22 @@ package commands import ( "fmt" "io/ioutil" + "os/signal" "sort" "sync/atomic" + + "github.com/pkg/errors" + + "github.com/gohugoio/hugo/common/herrors" + "github.com/gohugoio/hugo/common/loggers" + "syscall" "github.com/gohugoio/hugo/hugolib/filesystems" "golang.org/x/sync/errgroup" - "log" "os" "path/filepath" "runtime" @@ -85,7 +91,7 @@ func Execute(args []string) Response { } if err == nil { - errCount := int(jww.LogCountForLevelsGreaterThanorEqualTo(jww.LevelError)) + errCount := int(loggers.GlobalErrorCounter.Count()) if errCount > 0 { err = fmt.Errorf("logged %d errors", errCount) } else if resp.Result != nil { @@ -118,7 +124,7 @@ func initializeConfig(mustHaveConfigFile, running bool, } -func (c *commandeer) createLogger(cfg config.Provider) (*jww.Notepad, error) { +func (c *commandeer) createLogger(cfg config.Provider, running bool) (*loggers.Logger, error) { var ( logHandle = ioutil.Discard logThreshold = jww.LevelWarn @@ -161,7 +167,7 @@ func (c *commandeer) createLogger(cfg config.Provider) (*jww.Notepad, error) { jww.SetStdoutThreshold(stdoutThreshold) helpers.InitLoggers() - return jww.NewNotepad(stdoutThreshold, logThreshold, outHandle, logHandle, "", log.Ldate|log.Ltime), nil + return loggers.NewLogger(stdoutThreshold, logThreshold, outHandle, logHandle, running), nil } func initializeFlags(cmd *cobra.Command, cfg config.Provider) { @@ -275,9 +281,9 @@ func (c *commandeer) fullBuild() error { cnt, err := c.copyStatic() if err != nil { if !os.IsNotExist(err) { - return fmt.Errorf("Error copying static files: %s", err) + return errors.Wrap(err, "Error copying static files") } - c.Logger.WARN.Println("No Static directory found") + c.logger.WARN.Println("No Static directory found") } langCount = cnt langCount = cnt @@ -285,7 +291,7 @@ func (c *commandeer) fullBuild() error { } buildSitesFunc := func() error { if err := c.buildSites(); err != nil { - return fmt.Errorf("Error building site: %s", err) + return errors.Wrap(err, "Error building site") } return nil } @@ -345,8 +351,8 @@ func (c *commandeer) build() error { if err != nil { return err } - c.Logger.FEEDBACK.Println("Watching for changes in", c.hugo.PathSpec.AbsPathify(c.Cfg.GetString("contentDir"))) - c.Logger.FEEDBACK.Println("Press Ctrl+C to stop") + c.logger.FEEDBACK.Println("Watching for changes in", c.hugo.PathSpec.AbsPathify(c.Cfg.GetString("contentDir"))) + c.logger.FEEDBACK.Println("Press Ctrl+C to stop") watcher, err := c.newWatcher(watchDirs...) checkErr(c.Logger, err) defer watcher.Close() @@ -388,7 +394,7 @@ func (c *commandeer) doWithPublishDirs(f func(sourceFs *filesystems.SourceFilesy staticFilesystems := c.hugo.BaseFs.SourceFilesystems.Static if len(staticFilesystems) == 0 { - c.Logger.WARN.Println("No static directories found to sync") + c.logger.WARN.Println("No static directories found to sync") return langCount, nil } @@ -448,13 +454,13 @@ func (c *commandeer) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint6 syncer.Delete = c.Cfg.GetBool("cleanDestinationDir") if syncer.Delete { - c.Logger.INFO.Println("removing all files from destination that don't exist in static dirs") + c.logger.INFO.Println("removing all files from destination that don't exist in static dirs") syncer.DeleteFilter = func(f os.FileInfo) bool { return f.IsDir() && strings.HasPrefix(f.Name(), ".") } } - c.Logger.INFO.Println("syncing static files to", publishDir) + c.logger.INFO.Println("syncing static files to", publishDir) var err error @@ -480,7 +486,7 @@ func (c *commandeer) timeTrack(start time.Time, name string) { return } elapsed := time.Since(start) - c.Logger.FEEDBACK.Printf("%s in %v ms", name, int(1000*elapsed.Seconds())) + c.logger.FEEDBACK.Printf("%s in %v ms", name, int(1000*elapsed.Seconds())) } // getDirList provides NewWatcher |