diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2018-10-03 15:58:09 +0300 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2018-10-16 23:10:56 +0300 |
commit | 35fbfb19a173b01bc881f2bbc5d104136633a7ec (patch) | |
tree | 636d0d51fa262dc808eb3c5cc9cf92ad977a0c6a /commands/server.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 'commands/server.go')
-rw-r--r-- | commands/server.go | 73 |
1 files changed, 54 insertions, 19 deletions
diff --git a/commands/server.go b/commands/server.go index 27999fa6c..ffdbc95c9 100644 --- a/commands/server.go +++ b/commands/server.go @@ -14,6 +14,7 @@ package commands import ( + "bytes" "fmt" "net" "net/http" @@ -21,6 +22,7 @@ import ( "os" "os/signal" "path/filepath" + "regexp" "runtime" "strconv" "strings" @@ -28,7 +30,10 @@ import ( "syscall" "time" + "github.com/pkg/errors" + "github.com/gohugoio/hugo/livereload" + "github.com/gohugoio/hugo/tpl" "github.com/gohugoio/hugo/config" @@ -52,7 +57,8 @@ type serverCmd struct { serverWatch bool noHTTPCache bool - disableFastRender bool + disableFastRender bool + disableBrowserError bool *baseBuilderCmd } @@ -93,6 +99,7 @@ of a second, you will be able to save and see your changes nearly instantly.`, cc.cmd.Flags().BoolVar(&cc.navigateToChanged, "navigateToChanged", false, "navigate to changed content file on live browser reload") cc.cmd.Flags().BoolVar(&cc.renderToDisk, "renderToDisk", false, "render to Destination path (default is render to memory & serve from there)") cc.cmd.Flags().BoolVar(&cc.disableFastRender, "disableFastRender", false, "enables full re-renders on changes") + cc.cmd.Flags().BoolVar(&cc.disableBrowserError, "disableBrowserError", false, "do not show build errors in the browser") cc.cmd.Flags().String("memstats", "", "log memory usage to this file") cc.cmd.Flags().String("meminterval", "100ms", "interval to poll memory usage (requires --memstats), valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".") @@ -142,6 +149,9 @@ func (sc *serverCmd) server(cmd *cobra.Command, args []string) error { if cmd.Flags().Changed("disableFastRender") { c.Set("disableFastRender", sc.disableFastRender) } + if cmd.Flags().Changed("disableBrowserError") { + c.Set("disableBrowserError", sc.disableBrowserError) + } if sc.serverWatch { c.Set("watch", true) } @@ -176,7 +186,7 @@ func (sc *serverCmd) server(cmd *cobra.Command, args []string) error { // port set explicitly by user -- he/she probably meant it! err = newSystemErrorF("Server startup failed: %s", err) } - jww.ERROR.Println("port", sc.serverPort, "already in use, attempting to use an available port") + c.logger.FEEDBACK.Println("port", sc.serverPort, "already in use, attempting to use an available port") sp, err := helpers.FindAvailablePort() if err != nil { err = newSystemError("Unable to find alternative port to use:", err) @@ -223,7 +233,7 @@ func (sc *serverCmd) server(cmd *cobra.Command, args []string) error { } if err := memStats(); err != nil { - jww.ERROR.Println("memstats error:", err) + jww.WARN.Println("memstats error:", err) } c, err := initializeConfig(true, true, &sc.hugoBuilderCommon, sc, cfgInit) @@ -271,10 +281,11 @@ func (sc *serverCmd) server(cmd *cobra.Command, args []string) error { } type fileServer struct { - baseURLs []string - roots []string - c *commandeer - s *serverCmd + baseURLs []string + roots []string + errorTemplate tpl.Template + c *commandeer + s *serverCmd } func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, error) { @@ -301,27 +312,40 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro httpFs := afero.NewHttpFs(f.c.destinationFs) fs := filesOnlyFs{httpFs.Dir(absPublishDir)} - doLiveReload := !f.s.buildWatch && !f.c.Cfg.GetBool("disableLiveReload") - fastRenderMode := doLiveReload && !f.c.Cfg.GetBool("disableFastRender") - - if i == 0 && fastRenderMode { + if i == 0 && f.c.fastRenderMode { jww.FEEDBACK.Println("Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender") } // We're only interested in the path u, err := url.Parse(baseURL) if err != nil { - return nil, "", "", fmt.Errorf("Invalid baseURL: %s", err) + return nil, "", "", errors.Wrap(err, "Invalid baseURL") } decorate := func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if f.c.showErrorInBrowser { + // First check the error state + err := f.c.getErrorWithContext() + if err != nil { + w.WriteHeader(500) + var b bytes.Buffer + err := f.errorTemplate.Execute(&b, err) + if err != nil { + f.c.logger.ERROR.Println(err) + } + fmt.Fprint(w, injectLiveReloadScript(&b, f.c.Cfg.GetInt("liveReloadPort"))) + + return + } + } + if f.s.noHTTPCache { w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0") w.Header().Set("Pragma", "no-cache") } - if fastRenderMode { + if f.c.fastRenderMode { p := r.RequestURI if strings.HasSuffix(p, "/") || strings.HasSuffix(p, "html") || strings.HasSuffix(p, "htm") { f.c.visitedURLs.Add(p) @@ -345,6 +369,11 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro return mu, u.String(), endpoint, nil } +var logErrorRe = regexp.MustCompile("(?s)ERROR \\d{4}/\\d{2}/\\d{2} \\d{2}:\\d{2}:\\d{2} ") + +func removeErrorPrefixFromLog(content string) string { + return logErrorRe.ReplaceAllLiteralString(content, "") +} func (c *commandeer) serve(s *serverCmd) error { isMultiHost := c.hugo.IsMultihost() @@ -365,11 +394,17 @@ func (c *commandeer) serve(s *serverCmd) error { roots = []string{""} } + templ, err := c.hugo.TextTmpl.Parse("__default_server_error", buildErrorTemplate) + if err != nil { + return err + } + srv := &fileServer{ - baseURLs: baseURLs, - roots: roots, - c: c, - s: s, + baseURLs: baseURLs, + roots: roots, + c: c, + s: s, + errorTemplate: templ, } doLiveReload := !c.Cfg.GetBool("disableLiveReload") @@ -392,7 +427,7 @@ func (c *commandeer) serve(s *serverCmd) error { go func() { err = http.ListenAndServe(endpoint, mu) if err != nil { - jww.ERROR.Printf("Error: %s\n", err.Error()) + c.logger.ERROR.Printf("Error: %s\n", err.Error()) os.Exit(1) } }() @@ -453,7 +488,7 @@ func (sc *serverCmd) fixURL(cfg config.Provider, s string, port int) (string, er if strings.Contains(u.Host, ":") { u.Host, _, err = net.SplitHostPort(u.Host) if err != nil { - return "", fmt.Errorf("Failed to split baseURL hostpost: %s", err) + return "", errors.Wrap(err, "Failed to split baseURL hostpost") } } u.Host += fmt.Sprintf(":%d", port) |