summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2018-10-03 14:58:09 +0200
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2018-10-16 22:10:56 +0200
commit35fbfb19a173b01bc881f2bbc5d104136633a7ec (patch)
tree636d0d51fa262dc808eb3c5cc9cf92ad977a0c6a
parent3a3089121b852332b5744d1f566959c8cf93cef4 (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
-rw-r--r--commands/commandeer.go61
-rw-r--r--commands/commands.go28
-rw-r--r--commands/convert.go4
-rw-r--r--commands/hugo.go470
-rw-r--r--commands/new_site.go5
-rw-r--r--commands/server.go73
-rw-r--r--commands/server_errors.go95
-rw-r--r--commands/server_test.go13
-rw-r--r--commands/static_syncer.go6
-rw-r--r--commands/version.go11
-rw-r--r--common/herrors/error_locator.go194
-rw-r--r--common/herrors/error_locator_test.go112
-rw-r--r--common/herrors/errors.go (renamed from common/errors/errors.go)32
-rw-r--r--common/herrors/file_error.go111
-rw-r--r--common/herrors/file_error_test.go56
-rw-r--r--common/herrors/line_number_extractors.go59
-rw-r--r--common/loggers/loggers.go75
-rw-r--r--create/content.go8
-rw-r--r--create/content_template_handler.go6
-rw-r--r--deps/deps.go9
-rw-r--r--go.mod5
-rw-r--r--go.sum6
-rw-r--r--helpers/path.go5
-rw-r--r--hugolib/alias.go8
-rw-r--r--hugolib/config.go7
-rw-r--r--hugolib/datafiles_test.go2
-rw-r--r--hugolib/fileInfo.go3
-rw-r--r--hugolib/hugo_sites.go11
-rw-r--r--hugolib/hugo_sites_build.go4
-rw-r--r--hugolib/hugo_sites_build_errors_test.go182
-rw-r--r--hugolib/hugo_sites_build_failures_test.go42
-rw-r--r--hugolib/page.go45
-rw-r--r--hugolib/page_bundler.go4
-rw-r--r--hugolib/page_bundler_capture.go13
-rw-r--r--hugolib/page_bundler_capture_test.go5
-rw-r--r--hugolib/page_bundler_test.go2
-rw-r--r--hugolib/page_test.go17
-rw-r--r--hugolib/pagemeta/page_frontmatter.go11
-rw-r--r--hugolib/paths/paths.go11
-rw-r--r--hugolib/shortcode.go45
-rw-r--r--hugolib/shortcode_test.go18
-rw-r--r--hugolib/site.go105
-rw-r--r--hugolib/site_render.go7
-rw-r--r--hugolib/site_test.go2
-rw-r--r--hugolib/testhelpers_test.go41
-rw-r--r--i18n/i18n.go6
-rw-r--r--i18n/i18n_test.go9
-rw-r--r--i18n/translationProvider.go9
-rw-r--r--releaser/releaser.go5
-rw-r--r--resource/image.go6
-rw-r--r--resource/postcss/postcss.go11
-rw-r--r--resource/resource.go12
-rw-r--r--resource/resource_metadata.go3
-rw-r--r--resource/templates/execute_as_template.go5
-rw-r--r--resource/tocss/scss/tocss.go3
-rw-r--r--resource/tocss/scss/tocss_notavailable.go4
-rw-r--r--resource/transform.go4
-rw-r--r--tpl/collections/collections_test.go7
-rw-r--r--tpl/data/data.go6
-rw-r--r--tpl/data/data_test.go10
-rw-r--r--tpl/fmt/fmt.go5
-rw-r--r--tpl/fmt/init.go2
-rw-r--r--tpl/fmt/init_test.go3
-rw-r--r--tpl/partials/init_test.go2
-rw-r--r--tpl/resources/resources.go4
-rw-r--r--tpl/strings/strings.go8
-rw-r--r--tpl/template.go127
-rw-r--r--tpl/template_test.go31
-rw-r--r--tpl/tplimpl/template.go147
-rw-r--r--tpl/tplimpl/templateProvider.go7
-rw-r--r--tpl/tplimpl/template_errors.go46
-rw-r--r--tpl/tplimpl/template_funcs_test.go8
-rw-r--r--tpl/urls/urls.go7
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() with a list of directories to watch for changes.
@@ -498,7 +504,7 @@ func (c *commandeer) getDirList() ([]string, error) {
return nil
}
- c.Logger.ERROR.Println("Walker: ", err)
+ c.logger.ERROR.Println("Walker: ", err)
return nil
}
@@ -511,16 +517,16 @@ func (c *commandeer) getDirList() ([]string, error) {
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
link, err := filepath.EvalSymlinks(path)
if err != nil {
- c.Logger.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", path, err)
+ c.logger.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", path, err)
return nil
}
linkfi, err := helpers.LstatIfPossible(c.Fs.Source, link)
if err != nil {
- c.Logger.ERROR.Printf("Cannot stat %q: %s", link, err)
+ c.logger.ERROR.Printf("Cannot stat %q: %s", link, err)
return nil
}
if !allowSymbolicDirs && !linkfi.Mode().IsRegular() {
- c.Logger.ERROR.Printf("Symbolic links for directories not supported, skipping %q", path)
+ c.logger.ERROR.Printf("Symbolic links for directories not supported, skipping %q", path)
return nil
}
@@ -603,7 +609,7 @@ func (c *commandeer) getDirList() ([]string, error) {
func (c *commandeer) resetAndBuildSites() (err error) {
if !c.h.quiet {
- c.Logger.FEEDBACK.Println("Started bui