aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--cache/filecache/integration_test.go5
-rw-r--r--commands/commandeer.go58
-rw-r--r--commands/deploy.go2
-rw-r--r--commands/hugobuilder.go27
-rw-r--r--commands/import.go11
-rw-r--r--commands/server.go19
-rw-r--r--commands/xcommand_template.go79
-rw-r--r--common/loggers/handlerdefault.go106
-rw-r--r--common/loggers/handlersmisc.go158
-rw-r--r--common/loggers/handlerterminal.go90
-rw-r--r--common/loggers/ignorableLogger.go63
-rw-r--r--common/loggers/logger.go303
-rw-r--r--common/loggers/logger_test.go156
-rw-r--r--common/loggers/loggerglobal.go53
-rw-r--r--common/loggers/loggers.go355
-rw-r--r--common/loggers/loggers_test.go60
-rw-r--r--config/allconfig/allconfig.go3
-rw-r--r--config/allconfig/load.go5
-rw-r--r--config/commonConfig.go8
-rw-r--r--config/commonConfig_test.go4
-rw-r--r--deploy/deploy.go81
-rw-r--r--deploy/deploy_test.go13
-rw-r--r--deps/deps.go32
-rw-r--r--go.mod6
-rw-r--r--go.sum12
-rw-r--r--helpers/general.go149
-rw-r--r--helpers/general_test.go56
-rw-r--r--helpers/testhelpers_test.go4
-rw-r--r--hugofs/nosymlink_fs.go1
-rw-r--r--hugofs/nosymlink_test.go8
-rw-r--r--hugofs/walk.go2
-rw-r--r--hugolib/alias.go1
-rw-r--r--hugolib/alias_test.go5
-rw-r--r--hugolib/config_test.go3
-rw-r--r--hugolib/filesystems/basefs.go4
-rw-r--r--hugolib/hugo_modules_test.go10
-rw-r--r--hugolib/hugo_sites.go6
-rw-r--r--hugolib/hugo_sites_build.go34
-rw-r--r--hugolib/hugo_sites_build_test.go2
-rw-r--r--hugolib/integrationtest_builder.go12
-rw-r--r--hugolib/mount_filters_test.go3
-rw-r--r--hugolib/page__new.go2
-rw-r--r--hugolib/page_test.go8
-rw-r--r--hugolib/pagebundler_test.go10
-rw-r--r--hugolib/pages_capture.go2
-rw-r--r--hugolib/pages_capture_test.go2
-rw-r--r--hugolib/renderstring_test.go5
-rw-r--r--hugolib/resource_chain_test.go2
-rw-r--r--hugolib/site.go23
-rw-r--r--hugolib/site_new.go37
-rw-r--r--hugolib/site_render.go2
-rw-r--r--hugolib/testhelpers_test.go4
-rw-r--r--langs/i18n/i18n.go5
-rw-r--r--langs/i18n/i18n_test.go7
-rw-r--r--markup/asciidocext/convert_test.go18
-rw-r--r--markup/goldmark/convert_test.go4
-rw-r--r--markup/goldmark/toc_test.go9
-rw-r--r--markup/org/convert.go3
-rw-r--r--markup/org/convert_test.go2
-rw-r--r--markup/pandoc/convert_test.go2
-rw-r--r--markup/rst/convert_test.go2
-rw-r--r--modules/client.go5
-rw-r--r--parser/metadecoders/decoder.go6
-rw-r--r--resources/page/pagemeta/page_frontmatter.go4
-rw-r--r--resources/page/testhelpers_page_test.go2
-rw-r--r--resources/resource_spec.go4
-rw-r--r--resources/resource_transformers/babel/babel.go6
-rw-r--r--resources/resource_transformers/babel/integration_test.go5
-rw-r--r--resources/resource_transformers/postcss/integration_test.go17
-rw-r--r--resources/resource_transformers/postcss/postcss.go11
-rw-r--r--resources/resource_transformers/postcss/postcss_test.go6
-rw-r--r--resources/resource_transformers/tocss/dartsass/client.go10
-rw-r--r--resources/resource_transformers/tocss/dartsass/integration_test.go34
-rw-r--r--tpl/collections/collections.go3
-rw-r--r--tpl/data/data.go5
-rw-r--r--tpl/data/data_test.go11
-rw-r--r--tpl/data/resources_test.go6
-rw-r--r--tpl/fmt/fmt.go51
-rw-r--r--transform/livereloadinject/livereloadinject.go5
-rw-r--r--transform/metainject/hugogenerator.go6
80 files changed, 1273 insertions, 1082 deletions
diff --git a/cache/filecache/integration_test.go b/cache/filecache/integration_test.go
index a59ea048d..a8a45988e 100644
--- a/cache/filecache/integration_test.go
+++ b/cache/filecache/integration_test.go
@@ -16,11 +16,10 @@ package filecache_test
import (
"path/filepath"
- jww "github.com/spf13/jwalterweatherman"
-
"testing"
"time"
+ "github.com/bep/logg"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/hugolib"
@@ -80,7 +79,7 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA
`
b := hugolib.NewIntegrationTestBuilder(
- hugolib.IntegrationTestConfig{T: t, TxtarString: files, Running: true, RunGC: true, NeedsOsFS: true, LogLevel: jww.LevelInfo},
+ hugolib.IntegrationTestConfig{T: t, TxtarString: files, Running: true, RunGC: true, NeedsOsFS: true, LogLevel: logg.LevelInfo},
).Build()
b.Assert(b.GCCount, qt.Equals, 0)
diff --git a/commands/commandeer.go b/commands/commandeer.go
index f7b711973..7322a210a 100644
--- a/commands/commandeer.go
+++ b/commands/commandeer.go
@@ -28,12 +28,11 @@ import (
"syscall"
"time"
- jww "github.com/spf13/jwalterweatherman"
-
"go.uber.org/automaxprocs/maxprocs"
"github.com/bep/clock"
"github.com/bep/lazycache"
+ "github.com/bep/logg"
"github.com/bep/overlayfs"
"github.com/bep/simplecobra"
@@ -114,7 +113,6 @@ type rootCommand struct {
baseURL string
gc bool
poll string
- panicOnWarning bool
forceSyncStatic bool
printPathWarnings bool
printUnusedTemplates bool
@@ -308,7 +306,7 @@ func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commo
func (r *rootCommand) HugFromConfig(conf *commonConfig) (*hugolib.HugoSites, error) {
h, _, err := r.hugoSites.GetOrCreate(r.configVersionID.Load(), func(key int32) (*hugolib.HugoSites, error) {
- depsCfg := deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, Logger: r.logger}
+ depsCfg := deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, LogOut: r.logger.Out(), LogLevel: r.logger.Level()}
return hugolib.NewHugoSites(depsCfg)
})
return h, err
@@ -320,7 +318,7 @@ func (r *rootCommand) Hugo(cfg config.Provider) (*hugolib.HugoSites, error) {
if err != nil {
return nil, err
}
- depsCfg := deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, Logger: r.logger}
+ depsCfg := deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, LogOut: r.logger.Out(), LogLevel: r.logger.Level()}
return hugolib.NewHugoSites(depsCfg)
})
return h, err
@@ -410,7 +408,6 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
return err
}
- loggers.PanicOnWarning.Store(r.panicOnWarning)
r.commonConfigs = lazycache.New[int32, *commonConfig](lazycache.Options{MaxEntries: 5})
r.hugoSites = lazycache.New[int32, *hugolib.HugoSites](lazycache.Options{MaxEntries: 5})
@@ -418,43 +415,48 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
}
func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) {
- var (
- outHandle = r.Out
- stdoutThreshold = jww.LevelWarn
- )
-
- if r.verbose {
- helpers.Deprecated("--verbose", "use --logLevel info", false)
- stdoutThreshold = jww.LevelInfo
- }
-
- if r.debug {
- helpers.Deprecated("--debug", "use --logLevel debug", false)
- stdoutThreshold = jww.LevelDebug
- }
+ level := logg.LevelWarn
if r.logLevel != "" {
switch strings.ToLower(r.logLevel) {
case "debug":
- stdoutThreshold = jww.LevelDebug
+ level = logg.LevelDebug
case "info":
- stdoutThreshold = jww.LevelInfo
+ level = logg.LevelInfo
case "warn", "warning":
- stdoutThreshold = jww.LevelWarn
+ level = logg.LevelWarn
case "error":
- stdoutThreshold = jww.LevelError
+ level = logg.LevelError
default:
return nil, fmt.Errorf("invalid log level: %q, must be one of debug, warn, info or error", r.logLevel)
}
+ } else {
+ if r.verbose {
+ helpers.Deprecated("--verbose", "use --logLevel info", false)
+ level = logg.LevelInfo
+ }
+
+ if r.debug {
+ helpers.Deprecated("--debug", "use --logLevel debug", false)
+ level = logg.LevelDebug
+ }
}
- loggers.InitGlobalLogger(stdoutThreshold, jww.LevelWarn, outHandle, io.Discard)
- helpers.InitLoggers()
- return loggers.NewLogger(stdoutThreshold, jww.LevelWarn, outHandle, io.Discard, running), nil
+ optsLogger := loggers.Options{
+ Distinct: true,
+ Level: level,
+ Stdout: r.Out,
+ Stderr: r.Out,
+ StoreErrors: running,
+ }
+
+ return loggers.New(optsLogger), nil
+
}
func (r *rootCommand) Reset() {
r.logger.Reset()
+ loggers.Log().Reset()
}
// IsTestRun reports whether the command is running as a test.
@@ -530,7 +532,7 @@ func applyLocalFlagsBuild(cmd *cobra.Command, r *rootCommand) {
cmd.Flags().StringP("layoutDir", "l", "", "filesystem path to layout directory")
cmd.Flags().BoolVar(&r.gc, "gc", false, "enable to run some cleanup tasks (remove unused cache files) after the build")
cmd.Flags().StringVar(&r.poll, "poll", "", "set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes")
- cmd.Flags().BoolVar(&r.panicOnWarning, "panicOnWarning", false, "panic on first WARNING log")
+ cmd.Flags().Bool("panicOnWarning", false, "panic on first WARNING log")
cmd.Flags().Bool("templateMetrics", false, "display metrics about template executions")
cmd.Flags().Bool("templateMetricsHints", false, "calculate some improvement hints when combined with --templateMetrics")
cmd.Flags().BoolVar(&r.forceSyncStatic, "forceSyncStatic", false, "copy all files when static is changed.")
diff --git a/commands/deploy.go b/commands/deploy.go
index 8dae4bd88..82127adf4 100644
--- a/commands/deploy.go
+++ b/commands/deploy.go
@@ -52,7 +52,7 @@ documentation.
if err != nil {
return err
}
- deployer, err := deploy.New(h.Configs.GetFirstLanguageConfig(), h.PathSpec.PublishFs)
+ deployer, err := deploy.New(h.Configs.GetFirstLanguageConfig(), h.Log, h.PathSpec.PublishFs)
if err != nil {
return err
}
diff --git a/commands/hugobuilder.go b/commands/hugobuilder.go
index 95dbb1ca8..60e558c70 100644
--- a/commands/hugobuilder.go
+++ b/commands/hugobuilder.go
@@ -26,11 +26,13 @@ import (
"sync"
"time"
+ "github.com/bep/logg"
"github.com/bep/simplecobra"
"github.com/fsnotify/fsnotify"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/hugo"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/common/terminal"
"github.com/gohugoio/hugo/common/types"
@@ -68,7 +70,6 @@ type hugoBuilder struct {
onConfigLoaded func(reloaded bool) error
fastRenderMode bool
- buildWatch bool
showErrorInBrowser bool
errState hugoBuilderErrState
@@ -131,7 +132,7 @@ func (e *hugoBuilderErrState) wasErr() bool {
}
func (c *hugoBuilder) errCount() int {
- return int(c.r.logger.LogCounters().ErrorCounter.Count())
+ return c.r.logger.LoggCount(logg.LevelError) + loggers.Log().LoggCount(logg.LevelError)
}
// getDirList provides NewWatcher() with a list of directories to watch for changes.
@@ -363,7 +364,7 @@ func (c *hugoBuilder) newWatcher(pollIntervalStr string, dirList ...string) (*wa
configFiles = conf.configs.LoadingInfo.ConfigFiles
})
- c.r.logger.Println("Watching for config changes in", strings.Join(configFiles, ", "))
+ c.r.Println("Watching for config changes in", strings.Join(configFiles, ", "))
for _, configFile := range configFiles {
watcher.Add(configFile)
configSet[configFile] = true
@@ -461,6 +462,7 @@ func (c *hugoBuilder) copyStatic() (map[string]uint64, error) {
}
func (c *hugoBuilder) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint64, error) {
+ infol := c.r.logger.InfoCommand("copy static")
publishDir := helpers.FilePathSeparator
if sourceFs.PublishFolder != "" {
@@ -484,13 +486,13 @@ func (c *hugoBuilder) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint
syncer.SrcFs = fs
if syncer.Delete {
- c.r.logger.Infoln("removing all files from destination that don't exist in static dirs")
+ infol.Logf("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.r.logger.Infoln("syncing static files to", publishDir)
+ infol.Logf("syncing static files to %s", publishDir)
// because we are using a baseFs (to get the union right).
// set sync src to root
@@ -545,14 +547,13 @@ func (c *hugoBuilder) fullBuild(noBuildLock bool) error {
langCount map[string]uint64
)
- if !c.r.quiet {
- fmt.Println("Start building sites … ")
- fmt.Println(hugo.BuildVersionString())
- if terminal.IsTerminal(os.Stdout) {
- defer func() {
- fmt.Print(showCursor + clearLine)
- }()
- }
+ c.r.logger.Println("Start building sites … ")
+ c.r.logger.Println(hugo.BuildVersionString())
+ c.r.logger.Println()
+ if terminal.IsTerminal(os.Stdout) {
+ defer func() {
+ fmt.Print(showCursor + clearLine)
+ }()
}
copyStaticFunc := func() error {
diff --git a/commands/import.go b/commands/import.go
index 30ada15f8..f2c56a9a1 100644
--- a/commands/import.go
+++ b/commands/import.go
@@ -19,12 +19,11 @@ import (
"errors"
"fmt"
"io"
+ "log"
"os"
"path/filepath"
"regexp"
- jww "github.com/spf13/jwalterweatherman"
-
"strconv"
"strings"
"time"
@@ -299,7 +298,7 @@ func (c *importCommand) convertJekyllMetaData(m any, postName string, postDate t
}
func (c *importCommand) convertJekyllPost(path, relPath, targetDir string, draft bool) error {
- jww.TRACE.Println("Converting", path)
+ log.Println("Converting", path)
filename := filepath.Base(path)
postDate, postName, err := c.parseJekyllFilename(filename)
@@ -308,7 +307,7 @@ func (c *importCommand) convertJekyllPost(path, relPath, targetDir string, draft
return nil
}
- jww.TRACE.Println(filename, postDate, postName)
+ log.Println(filename, postDate, postName)
targetFile := filepath.Join(targetDir, relPath)
targetParentDir := filepath.Dir(targetFile)
@@ -367,7 +366,7 @@ func (c *importCommand) copyJekyllFilesAndFolders(jekyllRoot, dest string, jekyl
if _, ok := jekyllPostDirs[entry.Name()]; !ok {
err = hugio.CopyDir(fs, sfp, dfp, nil)
if err != nil {
- jww.ERROR.Println(err)
+ c.r.logger.Errorln(err)
}
}
}
@@ -388,7 +387,7 @@ func (c *importCommand) copyJekyllFilesAndFolders(jekyllRoot, dest string, jekyl
if !isExcept && entry.Name()[0] != '.' && entry.Name()[0] != '_' {
err = hugio.CopyFile(fs, sfp, dfp)
if err != nil {
- jww.ERROR.Println(err)
+ c.r.logger.Errorln(err)
}
}
}
diff --git a/commands/server.go b/commands/server.go
index 80036618c..7e6ec5b13 100644
--- a/commands/server.go
+++ b/commands/server.go
@@ -67,7 +67,6 @@ import (
)
var (
- logErrorRe = regexp.MustCompile(`(?s)ERROR \d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} `)
logDuplicateTemplateExecuteRe = regexp.MustCompile(`: template: .*?:\d+:\d+: executing ".*?"`)
logDuplicateTemplateParseRe = regexp.MustCompile(`: template: .*?:\d+:\d*`)
)
@@ -106,9 +105,7 @@ func newServerCommand() *serverCommand {
// Flags.
var uninstall bool
- var c *serverCommand
-
- c = &serverCommand{
+ c := &serverCommand{
quit: make(chan bool),
commands: []simplecobra.Commander{
&simpleCommand{
@@ -654,8 +651,8 @@ func (c *serverCommand) getErrorWithContext() any {
m := make(map[string]any)
- //xwm["Error"] = errors.New(cleanErrorLog(removeErrorPrefixFromLog(c.r.logger.Errors())))
- m["Error"] = errors.New(cleanErrorLog(removeErrorPrefixFromLog(c.r.logger.Errors())))
+ m["Error"] = cleanErrorLog(c.r.logger.Errors())
+
m["Version"] = hugo.BuildVersionString()
ferrors := herrors.UnwrapFileErrorsWithErrorContext(c.errState.buildErr())
m["Files"] = ferrors
@@ -861,6 +858,9 @@ func (c *serverCommand) serve() error {
return err
}
+ // We need the server to share the same logger as the Hugo build (for error counts etc.)
+ c.r.logger = h.Log
+
if isMultiHost {
for _, l := range conf.configs.ConfigLangs() {
baseURLs = append(baseURLs, l.BaseURL().String())
@@ -1066,8 +1066,7 @@ func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
}
})
- // prevent spamming the log on changes
- logger := helpers.NewDistinctErrorLogger()
+ logger := s.c.r.logger
for _, ev := range staticEvents {
// Due to our approach of layering both directories and the content's rendered output
@@ -1206,10 +1205,6 @@ func pickOneWriteOrCreatePath(events []fsnotify.Event) string {
return name
}
-func removeErrorPrefixFromLog(content string) string {
- return logErrorRe.ReplaceAllLiteralString(content, "")
-}
-
func formatByteCount(b uint64) string {
const unit = 1000
if b < unit {
diff --git a/commands/xcommand_template.go b/commands/xcommand_template.go
deleted file mode 100644
index eeb9409a0..000000000
--- a/commands/xcommand_template.go
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package commands
-
-import (
- "context"
- "fmt"
-
- "github.com/bep/simplecobra"
- "github.com/spf13/cobra"
-)
-
-func newSimpleTemplateCommand() simplecobra.Commander {
- return &simpleCommand{
- name: "template",
- run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
-
- return nil
- },
- withc: func(cmd *cobra.Command, r *rootCommand) {
-
- },
- }
-
-}
-
-func newTemplateCommand() *templateCommand {
- return &templateCommand{
- commands: []simplecobra.Commander{},
- }
-
-}
-
-type templateCommand struct {
- r *rootCommand
-
- commands []simplecobra.Commander
-}
-
-func (c *templateCommand) Commands() []simplecobra.Commander {
- return c.commands
-}
-
-func (c *templateCommand) Name() string {
- return "template"
-}
-
-func (c *templateCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error {
- conf, err := c.r.ConfigFromProvider(c.r.configVersionID.Load(), flagsToCfg(cd, nil))
- if err != nil {
- return err
- }
- fmt.Println("templateCommand.Run", conf)
-
- return nil
-}
-
-func (c *templateCommand) Init(cd *simplecobra.Commandeer) error {
- cmd := cd.CobraCommand
- cmd.Short = "Print the site configuration"
- cmd.Long = `Print the site configuration, both default and custom settings.`
- return nil
-}
-
-func (c *templateCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
- c.r = cd.Root.Command.(*rootCommand)
- return nil
-}
diff --git a/common/loggers/handlerdefault.go b/common/loggers/handlerdefault.go
new file mode 100644
index 000000000..28b85ed22
--- /dev/null
+++ b/common/loggers/handlerdefault.go
@@ -0,0 +1,106 @@
+// Copyright 2023 The Hugo Authors. All rights reserved.
+// Some functions in this file (see comments) is based on the Go source code,
+// copyright The Go Authors and governed by a BSD-style license.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// package loggers contains some basic logging setup.
+package loggers
+
+import (
+ "fmt"
+ "io"
+ "strings"
+ "sync"
+
+ "github.com/bep/logg"
+
+ "github.com/fatih/color"
+)
+
+var bold = color.New(color.Bold)
+
+// levelColor mapping.
+var levelColor = [...]*color.Color{
+ logg.LevelDebug: color.New(color.FgWhite),
+ logg.LevelInfo: color.New(color.FgBlue),
+ logg.LevelWarn: color.New(color.FgYellow),
+ logg.LevelError: color.New(color.FgRed),
+}
+
+// levelString mapping.
+var levelString = [...]string{
+ logg.LevelDebug: "DEBUG",
+ logg.LevelInfo: "INFO ",
+ logg.LevelWarn: "WARN ",
+ logg.LevelError: "ERROR",
+}
+
+// newDefaultHandler handler.
+func newDefaultHandler(outWriter, errWriter io.Writer) logg.Handler {
+ return &defaultHandler{
+ outWriter: outWriter,
+ errWriter: errWriter,
+ Padding: 0,
+ }
+}
+
+// Default Handler implementation.
+// Based on https://github.com/apex/log/blob/master/handlers/cli/cli.go
+type defaultHandler struct {
+ mu sync.Mutex
+ outWriter io.Writer // Defaults to os.Stdout.
+ errWriter io.Writer // Defaults to os.Stderr.
+
+ Padding int
+}
+
+// HandleLog implements logg.Handler.
+func (h *defaultHandler) HandleLog(e *logg.Entry) error {
+ color := levelColor[e.Level]
+ level := levelString[e.Level]
+
+ h.mu.Lock()
+ defer h.mu.Unlock()
+
+ var w io.Writer
+ if e.Level > logg.LevelInfo {
+ w = h.errWriter
+ } else {
+ w = h.outWriter
+ }
+
+ var prefix string
+ for _, field := range e.Fields {
+ if field.Name == FieldNameCmd {
+ prefix = fmt.Sprint(field.Value)
+ break
+ }
+ }
+
+ if prefix != "" {
+ prefix = prefix + ": "
+ }
+
+ color.Fprintf(w, "%s %s%s", bold.Sprintf("%*s", h.Padding+1, level), color.Sprint(prefix), e.Message)
+
+ for _, field := range e.Fields {
+ if strings.HasPrefix(field.Name, reservedFieldNamePrefix) {
+ continue
+ }
+ fmt.Fprintf(w, " %s %v", color.Sprint(field.Name), field.Value)
+ }
+
+ fmt.Fprintln(w)
+
+ return nil
+}
diff --git a/common/loggers/handlersmisc.go b/common/loggers/handlersmisc.go
new file mode 100644
index 000000000..5c9d6c091
--- /dev/null
+++ b/common/loggers/handlersmisc.go
@@ -0,0 +1,158 @@
+// Copyright 2023 The Hugo Authors. All rights reserved.
+// Some functions in this file (see comments) is based on the Go source code,
+// copyright The Go Authors and governed by a BSD-style license.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package loggers
+
+import (
+ "fmt"
+ "strings"
+ "sync"
+
+ "github.com/bep/logg"
+ "github.com/gohugoio/hugo/identity"
+)
+
+// PanicOnWarningHook panics on warnings.
+var PanicOnWarningHook = func(e *logg.Entry) error {
+ if e.Level != logg.LevelWarn {
+ return nil
+ }
+ panic(e.Message)
+}
+
+func newLogLevelCounter() *logLevelCounter {
+ return &logLevelCounter{
+ counters: make(map[logg.Level]int),
+ }
+}
+
+func newLogOnceHandler(threshold logg.Level) *logOnceHandler {
+ return &logOnceHandler{
+ threshold: threshold,
+ seen: make(map[uint64]bool),
+ }
+}
+
+func newStopHandler(h ...logg.Handler) *stopHandler {
+ return &stopHandler{
+ handlers: h,
+ }
+}
+
+func newSuppressStatementsHandler(statements map[string]bool) *suppressStatementsHandler {
+ return &suppressStatementsHandler{
+ statements: statements,
+ }
+}
+
+type logLevelCounter struct {
+ mu sync.RWMutex
+ counters map[logg.Level]int
+}
+
+func (h *logLevelCounter) HandleLog(e *logg.Entry) error {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ h.counters[e.Level]++
+ return nil
+}
+
+var stopError = fmt.Errorf("stop")
+
+type logOnceHandler struct {
+ threshold logg.Level
+ mu sync.Mutex
+ seen map[uint64]bool
+}
+
+func (h *logOnceHandler) HandleLog(e *logg.Entry) error {
+ if e.Level < h.threshold {
+ // We typically only want to enable this for warnings and above.
+ // The common use case is that many go routines may log the same error.
+ return nil
+ }
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ hash := identity.HashUint64(e.Level, e.Message, e.Fields)
+ if h.seen[hash] {
+ return stopError
+ }
+ h.seen[hash] = true
+ return nil
+}
+
+func (h *logOnceHandler) reset() {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ h.seen = make(map[uint64]bool)
+}
+
+type stopHandler struct {
+ handlers []logg.Handler
+}
+
+// HandleLog implements logg.Handler.
+func (h *stopHandler) HandleLog(e *logg.Entry) error {
+ for _, handler := range h.handlers {
+ if err := handler.HandleLog(e); err != nil {
+ if err == stopError {
+ return nil
+ }
+ return err
+ }
+ }
+ return nil
+}
+
+type suppressStatementsHandler struct {
+ statements map[string]bool
+}
+
+func (h *suppressStatementsHandler) HandleLog(e *logg.Entry) error {
+ for _, field := range e.Fields {
+ if field.Name == FieldNameStatementID {
+ if h.statements[field.Value.(string)] {
+ return stopError
+ }
+ }
+ }
+ return nil
+}
+
+// replacer creates a new log handler that does string replacement in log messages.
+func replacer(repl *strings.Replacer) logg.Handler {
+ return logg.HandlerFunc(func(e *logg.Entry) error {
+ e.Message = repl.Replace(e.Message)
+ for i, field := range e.Fields {
+ if s, ok := field.Value.(string); ok {
+ e.Fields[i].Value = repl.Replace(s)
+ }
+ }
+ return nil
+ })
+}
+
+// whiteSpaceTrimmer creates a new log handler that trims whitespace from log messages and string fields.
+func whiteSpaceTrimmer() logg.Handler {
+ return logg.HandlerFunc(func(e *logg.Entry) error {
+ e.Message = strings.TrimSpace(e.Message)
+ for i, field := range e.Fields {
+ if s, ok := field.Value.(string); ok {
+ e.Fields[i].Value = strings.TrimSpace(s)
+ }
+ }
+ return nil
+ })
+}
diff --git a/common/loggers/handlerterminal.go b/common/loggers/handlerterminal.go
new file mode 100644
index 000000000..e3d377bbf
--- /dev/null
+++ b/common/loggers/handlerterminal.go
@@ -0,0 +1,90 @@
+// Copyright 2023 The Hugo Authors. All rights reserved.
+// Some functions in this file (see comments) is based on the Go source code,
+// copyright The Go Authors and governed by a BSD-style license.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package loggers
+
+import (
+ "fmt"
+ "io"
+ "strings"
+ "sync"
+
+ "github.com/bep/logg"
+)
+
+// newNoColoursHandler creates a new NoColoursHandler
+func newNoColoursHandler(outWriter, errWriter io.Writer, noLevelPrefix bool, predicate func(*logg.Entry) bool) *noColoursHandler {
+ if predicate == nil {
+ predicate = func(e *logg.Entry) bool { return true }
+ }
+ return &noColoursHandler{
+ noLevelPrefix: noLevelPrefix,
+ outWriter: outWriter,
+ errWriter: errWriter,
+ predicate: predicate,
+ }
+}
+
+type noColoursHandler struct {
+ mu sync.Mutex
+ outWriter io.Writer // Defaults to os.Stdout.
+ errWriter io.Writer // Defaults to os.Stderr.
+ predicate func(*logg.Entry) bool
+ noLevelPrefix bool
+}
+
+func (h *noColoursHandler) HandleLog(e *logg.Entry) error {
+ if !h.predicate(e) {
+ return nil
+ }
+ h.mu.Lock()
+ defer h.mu.Unlock()
+
+ var w io.Writer
+ if e.Level > logg.LevelInfo {
+ w = h.errWriter
+ } else {
+ w = h.outWriter
+ }
+
+ var prefix string
+ for _, field := range e.Fields {
+ if field.Name == FieldNameCmd {
+ prefix = fmt.Sprint(field.Value)
+ break
+ }
+ }
+
+ if prefix != "" {
+ prefix = prefix + ": "
+ }
+
+ if h.noLevelPrefix {
+ fmt.Fprintf(w, "%s%s", prefix, e.Message)
+ } else {
+ fmt.Fprintf(w, "%s %s%s", levelString[e.Level], prefix, e.Message)
+ }
+
+ for _, field := range e.Fields {
+ if strings.HasPrefix(field.Name, reservedFieldNamePrefix) {
+ continue
+ }
+ fmt.Fprintf(w, " %s %q", field.Name, field.Value)
+
+ }
+ fmt.Fprintln(w)
+
+ return nil
+}
diff --git a/common/loggers/ignorableLogger.go b/common/loggers/ignorableLogger.go
deleted file mode 100644
index c8aba560e..000000000
--- a/common/loggers/ignorableLogger.go
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2020 The Hugo Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package loggers
-
-import (
- "fmt"
-)
-
-// IgnorableLogger is a logger that ignores certain log statements.
-type IgnorableLogger interface {
- Logger
- Errorsf(statementID, format string, v ...any)
- Apply(logger Logger) IgnorableLogger
-}
-
-type ignorableLogger struct {
- Logger
- statements map[string]bool
-}
-
-// NewIgnorableLogger wraps the given logger and ignores the log statement IDs given.
-func NewIgnorableLogger(logger Logger, statements map[string]bool) IgnorableLogger {
- if statements == nil {
- statements = make(map[string]bool)
- }
- return ignorableLogger{
- Logger: logger,
- statements: statements,
- }
-}
-
-// Errorsf logs statementID as an ERROR if not configured as ignoreable.
-func (l ignorableLogger) Errorsf(statementID, format string, v ...any) {
- if l.statements[statementID] {
- // Ignore.
- return
- }
- ignoreMsg := fmt.Sprintf(`
-If you feel that this should not be logged as an ERROR, you can ignore it by adding this to your site config:
-ignoreErrors = [%q]`, statementID)
-
- format += ignoreMsg
-
- l.Errorf(format, v...)
-}
-
-func (l ignorableLogger) Apply(logger Logger) IgnorableLogger {
- return ignorableLogger{
- Logger: logger,
- statements: l.statements,
- }
-}
diff --git a/common/loggers/logger.go b/common/loggers/logger.go
new file mode 100644
index 000000000..85c75ef98
--- /dev/null
+++ b/common/loggers/logger.go
@@ -0,0 +1,303 @@
+// Copyright 2023 The Hugo Authors. All rights reserved.
+// Some functions in this file (see comments) is based on the Go source code,
+// copyright The Go Authors and governed by a BSD-style license.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package loggers
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "time"
+
+ "github.com/bep/logg"
+ "github.com/bep/logg/handlers/multi"
+ "github.com/gohugoio/hugo/common/terminal"
+)
+
+var (
+ reservedFieldNamePrefix = "__h_field_"
+ // FieldNameCmd is the name of the field that holds the command name.
+ FieldNameCmd = reservedFieldNamePrefix + "_cmd"
+ // Used to suppress statements.
+ FieldNameStatementID = reservedFieldNamePrefix + "__h_field_statement_id"
+)
+
+// Options defines options for the logger.
+type Options struct {
+ Level logg.Level
+ Stdout io.Writer
+ Stderr io.Writer
+ Distinct bool
+ StoreErrors bool
+ HandlerPost func(e *logg.Entry) error
+ SuppresssStatements map[string]bool
+}
+
+// New creates a new logger with the given options.
+func New(opts Options) Logger {
+ if opts.Stdout == nil {
+ opts.Stdout = os.Stdout
+ }
+ if opts.Stderr == nil {
+ opts.Stderr = os.Stdout
+ }
+ if opts.Level == 0 {
+ opts.Level = logg.LevelWarn
+ }
+
+ var logHandler logg.Handler
+ if terminal.PrintANSIColors(os.Stdout) {
+ logHandler = newDefaultHandler(opts.Stdout, opts.Stderr)
+ } else {
+ logHandler = newNoColoursHandler(opts.Stdout, opts.Stderr, false, nil)
+ }
+
+ errorsw := &strings.Builder{}
+ logCounters := newLogLevelCounter()
+ handlers := []logg.Handler{
+ whiteSpaceTrimmer(),
+ logHandler,
+ logCounters,
+ }
+
+ if opts.HandlerPost != nil {
+ var hookHandler logg.HandlerFunc = func(e *logg.Entry) error {
+ opts.HandlerPost(e)
+ return nil
+ }
+ handlers = append(handlers, hookHandler)
+ }
+
+ if opts.StoreErrors {
+ h := newNoColoursHandler(io.Discard, errorsw, true, func(e *logg.Entry) bool {
+ return e.Level >= logg.LevelError
+ })
+
+ handlers = append(handlers, h)
+ }
+
+ logHandler = multi.New(handlers...)
+
+ var logOnce *logOnceHandler
+ if opts.Distinct {
+ logOnce = newLogOnceHandler(logg.LevelWarn)
+ logHandler = newStopHandler(logOnce, logHandler)
+ }
+
+ if opts.SuppresssStatements != nil && len(opts.SuppresssStatements) > 0 {
+ logHandler = newStopHandler(newSuppressStatementsHandler(opts.SuppresssStatements), logHandler)
+ }
+
+ logger := logg.New(
+ logg.Options{
+ Level: opts.Level,
+ Handler: logHandler,
+ },
+ )
+
+ l := logger.WithLevel(opts.Level)
+
+ reset := func() {
+ logCounters.mu.Lock()
+ defer logCounters.mu.Unlock()
+ logCounters.counters = make(map[logg.Level]int)
+ errorsw.Reset()
+ if logOnce != nil {
+ logOnce.reset()
+ }
+ }
+
+ return &logAdapter{
+ logCounters: logCounters,
+ errors: errorsw,
+ reset: reset,
+ out: opts.Stdout,
+ level: opts.Level,
+ logger: logger,
+ debugl: l.WithLevel(logg.LevelDebug),
+ infol: l.WithLevel(logg.LevelInfo),
+ warnl: l.WithLevel(logg.LevelWarn),
+ errorl: l.WithLevel(logg.LevelError),
+ }
+}
+
+// NewDefault creates a new logger with the default options.
+func NewDefault() Logger {
+ opts := Options{
+ Distinct: true,
+ Level: logg.LevelWarn,
+ Stdout: os.Stdout,
+ Stderr: os.Stdout,
+ }
+ return New(opts)
+}
+
+func LevelLoggerToWriter(l logg.LevelLogger) io.Writer {
+ return logWriter{l: l}
+}
+
+type Logger interface {
+ Debugf(format string, v ...any)
+ Debugln(v ...any)
+ Error() logg.LevelLogger
+ Errorf(format string, v ...any)
+ Errorln(v ...any)
+ Errors() string
+ Errorsf(id, format string, v ...any)
+ Info() logg.LevelLogger
+ InfoCommand(command string) logg.LevelLogger
+ Infof(format string, v ...any)
+ Infoln(v ...any)
+ Level() logg.Level
+ LoggCount(logg.Level) int
+ Logger() logg.Logger
+ Out() io.Writer
+ Printf(format string, v ...any)
+ Println(v ...any)
+ PrintTimerIfDelayed(start time.Time, name string)
+ Reset()
+ Warn() logg.LevelLogger
+ WarnCommand(command string) logg.LevelLogger
+ Warnf(format string, v ...any)
+ Warnln(v ...any)
+}
+
+type logAdapter struct {
+ logCounters *logLevelCounter
+ errors *strings.Builder
+ reset func()
+ out io.Writer
+ level logg.Level
+ logger logg.Logger
+ debugl logg.LevelLogger
+ infol logg.LevelLogger
+ warnl logg.LevelLogger
+ errorl logg.LevelLogger
+}
+
+func (l *logAdapter) Debugf(format string, v ...any) {
+ l.debugl.Logf(format, v...)
+}
+
+func (l *logAdapter) Debugln(v ...any) {
+ l.debugl.Logf(l.sprint(v...))
+}
+
+func (l *logAdapter) Info() logg.LevelLogger {
+ return l.infol
+}
+
+func (l *logAdapter) InfoCommand(command string) logg.LevelLogger {
+ return l.infol.WithField(FieldNameCmd, command)
+}
+
+func (l *logAdapter) Infof(format string, v ...any) {
+ l.infol.Logf(format, v...)
+}
+
+func (l *logAdapter) Infoln(v ...any) {
+ l.infol.Logf(l.sprint(v...))
+}
+
+func (l *logAdapter) Level() logg.Level {
+ return l.level
+}
+
+func (l *logAdapter) LoggCount(level logg.Level) int {
+ l.logCounters.mu.RLock()
+ defer l.logCounters.mu.RUnlock()
+ return l.logCounters.counters[level]
+}
+
+func (l *logAdapter) Logger() logg.Logger {
+ return l.logger
+}
+
+func (l *logAdapter) Out() io.Writer {
+ return l.out
+}
+
+// PrintTimerIfDelayed prints a time statement to the FEEDBACK logger
+// if considerable time is spent.
+func (l *logAdapter) PrintTimerIfDelayed(start time.Time, name string) {
+ elapsed := time.Since(start)
+ milli := int(1000 * elapsed.Seconds())
+ if milli < 500 {
+ return
+ }
+ l.Printf("%s in %v ms", name, milli)
+}
+
+func (l *logAdapter) Printf(format string, v ...any) {
+ fmt.Fprintf(l.out, format, v...)
+}
+
+func (l *logAdapter) Println(v ...any) {
+ fmt.Fprintln(l.out, v...)
+}
+
+func (l *logAdapter) Reset() {
+ l.reset()
+}
+
+func (l *logAdapter) Warn() logg.LevelLogger {
+ return l.warnl
+}
+
+func (l *logAdapter) Warnf(format string, v ...any) {
+ l.warnl.Logf(format, v...)
+}
+
+func (l *logAdapter) WarnCommand(command string) logg.LevelLogger {
+ return l.warnl.WithField(FieldNameCmd, command)
+}
+
+func (l *logAdapter) Warnln(v ...any) {
+ l.warnl.Logf(l.sprint(v...))
+}
+
+func (l *logAdapter) Error() logg.LevelLogger {
+ return l.errorl
+}
+
+func (l *logAdapter) Errorf(format string, v ...any) {
+ l.errorl.Logf(format, v...)
+}
+
+func (l *logAdapter) Errorln(v ...any) {
+ l.errorl.Logf(l.sprint(v...))
+}
+
+func (l *logAdapter) Errors() string {
+ return l.errors.String()
+}
+
+func (l *logAdapter) Errorsf(id, format string, v ...any) {
+ l.errorl.WithField(FieldNameStatementID, id).Logf(format, v...)
+}
+
+func (l *logAdapter) sprint(v ...any) string {
+ return strings.TrimRight(fmt.Sprintln(v...), "\n")
+}
+
+type logWriter struct {
+ l logg.LevelLogger
+}
+
+func (w logWriter) Write(p []byte) (n int, err error) {
+ w.l.Logf("%s", p)
+ return len(p), nil
+}
diff --git a/common/loggers/logger_test.go b/common/loggers/logger_test.go
new file mode 100644
index 000000000..6aa540b0b
--- /dev/null
+++ b/common/loggers/logger_test.go
@@ -0,0 +1,156 @@
+// Copyright 2023 The Hugo Authors. All rights reserved.
+// Some functions in this file (see comments) is based on the Go source code,
+// copyright The Go Authors and governed by a BSD-style license.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package loggers_test
+
+import (
+ "io"
+ "strings"
+ "testing"
+
+ "github.com/bep/logg"
+ qt "github.com/frankban/quicktest"
+ "github.com/gohugoio/hugo/common/loggers"
+)
+
+func TestLogDistinct(t *testing.T) {
+ c := qt.New(t)
+
+ opts := loggers.Options{
+ Distinct: true,
+ StoreErrors: true,
+ Stdout: io.Discard,
+ Stderr: io.Discard,
+ }
+
+ l := loggers.New(opts)
+
+ for i := 0; i < 10; i++ {
+ l.Errorln("error 1")
+ l.Errorln("error 2")
+ l.Warnln("warn 1")
+ }
+ c.Assert(strings.Count(l.Errors(), "error 1"), qt.Equals, 1)
+ c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 2)
+ c.Assert(l.LoggCount(logg.LevelWarn), qt.Equals, 1)
+}
+
+func TestHookLast(t *testing.T) {
+ c := qt.New(t)
+
+ opts := loggers.Options{
+ HandlerPost: func(e *logg.Entry) error {
+ panic(e.Message)
+ },
+ Stdout: io.Discard,
+ Stderr: io.Discard,
+ }
+
+ l := loggers.New(opts)
+
+ c.Assert(func() { l.Warnln("warn 1") }, qt.PanicMatches, "warn 1")
+}
+
+func TestOptionStoreErrors(t *testing.T) {
+ c := qt.New(t)
+
+ var sb strings.Builder
+
+ opts := loggers.Options{
+ StoreErrors: true,
+ Stderr: &sb,
+ Stdout: &sb,
+ }
+
+ l := loggers.New(opts)
+ l.Errorln("error 1")
+ l.Errorln("error 2")
+
+ errorsStr := l.Errors()
+
+ c.Assert(errorsStr, qt.Contains, "error 1")
+ c.Assert(errorsStr, qt.Not(qt.Contains), "ERROR")
+
+ c.Assert(sb.String(), qt.Contains, "error 1")
+ c.Assert(sb.String(), qt.Contains, "ERROR")
+
+}
+
+func TestLogCount(t *testing.T) {
+ c := qt.New(t)
+
+ opts := loggers.Options{
+ StoreErrors: true,
+ }
+
+ l := loggers.New(opts)
+ l.Errorln("error 1")
+ l.Errorln("error 2")
+ l.Warnln("warn 1")
+
+ c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 2)
+ c.Assert(l.LoggCount(logg.LevelWarn), qt.Equals, 1)
+ c.Assert(l.LoggCount(logg.LevelInfo), qt.Equals, 0)
+}
+
+func TestSuppressStatements(t *testing.T) {
+ c := qt.New(t)
+
+ opts := loggers.Options{
+ StoreErrors: true,
+ SuppresssStatements: map[string]bool{
+ "error-1": true,
+ },
+ }
+
+ l := loggers.New(opts)
+ l.Error().WithField(loggers.FieldNameStatementID, "error-1").Logf("error 1")
+ l.Errorln("error 2")
+
+ errorsStr := l.Errors()
+
+ c.Assert(errorsStr, qt.Not(qt.Contains), "error 1")
+ c.Assert(errorsStr, qt.Contains, "error 2")
+ c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 1)
+
+}
+
+func TestReset(t *testing.T) {
+ c := qt.New(t)
+
+ opts := loggers.Options{
+ StoreErrors: true,
+ Distinct: true,
+ Stdout: io.Discard,
+ Stderr: io.Discard,
+ }
+
+ l := loggers.New(opts)
+
+ for i := 0; i < 3; i++ {
+ l.Errorln("error 1")
+ l.Errorln("error 2")
+ l.Errorln("error 1")
+ c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 2)
+
+ l.Reset()
+
+ errorsStr := l.Errors()
+
+ c.Assert(errorsStr, qt.Equals, "")
+ c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 0)
+
+ }
+}
diff --git a/common/loggers/loggerglobal.go b/common/loggers/loggerglobal.go
new file mode 100644
index 000000000..92b2469ba
--- /dev/null
+++ b/common/loggers/loggerglobal.go
@@ -0,0 +1,53 @@
+// Copyright 2023 The Hugo Authors. All rights reserved.
+// Some functions in this file (see comments) is based on the Go source code,
+// copyright The Go Authors and governed by a BSD-style license.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package loggers
+
+import (
+ "sync"
+
+ "github.com/bep/logg"
+)
+
+func InitGlobalLogger(panicOnWarnings bool) {
+ logMu.Lock()
+ defer logMu.Unlock()
+ var logHookLast func(e *logg.Entry) error
+ if panicOnWarnings {
+ logHookLast = PanicOnWarningHook
+ }
+
+ log = New(
+ Options{
+ Distinct: true,
+ HandlerPost: logHookLast,
+ },
+ )
+}
+
+var logMu sync.Mutex
+
+func Log() Logger {
+ logMu.Lock()
+ defer logMu.Unlock()
+ return log
+}
+
+// The global logger.
+var log Logger
+
+func init() {
+ InitGlobalLogger(false)
+}
diff --git a/common/loggers/loggers.go b/common/loggers/loggers.go
deleted file mode 100644
index fbbbca435..000000000
--- a/common/loggers/loggers.go
+++ /dev/null
@@ -1,355 +0,0 @@
-// Copyright 2020 The Hugo Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package loggers
-
-import (
- "bytes"
- "fmt"
- "io"
- "log"
- "os"
- "regexp"
- "runtime"
- "sync/atomic"
- "time"
-
- "github.com/gohugoio/hugo/common/terminal"
-
- jww "github.com/spf13/jwalterweatherman"
-)
-
-var (
- // Counts ERROR logs to the global jww logger.
- GlobalErrorCounter *jww.Counter
- PanicOnWarning atomic.Bool
-)
-
-func init() {
- GlobalErrorCounter = &jww.Counter{}
- jww.SetLogListeners(jww.LogCounter(GlobalErrorCounter, jww.LevelError))
-}
-
-func LoggerToWriterWithPrefix(logger *log.Logger, prefix string) io.Writer {
- return prefixWriter{
- logger: logger,
- prefix: prefix,
- }
-}
-
-type prefixWriter struct {
- logger *log.Logger
- prefix string
-}
-
-func (w prefixWriter) Write(p []byte) (n int, err error) {
- w.logger.Printf("%s: %s", w.prefix, p)
- return len(p), nil
-}
-
-type Logger interface {
- Printf(format string, v ...any)
- Println(v ...any)
- PrintTimerIfDelayed(start time.Time, name string)
- Debug() *log.Logger
- Debugf(format string, v ...any)
- Debugln(v ...any)
- Info() *log.Logger
- Infof(format string, v ...any)
- Infoln(v ...any)
- Warn() *log.Logger
- Warnf(format string, v ...any)
- Warnln(v ...any)
- Error() *log.Logger
- Errorf(format string, v ...any)
- Errorln(v ...any)
- Errors() string
-
- Out() io.Writer
-
- Reset()
-
- // Used in tests.
- LogCounters() *LogCounters
-}
-
-type LogCounters struct {
- ErrorCounter *jww.Counter
- WarnCounter *jww.Counter
-}
-
-type logger struct {
- *jww.Notepad
-
- // The writer that represents stdout.
- // Will be io.Discard when in quiet mode.
- out io.Writer
-
- logCounters *LogCounters
-
- // This is only set in server mode.
- errors *bytes.Buffer
-}
-
-func (l *logger) Printf(format string, v ...any) {
- l.FEEDBACK.Printf(format, v...)
-}
-
-func (l *logger) Println(v ...any) {
- l.FEEDBACK.Println(v...)
-}
-
-func (l *logger) Debug() *log.Logger {
- return l.DEBUG
-}
-
-func (l *logger) Debugf(format string, v ...any) {
- l.DEBUG.Printf(format, v...)
-}
-
-func (l *logger) Debugln(v ...any) {
- l.DEBUG.Println(v...)
-}
-
-func (l *logger) Infof(format string, v ...any) {
- l.INFO.Printf(format, v...)
-}
-
-func (l *logger) Infoln(v ...any) {
- l.INFO.Println(v...)
-}
-
-func (l *logger) Info() *log.Logger {
- return l.INFO
-}
-
-const panicOnWarningMessage = "Warning trapped. Remove the --panicOnWarning flag to continue."
-
-func (l *logger) Warnf(format string, v ...any) {
- l.WARN.Printf(format, v...)
- if PanicOnWarning.Load() {
- panic(panicOnWarningMessage)
- }
-}
-
-func (l *logger) Warnln(v ...any) {
- l.WARN.Println(v...)
- if PanicOnWarning.Load() {
- panic(panicOnWarningMessage)
- }
-}
-
-func (l *logger) Warn() *log.Logger {
- return l.WARN
-}
-
-func (l *logger) Errorf(format string, v ...any) {
- l.ERROR.Printf(format, v...)
-}
-
-func (l *logger) Errorln(v ...any) {
- l.ERROR.Println(v...)
-}
-
-func (l *logger) Error() *log.Logger {
- return l.ERROR
-}
-
-func (l *logger) LogCounters() *LogCounters {
- return l.logCounters
-}
-
-func (l *logger) Out() io.Writer {
- return l.out
-}
-
-// PrintTimerIfDelayed prints a time statement to the FEEDBACK logger
-// if considerable time is spent.
-func (l *logger) PrintTimerIfDelayed(start time.Time, name string) {
- elapsed := time.Since(start)
- milli := int(1000 * elapsed.Seconds())
- if milli < 500 {
- return
- }
- l.Printf("%s in %v ms", name, milli)
-}
-
-func (l *logger) PrintTimer(start time.Time, name string) {
- elapsed := time.Since(start)
- milli := int(1000 * elapsed.Seconds())
- l.Printf("%s in %v ms", name, milli)
-}
-
-func (l *logger) Errors() string {
- if l.errors == nil {
- return ""
- }
- return ansiColorRe.ReplaceAllString(l.errors.String(), "")
-}
-
-// Reset resets the logger's internal state.
-func (l *logger) Reset() {
- l.logCounters.ErrorCounter.Reset()
- if l.errors != nil {
- l.errors.Reset()
- }
-}
-
-// NewLogger creates a new Logger for the given thresholds
-func NewLogger(stdoutThreshold, logThreshold jww.Threshold, outHandle, logHandle io.Writer, saveErrors bool) Logger {
- return newLogger(stdoutThreshold, logThreshold, outHandle, logHandle, saveErrors)
-}
-
-// NewDebugLogger is a convenience function to create a debug logger.
-func NewDebugLogger() Logger {
- return NewBasicLogger(jww.LevelDebug)
-}
-
-// NewWarningLogger is a convenience function to create a warning logger.
-func NewWarningLogger() Logger {
- return NewBasicLogger(jww.LevelWarn)
-}
-
-// NewInfoLogger is a convenience function to create a info logger.
-func NewInfoLogger() Logger {
- return NewBasicLogger(jww.LevelInfo)
-}
-
-// NewErrorLogger is a convenience function to create an error logger.
-func NewErrorLogger() Logger {
- return NewBasicLogger(jww.LevelError)
-}
-
-// NewBasicLogger creates a new basic logger writing to Stdout.
-func NewBasicLogger(t jww.Threshold) Logger {
- return newLogger(t, jww.LevelError, os.Stdout, io.Discard, false)
-}
-
-// NewBasicLoggerForWriter creates a new basic logger writing to w.
-func NewBasicLoggerForWriter(t jww.Threshold, w io.Writer) Logger {
- return newLogger(t, jww.LevelError, w, io.Discard, false)
-}
-
-// RemoveANSIColours removes all ANSI colours from the given string.
-func RemoveANSIColours(s string) string {
- return ansiColorRe.ReplaceAllString(s, "")
-}
-
-var (
- ansiColorRe = regexp.MustCompile("(?s)\\033\\[\\d*(;\\d*)*m")
- errorRe = regexp.MustCompile("^(ERROR|FATAL|WARN)")
-)
-
-type ansiCleaner struct {
- w io.Writer
-}
-
-func (a ansiCleaner) Write(p []byte) (n int, err error) {
- return a.w.Write(ansiColorRe.ReplaceAll(p, []byte("")))
-}
-
-type labelColorizer struct {
- w io.Writer
-}
-
-func (a labelColorizer) Write(p []byte) (n int, err error) {
- replaced := errorRe.ReplaceAllStringFunc(string(p), func(m string) string {
- switch m {
- case "ERROR", "FATAL":
- return terminal.Error(m)
- case "WARN":
- return terminal.Warning(m)
- default:
- return m
- }
- })
- // io.MultiWriter will abort if we return a bigger write count than input
- // bytes, so we lie a little.
- _, err = a.w.Write([]byte(replaced))
- return len(p), err
-}
-
-// InitGlobalLogger initializes the global logger, used in some rare cases.
-func InitGlobalLogger(stdoutThreshold, logThreshold jww.Threshold, outHandle, logHandle io.Writer) {
- outHandle, logHandle = getLogWriters(outHandle, logHandle)
-
- jww.SetStdoutOutput(outHandle)
- jww.SetLogOutput(logHandle)
- jww.SetLogThreshold(logThreshold)
- jww.SetStdoutThreshold(stdoutThreshold)
-}
-
-func getLogWriters(outHandle, logHandle io.Writer) (io.Writer, io.Writer) {
- isTerm := terminal.PrintANSIColors(os.Stdout)
- if logHandle != io.Discard && isTerm {
- // Remove any Ansi coloring from log output
- logHandle = ansiCleaner{w: logHandle}
- }
-
- if isTerm {
- outHandle = labelColorizer{w: outHandle}
- }
-
- return outHandle, logHandle
-}
-
-type fatalLogWriter int
-
-func (s fatalLogWriter) Write(p []byte) (n int, err error) {
- trace := make([]byte, 1500)
- runtime.Stack(trace, true)
- fmt.Printf("\n===========\n\n%s\n", trace)
- os.Exit(-1)
-
- return 0, nil
-}
-
-var fatalLogListener = func(t jww.Threshold) io.Writer {
- if t != jww.LevelError {
- // Only interested in ERROR
- return nil
- }
-
- return new(fatalLogWriter)
-}
-
-func newLogger(stdoutThreshold, logThreshold jww.Threshold, outHandle, logHandle io.Writer, saveErrors bool) *logger {
- errorCounter := &jww.Counter{}
- warnCounter := &jww.Counter{}
- outHandle, logHandle = getLogWriters(outHandle, logHandle)
-
- listeners := []jww.LogListener{jww.LogCounter(errorCounter, jww.LevelError), jww.LogCounter(warnCounter, jww.LevelWarn)}
- var errorBuff *bytes.Buffer
- if saveErrors {
- errorBuff = new(bytes.Buffer)
- errorCapture := func(t jww.Threshold) io.Writer {
- if t != jww.LevelError {
- // Only interested in ERROR
- return nil
- }
- return errorBuff
- }
-
- listeners = append(listeners, errorCapture)
- }
-
- return &logger{
- Notepad: jww.NewNotepad(stdoutThreshold, logThreshold, outHandle, logHandle, "", log.Ldate|log.Ltime, listeners...),
- out: outHandle,
- logCounters: &LogCounters{
- ErrorCounter: errorCounter,
- WarnCounter: warnCounter,
- },
- errors: errorBuff,
- }
-}
diff --git a/common/loggers/loggers_test.go b/common/loggers/loggers_test.go
deleted file mode 100644
index a7bd1ae12..000000000
--- a/common/loggers/loggers_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2018 The Hugo Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package loggers
-
-import (
- "bytes"
- "fmt"
- "log"
- "testing"
-
- qt "github.com/frankban/quicktest"
-)
-
-func TestLogger(t *testing.T) {
- c := qt.New(t)
- l := NewWarningLogger()
-
- l.Errorln("One error")
- l.Errorln("Two error")
- l.Warnln("A warning")
-
- c.Assert(l.LogCounters().ErrorCounter.Count(), qt.Equals, uint64(2))
-}
-
-func TestLoggerToWriterWithPrefix(t *testing.T) {
- c := qt.New(t)
-
- var b bytes.Buffer
-
- logger := log.New(&b, "", 0)
-
- w := LoggerToWriterWithPrefix(logger, "myprefix")
-
- fmt.Fprint(w, "Hello Hugo!")
-
- c.Assert(b.String(), qt.Equals, "myprefix: Hello Hugo!\n")
-}
-
-func TestRemoveANSIColours(t *testing.T) {
- c := qt.New(t)
-
- c.Assert(RemoveANSIColours(""), qt.Equals, "")
- c.Assert(RemoveANSIColours("\033[31m"), qt.Equals, "")
- c.Assert(RemoveANSIColours("\033[31mHello"), qt.Equals, "Hello")
- c.Assert(RemoveANSIColours("\033[31mHello\033[0m"), qt.Equals, "Hello")
- c.Assert(RemoveANSIColours("\033[31mHello\033[0m World"), qt.Equals, "Hello World")
- c.Assert(RemoveANSIColours("\033[31mHello\033[0m World\033[31m!"), qt.Equals, "Hello World!")
- c.Assert(RemoveANSIColours("\x1b[90m 5 |"), qt.Equals, " 5 |")
-}
diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go
index 08792d870..ec7895eea 100644
--- a/config/allconfig/allconfig.go
+++ b/config/allconfig/allconfig.go
@@ -490,6 +490,9 @@ type RootConfig struct {
// ENable to print warnings for multiple files published to the same destination.
LogPathWarnings bool
+ // Enable to panic on warning log entries. This may make it easier to detect the source.
+ PanicOnWarning bool
+
// The configured environment. Default is "development" for server and "production" for build.
Environment string
diff --git a/config/allconfig/load.go b/config/allconfig/load.go
index eca9d06df..4e5478c40 100644
--- a/config/allconfig/load.go
+++ b/config/allconfig/load.go
@@ -46,7 +46,7 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) {
}
if d.Logger == nil {
- d.Logger = loggers.NewErrorLogger()
+ d.Logger = loggers.NewDefault()
}
l := &configLoader{ConfigSourceDescriptor: d, cfg: config.New()}
@@ -90,8 +90,9 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) {
return nil, fmt.Errorf("failed to init config: %w", err)
}
- // This is unfortunate, but this is a global setting.
+ // This is unfortunate, but these are global settings.
tpl.SetSecurityAllowActionJSTmpl(configs.Base.Security.GoTemplates.AllowActionJSTmpl)
+ loggers.InitGlobalLogger(configs.Base.PanicOnWarning)
return configs, nil
diff --git a/config/commonConfig.go b/config/commonConfig.go
index bd3e235bd..ac1dd39fa 100644
--- a/config/commonConfig.go
+++ b/config/commonConfig.go
@@ -19,6 +19,7 @@ import (
"sort"
"strings"
+ "github.com/bep/logg"
"github.com/gobwas/glob"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/types"
@@ -306,6 +307,7 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
if c.compiledSource != nil {
return nil
}
+
source := c.Source
target := c.Target
sourceRe, err := regexp.Compile(source)
@@ -313,6 +315,8 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
return fmt.Errorf("failed to compile cache buster source %q: %w", c.Source, err)
}
var compileErr error
+ debugl := logger.Logger().WithLevel(logg.LevelDebug).WithField(loggers.FieldNameCmd, "cachebuster")
+
c.compiledSource = func(s string) func(string) bool {
m := sourceRe.FindStringSubmatch(s)
matchString := "no match"
@@ -320,7 +324,7 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
if match {
matchString = "match!"
}
- logger.Debugf("cachebuster: Matching %q with source %q: %s\n", s, source, matchString)
+ debugl.Logf("Matching %q with source %q: %s", s, source, matchString)
if !match {
return nil
}
@@ -341,7 +345,7 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
if match {
matchString = "match!"
}
- logger.Debugf("cachebuster: Matching %q with target %q: %s\n", s, target, matchString)
+ logger.Debugf("Matching %q with target %q: %s", s, target, matchString)
return match
}
diff --git a/config/commonConfig_test.go b/config/commonConfig_test.go
index 106069bdc..b8130eb0d 100644
--- a/config/commonConfig_test.go
+++ b/config/commonConfig_test.go
@@ -92,7 +92,7 @@ status = 301
s, err := DecodeServer(cfg)
c.Assert(err, qt.IsNil)
- c.Assert(s.CompileConfig(loggers.NewErrorLogger()), qt.IsNil)
+ c.Assert(s.CompileConfig(loggers.NewDefault()), qt.IsNil)
c.Assert(s.MatchHeaders("/foo.jpg"), qt.DeepEquals, []types.KeyValueStr{
{Key: "X-Content-Type-Options", Value: "nosniff"},
@@ -145,7 +145,7 @@ func TestBuildConfigCacheBusters(t *testing.T) {
c := qt.New(t)
cfg := New()
conf := DecodeBuildConfig(cfg)
- l := loggers.NewInfoLogger()
+ l := loggers.NewDefault()
c.Assert(conf.CompileConfig(l), qt.IsNil)
m, err := conf.MatchCacheBuster(l, "assets/foo/main.js")
diff --git a/deploy/deploy.go b/deploy/deploy.go
index db88996a9..60a3da363 100644
--- a/deploy/deploy.go
+++ b/deploy/deploy.go
@@ -37,10 +37,10 @@ import (
"github.com/dustin/go-humanize"
"github.com/gobwas/glob"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/media"
"github.com/spf13/afero"
- jww "github.com/spf13/jwalterweatherman"
"golang.org/x/text/unicode/norm"
"gocloud.dev/blob"
@@ -56,9 +56,10 @@ type Deployer struct {
bucket *blob.Bucket
mediaTypes media.Types // Hugo's MediaType to guess ContentType
- quiet bool // true reduces STDOUT
+ quiet bool // true reduces STDOUT // TODO(bep) remove, this is a global feature.
- cfg DeployConfig
+ cfg DeployConfig
+ logger loggers.Logger
target *Target // the target to deploy to
@@ -73,7 +74,7 @@ type deploySummary struct {
const metaMD5Hash = "md5chksum" // the meta key to store md5hash in
// New constructs a new *Deployer.
-func New(cfg config.AllProvider, localFs afero.Fs) (*Deployer, error) {
+func New(cfg config.AllProvider, logger loggers.Logger, localFs afero.Fs) (*Deployer, error) {
dcfg := cfg.GetConfigSection(deploymentConfigKey).(DeployConfig)
targetName := dcfg.Target
@@ -112,12 +113,16 @@ func (d *Deployer) openBucket(ctx context.Context) (*blob.Bucket, error) {
if d.bucket != nil {
return d.bucket, nil
}
- jww.FEEDBACK.Printf("Deploying to target %q (%s)\n", d.target.Name, d.target.URL)
+ d.logger.Printf("Deploying to target %q (%s)\n", d.target.Name, d.target.URL)
return blob.OpenBucket(ctx, d.target.URL)
}
// Deploy deploys the site to a target.
func (d *Deployer) Deploy(ctx context.Context) error {
+ if d.logger == nil {
+ d.logger = loggers.NewDefault()
+ }
+
bucket, err := d.openBucket(ctx)
if err != nil {
return err
@@ -132,33 +137,33 @@ func (d *Deployer) Deploy(ctx context.Context) error {
if d.target != nil {
include, exclude = d.target.includeGlob, d.target.excludeGlob
}
- local, err := walkLocal(d.localFs, d.cfg.Matchers, include, exclude, d.mediaTypes)
+ local, err := d.walkLocal(d.localFs, d.cfg.Matchers, include, exclude, d.mediaTypes)
if err != nil {
return err
}
- jww.INFO.Printf("Found %d local files.\n", len(local))
+ d.logger.Infof("Found %d local files.\n", len(local))
d.summary.NumLocal = len(local)
// Load remote files from the target.
- remote, err := walkRemote(ctx, bucket, include, exclude)
+ remote, err := d.walkRemote(ctx, bucket, include, exclude)
if err != nil {
return err
}
- jww.INFO.Printf("Found %d remote files.\n", len(remote))
+ d.logger.Infof("Found %d remote files.\n", len(remote))
d.summary.NumRemote = len(remote)
// Diff local vs remote to see what changes need to be applied.
- uploads, deletes := findDiffs(local, remote, d.cfg.Force)
+ uploads, deletes := d.findDiffs(local, remote, d.cfg.Force)
d.summary.NumUploads = len(uploads)
d.summary.NumDeletes = len(deletes)
if len(uploads)+len(deletes) == 0 {
if !d.quiet {
- jww.FEEDBACK.Println("No changes required.")
+ d.logger.Println("No changes required.")
}
return nil
}
if !d.quiet {
- jww.FEEDBACK.Println(summarizeChanges(uploads, deletes))
+ d.logger.Println(summarizeChanges(uploads, deletes))
}
// Ask for confirmation before proceeding.
@@ -192,14 +197,14 @@ func (d *Deployer) Deploy(ctx context.Context) error {
for _, upload := range uploads {
if d.cfg.DryRun {
if !d.quiet {
- jww.FEEDBACK.Printf("[DRY RUN] Would upload: %v\n", upload)
+ d.logger.Printf("[DRY RUN] Would upload: %v\n", upload)
}
continue
}
sem <- struct{}{}
go func(upload *fileToUpload) {
- if err := doSingleUpload(ctx, bucket, upload); err != nil {
+ if err := d.doSingleUpload(ctx, bucket, upload); err != nil {
errMu.Lock()
defer errMu.Unlock()
errs = append(errs, err)
@@ -214,7 +219,7 @@ func (d *Deployer) Deploy(ctx context.Context) error {
}
if d.cfg.MaxDeletes != -1 && len(deletes) > d.cfg.MaxDeletes {
- jww.WARN.Printf("Skipping %d deletes because it is more than --maxDeletes (%d). If this is expected, set --maxDeletes to a larger number, or -1 to disable this check.\n", len(deletes), d.cfg.MaxDeletes)
+ d.logger.Warnf("Skipping %d deletes because it is more than --maxDeletes (%d). If this is expected, set --maxDeletes to a larger number, or -1 to disable this check.\n", len(deletes), d.cfg.MaxDeletes)
d.summary.NumDeletes = 0
} else {
// Apply deletes in parallel.
@@ -223,16 +228,16 @@ func (d *Deployer) Deploy(ctx context.Context) error {
for _, del := range deletes {
if d.cfg.DryRun {
if !d.quiet {
- jww.FEEDBACK.Printf("[DRY RUN] Would delete %s\n", del)
+ d.logger.Printf("[DRY RUN] Would delete %s\n", del)
}
continue
}
sem <- struct{}{}
go func(del string) {
- jww.INFO.Printf("Deleting %s...\n", del)
+ d.logger.Infof("Deleting %s...\n", del)
if err := bucket.Delete(ctx, del); err != nil {
if gcerrors.Code(err) == gcerrors.NotFound {
- jww.WARN.Printf("Failed to delete %q because it wasn't found: %v", del, err)
+ d.logger.Warnf("Failed to delete %q because it wasn't found: %v", del, err)
} else {
errMu.Lock()
defer errMu.Unlock()
@@ -250,24 +255,24 @@ func (d *Deployer) Deploy(ctx context.Context) error {
if len(errs) > 0 {
if !d.quiet {
- jww.FEEDBACK.Printf("Encountered %d errors.\n", len(errs))
+ d.logger.Printf("Encountered %d errors.\n", len(errs))
}
return errs[0]
}
if !d.quiet {
- jww.FEEDBACK.Println("Success!")
+ d.logger.Println("Success!")
}
if d.cfg.InvalidateCDN {
if d.target.CloudFrontDistributionID != "" {
if d.cfg.DryRun {
if !d.quiet {
- jww.FEEDBACK.Printf("[DRY RUN] Would invalidate CloudFront CDN with ID %s\n", d.target.CloudFrontDistributionID)
+ d.logger.Printf("[DRY RUN] Would invalidate CloudFront CDN with ID %s\n", d.target.CloudFrontDistributionID)
}
} else {
- jww.FEEDBACK.Println("Invalidating CloudFront CDN...")
+ d.logger.Println("Invalidating CloudFront CDN...")
if err := InvalidateCloudFront(ctx, d.target.CloudFrontDistributionID); err != nil {
- jww.FEEDBACK.Printf("Failed to invalidate CloudFront CDN: %v\n", err)
+ d.logger.Printf("Failed to invalidate CloudFront CDN: %v\n", err)
return err
}
}
@@ -275,17 +280,17 @@ func (d *Deployer) Deploy(ctx context.Context) error {
if d.target.GoogleCloudCDNOrigin != "" {
if d.cfg.DryRun {
if !d.quiet {
- jww.FEEDBACK.Printf("[DRY RUN] Would invalidate Google Cloud CDN with origin %s\n", d.target.GoogleCloudCDNOrigin)
+ d.logger.Printf("[DRY RUN] Would invalidate Google Cloud CDN with origin %s\n", d.target.GoogleCloudCDNOrigin)
}
} else {
- jww.FEEDBACK.Println("Invalidating Google Cloud CDN...")
+ d.logger.Println("Invalidating Google Cloud CDN...")
if err := InvalidateGoogleCloudCDN(ctx, d.target.GoogleCloudCDNOrigin); err != nil {
- jww.FEEDBACK.Printf("Failed to invalidate Google Cloud CDN: %v\n", err)
+ d.logger.Printf("Failed to invalidate Google Cloud CDN: %v\n", err)
return err
}
}
}
- jww.FEEDBACK.Println("Success!")
+ d.logger.Println("Success!")
}
return nil
}
@@ -300,8 +305,8 @@ func summarizeChanges(uploads []*fileToUpload, deletes []string) string {
}
// doSingleUpload executes a single file upload.
-func doSingleUpload(ctx context.Context, bucket *blob.Bucket, upload *fileToUpload) error {
- jww.INFO.Printf("Uploading %v...\n", upload)
+func (d *Deployer) doSingleUpload(ctx context.Context, bucket *blob.Bucket, upload *fileToUpload) error {
+ d.logger.Infof("Uploading %v...\n", upload)
opts := &blob.WriterOptions{
CacheControl: upload.Local.CacheControl(),
ContentEncoding: upload.Local.ContentEncoding(),
@@ -479,7 +484,7 @@ func knownHiddenDirectory(name string) bool {
// walkLocal walks the source directory and returns a flat list of files,
// using localFile.SlashPath as the map keys.
-func walkLocal(fs afero.Fs, matchers []*Matcher, include, exclude glob.Glob, mediaTypes media.Types) (map[string]*localFile, error) {
+func (d *Deployer) walkLocal(fs afero.Fs, matchers []*Matcher, include, exclude glob.Glob, mediaTypes media.Types) (map[string]*localFile, error) {
retval := map[string]*localFile{}
err := afero.Walk(fs, "", func(path string, info os.FileInfo, err error) error {
if err != nil {
@@ -509,11 +514,11 @@ func walkLocal(fs afero.Fs, matchers []*Matcher, include, exclude glob.Glob, med
// Check include/exclude matchers.
slashpath := filepath.ToSlash(path)
if include != nil && !include.Match(slashpath) {
- jww.INFO.Printf(" dropping %q due to include\n", slashpath)
+ d.logger.Infof(" dropping %q due to include\n", slashpath)
return nil
}
if exclude != nil && exclude.Match(slashpath) {
- jww.INFO.Printf(" dropping %q due to exclude\n", slashpath)
+ d.logger.Infof(" dropping %q due to exclude\n", slashpath)
return nil
}
@@ -539,7 +544,7 @@ func walkLocal(fs afero.Fs, matchers []*Matcher, include, exclude glob.Glob, med
}
// walkRemote walks the target bucket and returns a flat list.
-func walkRemote(ctx context.Context, bucket *blob.Bucket, include, exclude glob.Glob) (map[string]*blob.ListObject, error) {
+func (d *Deployer) walkRemote(ctx context.Context, bucket *blob.Bucket, include, exclude glob.Glob) (map[string]*blob.ListObject, error) {
retval := map[string]*blob.ListObject{}
iter := bucket.List(nil)
for {
@@ -552,11 +557,11 @@ func walkRemote(ctx context.Context, bucket *blob.Bucket, include, exclude glob.
}
// Check include/exclude matchers.
if include != nil && !include.Match(obj.Key) {
- jww.INFO.Printf(" remote dropping %q due to include\n", obj.Key)
+ d.logger.Infof(" remote dropping %q due to include\n", obj.Key)
continue
}
if exclude != nil && exclude.Match(obj.Key) {
- jww.INFO.Printf(" remote dropping %q due to exclude\n", obj.Key)
+ d.logger.Infof(" remote dropping %q due to exclude\n", obj.Key)
continue
}
// If the remote didn't give us an MD5, use remote attributes MD5, if that doesn't exist compute one.
@@ -629,7 +634,7 @@ func (u *fileToUpload) String() string {
// findDiffs diffs localFiles vs remoteFiles to see what changes should be
// applied to the remote target. It returns a slice of *fileToUpload and a
// slice of paths for files to delete.
-func findDiffs(localFiles map[string]*localFile, remoteFiles map[string]*blob.ListObject, force bool) ([]*fileToUpload, []string) {
+func (d *Deployer) findDiffs(localFiles map[string]*localFile, remoteFiles map[string]*blob.ListObject, force bool) ([]*fileToUpload, []string) {
var uploads []*fileToUpload
var deletes []string
@@ -680,10 +685,10 @@ func findDiffs(localFiles map[string]*localFile, remoteFiles map[string]*blob.Li
reason = reasonNotFound
}
if upload {
- jww.DEBUG.Printf("%s needs to be uploaded: %v\n", path, reason)
+ d.logger.Debugf("%s needs to be uploaded: %v\n", path, reason)
uploads = append(uploads, &fileToUpload{lf, reason})
} else {
- jww.DEBUG.Printf("%s exists at target and does not need to be uploaded", path)
+ d.logger.Debugf("%s exists at target and does not need to be uploaded", path)
}
}
diff --git a/deploy/deploy_test.go b/deploy/deploy_test.go
index fe874fbbd..66eece10b 100644
--- a/deploy/deploy_test.go
+++ b/deploy/deploy_test.go
@@ -30,6 +30,7 @@ import (
"sort"
"testing"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/media"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
@@ -197,7 +198,8 @@ func TestFindDiffs(t *testing.T) {
for _, r := range tc.Remote {
remote[r.Key] = r
}
- gotUpdates, gotDeletes := findDiffs(local, remote, tc.Force)
+ d := newDeployer()
+ gotUpdates, gotDeletes := d.findDiffs(local, remote, tc.Force)
gotUpdates = applyOrdering(nil, gotUpdates)[0]
sort.Slice(gotDeletes, func(i, j int) bool { return gotDeletes[i] < gotDeletes[j] })
if diff := cmp.Diff(gotUpdates, tc.WantUpdates, cmpopts.IgnoreUnexported(localFile{})); diff != "" {
@@ -249,7 +251,8 @@ func TestWalkLocal(t *testing.T) {
fd.Close()
}
}
- if got, err := walkLocal(fs, nil, nil, nil, media.DefaultTypes); err != nil {
+ d := newDeployer()
+ if got, err := d.walkLocal(fs, nil, nil, nil, media.DefaultTypes); err != nil {
t.Fatal(err)
} else {
expect := map[string]any{}
@@ -1026,3 +1029,9 @@ func verifyRemote(ctx context.Context, bucket *blob.Bucket, local []*fileData) (
}
return diff, nil
}
+
+func newDeployer() *Deployer {
+ return &Deployer{
+ logger: loggers.NewDefault(),
+ }
+}
diff --git a/deps/deps.go b/deps/deps.go
index 39462de96..309555080 100644
--- a/deps/deps.go
+++ b/deps/deps.go
@@ -3,12 +3,14 @@ package deps
import (
"context"
"fmt"
+ "io"
"path/filepath"
"sort"
"strings"
"sync"
"sync/atomic"
+ "github.com/bep/logg"
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config"
@@ -25,7 +27,6 @@ import (
"github.com/gohugoio/hugo/source"
"github.com/gohugoio/hugo/tpl"
"github.com/spf13/afero"
- jww "github.com/spf13/jwalterweatherman"
)
// Deps holds dependencies used by many.
@@ -36,9 +37,6 @@ type Deps struct {
// The logger to use.
Log loggers.Logger `json:"-"`
- // Used to log errors that may repeat itself many times.
- LogDistinct loggers.Logger
-
ExecHelper *hexec.Exec
// The templates to use. This will usually implement the full tpl.TemplateManager.
@@ -117,15 +115,13 @@ func (d *Deps) Init() error {
}
if d.Log == nil {
- d.Log = loggers.NewErrorLogger()
- }
-
- if d.LogDistinct == nil {
- d.LogDistinct = helpers.NewDistinctLogger(d.Log)
+ d.Log = loggers.NewDefault()
}
if d.globalErrHandler == nil {
- d.globalErrHandler = &globalErrHandler{}
+ d.globalErrHandler = &globalErrHandler{
+ logger: d.Log,
+ }
}
if d.BuildState == nil {
@@ -228,6 +224,8 @@ func (d *Deps) Compile(prototype *Deps) error {
}
type globalErrHandler struct {
+ logger loggers.Logger
+
// Channel for some "hard to get to" build errors
buildErrors chan error
// Used to signal that the build is done.
@@ -246,8 +244,7 @@ func (e *globalErrHandler) SendError(err error) {
}
return
}
-
- jww.ERROR.Println(err)
+ e.logger.Errorln(err)
}
func (e *globalErrHandler) StartErrorCollector() chan error {
@@ -312,9 +309,16 @@ func (d *Deps) Close() error {
// on a global level, i.e. logging etc.
// Nil values will be given default values.
type DepsCfg struct {
+ // The logger to use. Only set in some tests.
+ // TODO(bep) get rid of this.
+ TestLogger loggers.Logger
+
+ // The logging level to use.
+ LogLevel logg.Level
- // The Logger to use.
- Logger loggers.Logger
+ // Where to write the logs.
+ // Currently we typically write everything to stdout.
+ LogOut io.Writer
// The file systems to use
Fs *hugofs.Fs
diff --git a/go.mod b/go.mod
index 6371423e0..92939a086 100644
--- a/go.mod
+++ b/go.mod
@@ -16,6 +16,7 @@ require (
github.com/bep/gowebp v0.2.0
github.com/bep/helpers v0.4.0
github.com/bep/lazycache v0.2.0
+ github.com/bep/logg v0.2.0
github.com/bep/mclib v1.20400.20402
github.com/bep/overlayfs v0.6.0
github.com/bep/simplecobra v0.3.2
@@ -25,6 +26,7 @@ require (
github.com/disintegration/gift v1.2.1
github.com/dustin/go-humanize v1.0.1
github.com/evanw/esbuild v0.18.3
+ github.com/fatih/color v1.15.0
github.com/fortytw2/leaktest v1.3.0
github.com/frankban/quicktest v1.14.5
github.com/fsnotify/fsnotify v1.6.0
@@ -58,7 +60,6 @@ require (
github.com/spf13/cast v1.5.1
github.com/spf13/cobra v1.7.0
github.com/spf13/fsync v0.9.0
- github.com/spf13/jwalterweatherman v1.1.0
github.com/spf13/pflag v1.0.5
github.com/tdewolff/minify/v2 v2.12.6
github.com/tdewolff/parse/v2 v2.6.6
@@ -109,6 +110,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.18.3 // indirect
github.com/aws/smithy-go v1.13.5 // indirect
+ github.com/bep/clocks v0.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
@@ -129,11 +131,13 @@ require (
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/perimeterx/marshmallow v1.1.4 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
+ github.com/pkg/errors v0.9.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.10.0 // indirect
diff --git a/go.sum b/go.sum
index 7ba2c773b..ffadc9c48 100644
--- a/go.sum
+++ b/go.sum
@@ -612,6 +612,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bep/clock v0.3.0 h1:vfOA6+wVb6pPQEiXow9f/too92vNTLe9MuwO13PfI0M=
github.com/bep/clock v0.3.0/go.mod h1:6Gz2lapnJ9vxpvPxQ2u6FcXFRoj4kkiqQ6pm0ERZlwk=
+github.com/bep/clocks v0.5.0 h1:hhvKVGLPQWRVsBP/UB7ErrHYIO42gINVbvqxvYTPVps=
+github.com/bep/clocks v0.5.0/go.mod h1:SUq3q+OOq41y2lRQqH5fsOoxN8GbxSiT6jvoVVLCVhU=
github.com/bep/debounce v1.2.0 h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo=
github.com/bep/debounce v1.2.0/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/bep/gitmap v1.1.2 h1:zk04w1qc1COTZPPYWDQHvns3y1afOsdRfraFQ3qI840=
@@ -630,6 +632,8 @@ github.com/bep/helpers v0.4.0 h1:ab9veaAiWY4ST48Oxp5usaqivDmYdB744fz+tcZ3Ifs=
github.com/bep/helpers v0.4.0/go.mod h1:/QpHdmcPagDw7+RjkLFCvnlUc8lQ5kg4KDrEkb2Yyco=
github.com/bep/lazycache v0.2.0 h1:HKrlZTrDxHIrNKqmnurH42ryxkngCMYLfBpyu40VcwY=
github.com/bep/lazycache v0.2.0/go.mod h1:xUIsoRD824Vx0Q/n57+ZO7kmbEhMBOnTjM/iPixNGbg=
+github.com/bep/logg v0.2.0 h1:EWKB04ea/K/V0xd/7O6x5q+1l+Grub+9N48YMcevtF4=
+github.com/bep/logg v0.2.0/go.mod h1:Ccp9yP3wbR1mm++Kpxet91hAZBEQgmWgFgnXX3GkIV0=
github.com/bep/mclib v1.20400.20402 h1:olpCE2WSPpOAbFE1R4hnftSEmQ34+xzy2HRzd0m69rA=
github.com/bep/mclib v1.20400.20402/go.mod h1:pkrk9Kyfqg34Uj6XlDq9tdEFJBiL1FvCoCgVKRzw1EY=
github.com/bep/overlayfs v0.6.0 h1:sgLcq/qtIzbaQNl2TldGXOkHvqeZB025sPvHOQL+DYo=
@@ -916,6 +920,8 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
+github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
+github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
@@ -1496,6 +1502,8 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@@ -1505,6 +1513,7 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
@@ -1692,6 +1701,7 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
@@ -1840,7 +1850,6 @@ github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRM
github.com/spf13/fsync v0.9.0 h1:f9CEt3DOB2mnHxZaftmEOFWjABEvKM/xpf3cUwJrGOY=
github.com/spf13/fsync v0.9.0/go.mod h1:fNtJEfG3HiltN3y4cPOz6MLjos9+2pIEqLIgszqhp/0=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -2441,6 +2450,7 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/helpers/general.go b/helpers/general.go
index 50f7920f6..d1df2e335 100644
--- a/helpers/general.go
+++ b/helpers/general.go
@@ -24,13 +24,11 @@ import (
"path/filepath"
"sort"
"strings"
- "sync"
"unicode"
"unicode/utf8"
- "github.com/gohugoio/hugo/common/loggers"
-
"github.com/gohugoio/hugo/common/hugo"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/spf13/afero"
@@ -254,143 +252,6 @@ func compareStringSlices(a, b []string) bool {
return true
}
-// DistinctLogger ignores duplicate log statements.
-type DistinctLogger struct {
- loggers.Logger
- sync.RWMutex
- m map[string]bool
-}
-
-func (l *DistinctLogger) Reset() {
- l.Lock()
- defer l.Unlock()
-
- l.m = make(map[string]bool)
-}
-
-// Println will log the string returned from fmt.Sprintln given the arguments,
-// but not if it has been logged before.
-func (l *DistinctLogger) Println(v ...any) {
- // fmt.Sprint doesn't add space between string arguments
- logStatement := strings.TrimSpace(fmt.Sprintln(v...))
- l.printIfNotPrinted("println", logStatement, func() {
- l.Logger.Println(logStatement)
- })
-}
-
-// Printf will log the string returned from fmt.Sprintf given the arguments,
-// but not if it has been logged before.
-func (l *DistinctLogger) Printf(format string, v ...any) {
- logStatement := fmt.Sprintf(format, v...)
- l.printIfNotPrinted("printf", logStatement, func() {
- l.Logger.Printf(format, v...)
- })
-}
-
-func (l *DistinctLogger) Debugf(format string, v ...any) {
- logStatement := fmt.Sprintf(format, v...)
- l.printIfNotPrinted("debugf", logStatement, func() {
- l.Logger.Debugf(format, v...)
- })
-}
-
-func (l *DistinctLogger) Debugln(v ...any) {
- logStatement := fmt.Sprint(v...)
- l.printIfNotPrinted("debugln", logStatement, func() {
- l.Logger.Debugln(v...)
- })
-}
-
-func (l *DistinctLogger) Infof(format string, v ...any) {
- logStatement := fmt.Sprintf(format, v...)
- l.printIfNotPrinted("info", logStatement, func() {
- l.Logger.Infof(format, v...)
- })
-}
-
-func (l *DistinctLogger) Infoln(v ...any) {
- logStatement := fmt.Sprint(v...)
- l.printIfNotPrinted("infoln", logStatement, func() {
- l.Logger.Infoln(v...)
- })
-}
-
-func (l *DistinctLogger) Warnf(format string, v ...any) {
- logStatement := fmt.Sprintf(format, v...)
- l.printIfNotPrinted("warnf", logStatement, func() {
- l.Logger.Warnf(format, v...)
- })
-}
-
-func (l *DistinctLogger) Warnln(v ...any) {
- logStatement := fmt.Sprint(v...)
- l.printIfNotPrinted("warnln", logStatement, func() {
- l.Logger.Warnln(v...)
- })
-}
-
-func (l *DistinctLogger) Errorf(format string, v ...any) {
- logStatement := fmt.Sprint(v...)
- l.printIfNotPrinted("errorf", logStatement, func() {
- l.Logger.Errorf(format, v...)
- })
-}
-
-func (l *DistinctLogger) Errorln(v ...any) {
- logStatement := fmt.Sprint(v...)
- l.printIfNotPrinted("errorln", logStatement, func() {
- l.Logger.Errorln(v...)
- })
-}
-
-func (l *DistinctLogger) hasPrinted(key string) bool {
- l.RLock()
- defer l.RUnlock()
- _, found := l.m[key]
- return found
-}
-
-func (l *DistinctLogger) printIfNotPrinted(level, logStatement string, print func()) {
- key := level + logStatement
- if l.hasPrinted(key) {
- return
- }
- l.Lock()
- defer l.Unlock()
- l.m[key] = true // Placing this after print() can cause duplicate warning entries to be logged when --panicOnWarning is true.
- print()
-
-}
-
-// NewDistinctErrorLogger creates a new DistinctLogger that logs ERRORs
-func NewDistinctErrorLogger() loggers.Logger {
- return &DistinctLogger{m: make(map[string]bool), Logger: loggers.NewErrorLogger()}
-}
-
-// NewDistinctLogger creates a new DistinctLogger that logs to the provided logger.
-func NewDistinctLogger(logger loggers.Logger) loggers.Logger {
- return &DistinctLogger{m: make(map[string]bool), Logger: logger}
-}
-
-// NewDistinctWarnLogger creates a new DistinctLogger that logs WARNs
-func NewDistinctWarnLogger() loggers.Logger {
- return &DistinctLogger{m: make(map[string]bool), Logger: loggers.NewWarningLogger()}
-}
-
-var (
- // DistinctErrorLog can be used to avoid spamming the logs with errors.
- DistinctErrorLog = NewDistinctErrorLogger()
-
- // DistinctWarnLog can be used to avoid spamming the logs with warnings.
- DistinctWarnLog = NewDistinctWarnLogger()
-)
-
-// InitLoggers resets the global distinct loggers.
-func InitLoggers() {
- DistinctErrorLog.Reset()
- DistinctWarnLog.Reset()
-}
-
// Deprecated informs about a deprecation, but only once for a given set of arguments' values.
// If the err flag is enabled, it logs as an ERROR (will exit with -1) and the text will
// point at the next Hugo release.
@@ -398,13 +259,9 @@ func InitLoggers() {
// plenty of time to fix their templates.
func Deprecated(item, alternative string, err bool) {
if err {
- DistinctErrorLog.Errorf("%s is deprecated and will be removed in Hugo %s. %s", item, hugo.CurrentVersion.Next().ReleaseVersion(), alternative)
+ loggers.Log().Errorf("%s is deprecated and will be removed in Hugo %s. %s", item, hugo.CurrentVersion.Next().ReleaseVersion(), alternative)
} else {
- var warnPanicMessage string
- if !loggers.PanicOnWarning.Load() {
- warnPanicMessage = "\n\nRe-run Hugo with the flag --panicOnWarning to get a better error message."
- }
- DistinctWarnLog.Warnf("%s is deprecated and will be removed in a future release. %s%s", item, alternative, warnPanicMessage)
+ loggers.Log().Warnf("%s is deprecated and will be removed in a future release. %s%s", item, alternative)
}
}
diff --git a/helpers/general_test.go b/helpers/general_test.go
index 9b2e4fc58..827411027 100644
--- a/helpers/general_test.go
+++ b/helpers/general_test.go
@@ -18,9 +18,7 @@ import (
"reflect"
"strings"
"testing"
- "time"
- "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/helpers"
qt "github.com/frankban/quicktest"
@@ -55,60 +53,6 @@ func TestResolveMarkup(t *testing.T) {
}
}
-func TestDistinctLoggerDoesNotLockOnWarningPanic(t *testing.T) {
- // Testing to make sure logger mutex doesn't lock if warnings cause panics.
- // func Warnf() of DistinctLogger is defined in general.go
- l := helpers.NewDistinctLogger(loggers.NewWarningLogger())
-
- // Set PanicOnWarning to true to reproduce issue 9380
- // Ensure global variable loggers.PanicOnWarning is reset to old value after test
- if !loggers.PanicOnWarning.Load() {
- loggers.PanicOnWarning.Store(true)
- defer func() {
- loggers.PanicOnWarning.Store(false)
- }()
- }
-
- // Establish timeout in case a lock occurs:
- timeIsUp := make(chan bool)
- timeOutSeconds := 1
- go func() {
- time.Sleep(time.Second * time.Duration(timeOutSeconds))
- timeIsUp <- true
- }()
-
- // Attempt to run multiple logging threads in parallel
- counterC := make(chan int)
- goroutines := 5
-
- for i := 0; i < goroutines; i++ {
- go func() {
- defer func() {
- // Intentional panic successfully recovered - notify counter channel
- recover()
- counterC <- 1
- }()
-
- l.Warnf("Placeholder template message: %v", "In this test, logging a warning causes a panic.")
- }()
- }
-
- // All goroutines should complete before timeout
- var counter int
- for {
- select {
- case <-counterC:
- counter++
- if counter == goroutines {
- return
- }
- case <-timeIsUp:
- t.Errorf("Unable to log warnings with --panicOnWarning within alloted time of: %v seconds. Investigate possible mutex locking on panic in distinct warning logger.", timeOutSeconds)
- return
- }
- }
-}
-
func TestFirstUpper(t *testing.T) {
for i, this := range []struct {
in string
diff --git a/helpers/testhelpers_test.go b/helpers/testhelpers_test.go
index be8983fdb..6c5f62488 100644
--- a/helpers/testhelpers_test.go
+++ b/helpers/testhelpers_test.go
@@ -23,7 +23,7 @@ func newTestPathSpecFromCfgAndLang(cfg config.Provider, lang string) *helpers.Pa
}
}
fs := hugofs.NewFrom(mfs, conf.BaseConfig())
- ps, err := helpers.NewPathSpec(fs, conf, loggers.NewErrorLogger())
+ ps, err := helpers.NewPathSpec(fs, conf, loggers.NewDefault())
if err != nil {
panic(err)
}
@@ -41,7 +41,7 @@ func newTestPathSpec(configKeyValues ...any) *helpers.PathSpec {
func newTestContentSpec(cfg config.Provider) *helpers.ContentSpec {
fs := afero.NewMemMapFs()
conf := testconfig.GetTestConfig(fs, cfg)
- spec, err := helpers.NewContentSpec(conf, loggers.NewErrorLogger(), fs, nil)
+ spec, err := helpers.NewContentSpec(conf, loggers.NewDefault(), fs, nil)
if err != nil {
panic(err)
}
diff --git a/hugofs/nosymlink_fs.go b/hugofs/nosymlink_fs.go
index d3cad5e74..af559844f 100644
--- a/hugofs/nosymlink_fs.go
+++ b/hugofs/nosymlink_fs.go
@@ -19,7 +19,6 @@ import (
"path/filepath"
"github.com/gohugoio/hugo/common/loggers"
-
"github.com/spf13/afero"
)
diff --git a/hugofs/nosymlink_test.go b/hugofs/nosymlink_test.go
index e00dcf1a8..d0a8baaaa 100644
--- a/hugofs/nosymlink_test.go
+++ b/hugofs/nosymlink_test.go
@@ -18,8 +18,8 @@ import (
"path/filepath"
"testing"
+ "github.com/bep/logg"
"github.com/gohugoio/hugo/common/loggers"
-
"github.com/gohugoio/hugo/htesting"
"github.com/spf13/afero"
@@ -64,11 +64,11 @@ func TestNoSymlinkFs(t *testing.T) {
blogDir := filepath.Join(workDir, "blog")
blogFile1 := filepath.Join(blogDir, "a.txt")
- logger := loggers.NewWarningLogger()
+ logger := loggers.NewDefault()
for _, bfs := range []afero.Fs{NewBaseFileDecorator(Os), Os} {
for _, allowFiles := range []bool{false, true} {
- logger.LogCounters().WarnCounter.Reset()
+ logger.Reset()
fs := NewNoSymlinkFs(bfs, logger, allowFiles)
ls := fs.(afero.Lstater)
symlinkedDir := filepath.Join(workDir, "symlinkdedir")
@@ -139,7 +139,7 @@ func TestNoSymlinkFs(t *testing.T) {
_, err = f.Readdir(-1)
c.Assert(err, qt.IsNil)
f.Close()
- c.Assert(logger.LogCounters().WarnCounter.Count(), qt.Equals, uint64(1))
+ c.Assert(logger.LoggCount(logg.LevelWarn), qt.Equals, 1)
}
}
diff --git a/hugofs/walk.go b/hugofs/walk.go
index e847174c6..e883f892e 100644
--- a/hugofs/walk.go
+++ b/hugofs/walk.go
@@ -86,7 +86,7 @@ func NewWalkway(cfg WalkwayConfig) *Walkway {
logger := cfg.Logger
if logger == nil {
- logger = loggers.NewWarningLogger()
+ logger = loggers.NewDefault()
}
return &Walkway{
diff --git a/hugolib/alias.go b/hugolib/alias.go
index d10f140bd..5165edb04 100644
--- a/hugolib/alias.go
+++ b/hugolib/alias.go
@@ -25,7 +25,6 @@ import (
"strings"
"github.com/gohugoio/hugo/common/loggers"
-
"github.com/gohugoio/hugo/output"
"github.com/gohugoio/hugo/publisher"
"github.com/gohugoio/hugo/resources/page"
diff --git a/hugolib/alias_test.go b/hugolib/alias_test.go
index 124c9f4ca..e03107ada 100644
--- a/hugolib/alias_test.go
+++ b/hugolib/alias_test.go
@@ -18,9 +18,8 @@ import (
"runtime"
"testing"
- "github.com/gohugoio/hugo/common/loggers"
-
qt "github.com/frankban/quicktest"
+ "github.com/gohugoio/hugo/common/loggers"
)
const pageWithAlias = `---
@@ -117,7 +116,7 @@ func TestAliasTemplate(t *testing.T) {
}
func TestTargetPathHTMLRedirectAlias(t *testing.T) {
- h := newAliasHandler(nil, loggers.NewErrorLogger(), false)
+ h := newAliasHandler(nil, loggers.NewDefault(), false)
errIsNilForThisOS := runtime.GOOS != "windows"
diff --git a/hugolib/config_test.go b/hugolib/config_test.go
index 5a3087207..02d25fa9b 100644
--- a/hugolib/config_test.go
+++ b/hugolib/config_test.go
@@ -20,6 +20,7 @@ import (
"strings"
"testing"
+ "github.com/bep/logg"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/config/allconfig"
@@ -933,7 +934,7 @@ LanguageCode: {{ eq site.LanguageCode site.Language.LanguageCode }}|{{ site.Lang
).Build()
{
- b.Assert(b.H.Log.LogCounters().WarnCounter.Count(), qt.Equals, uint64(2))
+ b.Assert(b.H.Log.LoggCount(logg.LevelWarn), qt.Equals, 1)
}
b.AssertFileContent("public/index.html", `
AllPages: 4|
diff --git a/hugolib/filesystems/basefs.go b/hugolib/filesystems/basefs.go
index ed0d36de1..d5c94799d 100644
--- a/hugolib/filesystems/basefs.go
+++ b/hugolib/filesystems/basefs.go
@@ -29,9 +29,9 @@ import (
"github.com/gohugoio/hugo/hugofs/glob"
"github.com/gohugoio/hugo/common/herrors"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/types"
- "github.com/gohugoio/hugo/common/loggers"
"github.com/rogpeppe/go-internal/lockedfile"
"github.com/gohugoio/hugo/hugofs/files"
@@ -471,7 +471,7 @@ var counter int
func NewBase(p *paths.Paths, logger loggers.Logger, options ...func(*BaseFs) error) (*BaseFs, error) {
fs := p.Fs
if logger == nil {
- logger = loggers.NewWarningLogger()
+ logger = loggers.NewDefault()
}
publishFs := hugofs.NewBaseFileDecorator(fs.PublishDir)
diff --git a/hugolib/hugo_modules_test.go b/hugolib/hugo_modules_test.go
index ee4ef798a..3353f508e 100644
--- a/hugolib/hugo_modules_test.go
+++ b/hugolib/hugo_modules_test.go
@@ -22,16 +22,16 @@ import (
"testing"
"time"
+ "github.com/bep/logg"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/modules/npm"
- "github.com/gohugoio/hugo/common/loggers"
-
"github.com/spf13/afero"
"github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/common/hugo"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/hugofs"
@@ -646,14 +646,14 @@ min_version = 0.55.0
`)
- logger := loggers.NewWarningLogger()
+ logger := loggers.NewDefault()
b.WithLogger(logger)
b.Build(BuildCfg{})
c := qt.New(t)
- c.Assert(logger.LogCounters().WarnCounter.Count(), qt.Equals, uint64(3))
+ c.Assert(logger.LoggCount(logg.LevelWarn), qt.Equals, 3)
}
func TestModulesSymlinks(t *testing.T) {
@@ -727,7 +727,7 @@ weight = 2
`
b := newTestSitesBuilder(t).WithNothingAdded().WithWorkingDir(workingDir)
- b.WithLogger(loggers.NewErrorLogger())
+ b.WithLogger(loggers.NewDefault())
b.Fs = fs
b.WithConfigFile("toml", config)
diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go
index 290eebe82..6ffef6fa8 100644
--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -23,6 +23,7 @@ import (
"sync"
"sync/atomic"
+ "github.com/bep/logg"
"github.com/gohugoio/hugo/config/allconfig"
"github.com/gohugoio/hugo/hugofs/glob"
@@ -42,7 +43,6 @@ import (
"github.com/gohugoio/hugo/source"
"github.com/gohugoio/hugo/common/herrors"
- "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/lazy"
@@ -265,7 +265,7 @@ func (h *HugoSites) NumLogErrors() int {
if h == nil {
return 0
}
- return int(h.Log.LogCounters().ErrorCounter.Count())
+ return h.Log.LoggCount(logg.LevelError)
}
func (h *HugoSites) PrintProcessingStats(w io.Writer) {
@@ -352,10 +352,8 @@ func (h *HugoSites) reset(config *BuildCfg) {
// resetLogs resets the log counters etc. Used to do a new build on the same sites.
func (h *HugoSites) resetLogs() {
h.Log.Reset()
- loggers.GlobalErrorCounter.Reset()
for _, s := range h.Sites {
s.Deps.Log.Reset()
- s.Deps.LogDistinct.Reset()
}
}
diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go
index c801ae3df..d90d9ce7b 100644
--- a/hugolib/hugo_sites_build.go
+++ b/hugolib/hugo_sites_build.go
@@ -22,6 +22,7 @@ import (
"strings"
"time"
+ "github.com/bep/logg"
"github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/publisher"
@@ -65,6 +66,8 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
defer unlock()
}
+ infol := h.Log.InfoCommand("build")
+
errCollector := h.StartErrorCollector()
errs := make(chan error)
@@ -120,11 +123,11 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
return nil
}
- if err := h.process(conf, init, events...); err != nil {
+ if err := h.process(infol, conf, init, events...); err != nil {
return fmt.Errorf("process: %w", err)
}
- if err := h.assemble(conf); err != nil {
+ if err := h.assemble(infol, conf); err != nil {
return fmt.Errorf("assemble: %w", err)
}
@@ -137,10 +140,10 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
}
if prepareErr == nil {
- if err := h.render(conf); err != nil {
+ if err := h.render(infol, conf); err != nil {
h.SendError(fmt.Errorf("render: %w", err))
}
- if err := h.postProcess(); err != nil {
+ if err := h.postProcess(infol); err != nil {
h.SendError(fmt.Errorf("postProcess: %w", err))
}
}
@@ -164,7 +167,7 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
return err
}
- errorCount := h.Log.LogCounters().ErrorCounter.Count()
+ errorCount := h.Log.LoggCount(logg.LevelError)
if errorCount > 0 {
return fmt.Errorf("logged %d error(s)", errorCount)
}
@@ -195,13 +198,12 @@ func (h *HugoSites) initRebuild(config *BuildCfg) error {
h.reset(config)
h.resetLogs()
- helpers.InitLoggers()
return nil
}
-func (h *HugoSites) process(config *BuildCfg, init func(config *BuildCfg) error, events ...fsnotify.Event) error {
- defer h.timeTrack(time.Now(), "process")
+func (h *HugoSites) process(l logg.LevelLogger, config *BuildCfg, init func(config *BuildCfg) error, events ...fsnotify.Event) error {
+ defer h.timeTrack(l, time.Now(), "process")
// We should probably refactor the Site and pull up most of the logic from there to here,
// but that seems like a daunting task.
@@ -218,8 +220,8 @@ func (h *HugoSites) process(config *BuildCfg, init func(config *BuildCfg) error,
return firstSite.process(*config)
}
-func (h *HugoSites) assemble(bcfg *BuildCfg) error {
- defer h.timeTrack(time.Now(), "assemble")
+func (h *HugoSites) assemble(l logg.LevelLogger, bcfg *BuildCfg) error {
+ defer h.timeTrack(l, time.Now(), "assemble")
if !bcfg.whatChanged.source {
return nil
@@ -236,13 +238,13 @@ func (h *HugoSites) assemble(bcfg *BuildCfg) error {
return nil
}
-func (h *HugoSites) timeTrack(start time.Time, name string) {
+func (h *HugoSites) timeTrack(l logg.LevelLogger, start time.Time, name string) {
elapsed := time.Since(start)
- h.Log.Infof("%s in %v ms\n", name, int(1000*elapsed.Seconds()))
+ l.WithField("step", name).WithField("duration", elapsed).Logf("running")
}
-func (h *HugoSites) render(config *BuildCfg) error {
- defer h.timeTrack(time.Now(), "render")
+func (h *HugoSites) render(l logg.LevelLogger, config *BuildCfg) error {
+ defer h.timeTrack(l, time.Now(), "render")
if _, err := h.init.layouts.Do(context.Background()); err != nil {
return err
}
@@ -312,8 +314,8 @@ func (h *HugoSites) render(config *BuildCfg) error {
return nil
}
-func (h *HugoSites) postProcess() error {
- defer h.timeTrack(time.Now(), "postProcess")
+func (h *HugoSites) postProcess(l logg.LevelLogger) error {
+ defer h.timeTrack(l, time.Now(), "postProcess")
// Make sure to write any build stats to disk first so it's available
// to the post processors.
diff --git a/hugolib/hugo_sites_build_test.go b/hugolib/hugo_sites_build_test.go
index b2798c863..3c82a60b6 100644
--- a/hugolib/hugo_sites_build_test.go
+++ b/hugolib/hugo_sites_build_test.go
@@ -1394,7 +1394,7 @@ other = %q
}
func TestRebuildOnAssetChange(t *testing.T) {
- b := newTestSitesBuilder(t).Running().WithLogger(loggers.NewInfoLogger())
+ b := newTestSitesBuilder(t).Running().WithLogger(loggers.NewDefault())
b.WithTemplatesAdded("index.html", `
{{ (resources.Get "data.json").Content }}
`)
diff --git a/hugolib/integrationtest_builder.go b/hugolib/integrationtest_builder.go
index 0daa766ac..cb34cb28b 100644
--- a/hugolib/integrationtest_builder.go
+++ b/hugolib/integrationtest_builder.go
@@ -13,7 +13,7 @@ import (
"sync"
"testing"
- jww "github.com/spf13/jwalterweatherman"
+ "github.com/bep/logg"
qt "github.com/frankban/quicktest"
"github.com/fsnotify/fsnotify"
@@ -292,11 +292,9 @@ func (s *IntegrationTestBuilder) initBuilder() error {
}
if s.Cfg.LogLevel == 0 {
- s.Cfg.LogLevel = jww.LevelWarn
+ s.Cfg.LogLevel = logg.LevelWarn
}
- logger := loggers.NewBasicLoggerForWriter(s.Cfg.LogLevel, &s.logBuff)
-
isBinaryRe := regexp.MustCompile(`^(.*)(\.png|\.jpg)$`)
const dataSourceFilenamePrefix = "sourcefilename:"
@@ -350,7 +348,7 @@ func (s *IntegrationTestBuilder) initBuilder() error {
Flags: flags,
ConfigDir: configDir,
Fs: afs,
- Logger: logger,
+ Logger: loggers.NewDefault(),
Environ: s.Cfg.Environ,
},
)
@@ -364,7 +362,7 @@ func (s *IntegrationTestBuilder) initBuilder() error {
s.Assert(err, qt.IsNil)
- depsCfg := deps.DepsCfg{Configs: res, Fs: fs, Logger: logger}
+ depsCfg := deps.DepsCfg{Configs: res, Fs: fs, LogLevel: s.Cfg.LogLevel, LogOut: &s.logBuff}
sites, err := NewHugoSites(depsCfg)
if err != nil {
initErr = err
@@ -528,7 +526,7 @@ type IntegrationTestConfig struct {
// Will print the log buffer after the build
Verbose bool
- LogLevel jww.Threshold
+ LogLevel logg.Level
// Whether it needs the real file system (e.g. for js.Build tests).
NeedsOsFS bool
diff --git a/hugolib/mount_filters_test.go b/hugolib/mount_filters_test.go
index 688cf2558..4f6a448d2 100644
--- a/hugolib/mount_filters_test.go
+++ b/hugolib/mount_filters_test.go
@@ -20,7 +20,6 @@ import (
"testing"
"github.com/gohugoio/hugo/common/loggers"
-
"github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/htesting"
@@ -39,7 +38,7 @@ func TestMountFilters(t *testing.T) {
for _, component := range files.ComponentFolders {
b.Assert(os.MkdirAll(filepath.Join(workingDir, component), 0777), qt.IsNil)
}
- b.WithWorkingDir(workingDir).WithLogger(loggers.NewInfoLogger())
+ b.WithWorkingDir(workingDir).WithLogger(loggers.NewDefault())
b.WithConfigFile("toml", fmt.Sprintf(`
workingDir = %q
diff --git a/hugolib/page__new.go b/hugolib/page__new.go
index e9a8b1a50..14db28c3d 100644
--- a/hugolib/page__new.go
+++ b/hugolib/page__new.go
@@ -99,7 +99,7 @@ func newPageFromMeta(
meta map[string]any,
metaProvider *pageMeta) (*pageState, error) {
if metaProvider.f == nil {
- metaProvider.f = page.NewZeroFile(metaProvider.s.LogDistinct)
+ metaProvider.f = page.NewZeroFile(metaProvider.s.Log)
}
ps, err := newPageBase(metaProvider)
diff --git a/hugolib/page_test.go b/hugolib/page_test.go
index 79b6401dc..6b817551e 100644
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -17,7 +17,6 @@ import (
"context"
"fmt"
"html/template"
- "os"
"path/filepath"
"strings"
"testing"
@@ -36,7 +35,6 @@ import (
"github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/hugo/resources/resource"
- "github.com/spf13/jwalterweatherman"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/deps"
@@ -739,7 +737,7 @@ Here is the last report for commits in the year 2016. It covers hrev50718-hrev50
func TestRenderStringForRegularPageTranslations(t *testing.T) {
c := qt.New(t)
b := newTestSitesBuilder(t)
- b.WithLogger(loggers.NewBasicLoggerForWriter(jwalterweatherman.LevelError, os.Stderr))
+ b.WithLogger(loggers.NewDefault())
b.WithConfigFile("toml",
`baseurl = "https://example.org/"
@@ -800,7 +798,7 @@ home = ["HTML", "JSON"]`)
// Issue 8919
func TestContentProviderWithCustomOutputFormat(t *testing.T) {
b := newTestSitesBuilder(t)
- b.WithLogger(loggers.NewBasicLoggerForWriter(jwalterweatherman.LevelDebug, os.Stderr))
+ b.WithLogger(loggers.NewDefault())
b.WithConfigFile("toml", `baseURL = 'http://example.org/'
title = 'My New Hugo Site'
@@ -1437,7 +1435,7 @@ Content:{{ .Content }}
// https://github.com/gohugoio/hugo/issues/5781
func TestPageWithZeroFile(t *testing.T) {
- newTestSitesBuilder(t).WithLogger(loggers.NewWarningLogger()).WithSimpleConfigFile().
+ newTestSitesBuilder(t).WithLogger(loggers.NewDefault()).WithSimpleConfigFile().
WithTemplatesAdded("index.html", "{{ .File.Filename }}{{ with .File }}{{ .Dir }}{{ end }}").Build(BuildCfg{})
}
diff --git a/hugolib/pagebundler_test.go b/hugolib/pagebundler_test.go
index 2ec3718f0..51ce82526 100644
--- a/hugolib/pagebundler_test.go
+++ b/hugolib/pagebundler_test.go
@@ -20,6 +20,9 @@ import (
"path"
"path/filepath"
"regexp"
+
+ "github.com/gohugoio/hugo/common/loggers"
+
"strings"
"testing"
@@ -31,7 +34,6 @@ import (
"github.com/gohugoio/hugo/hugofs"
- "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/hugo/htesting"
@@ -93,7 +95,7 @@ func TestPageBundlerSiteRegular(t *testing.T) {
c.Assert(err, qt.IsNil)
- b := newTestSitesBuilderFromDepsCfg(c, deps.DepsCfg{Logger: loggers.NewErrorLogger(), Fs: fs, Configs: configs}).WithNothingAdded()
+ b := newTestSitesBuilderFromDepsCfg(c, deps.DepsCfg{Fs: fs, Configs: configs}).WithNothingAdded()
b.Build(BuildCfg{})
@@ -1044,7 +1046,7 @@ title: %q
}
b := newTestSitesBuilder(t).WithConfigFile("toml", config)
- b.WithLogger(loggers.NewWarningLogger())
+ b.WithLogger(loggers.NewDefault())
b.WithTemplates("_default/list.html", `{{ range .Site.Pages }}
{{ .Kind }}|{{ .Path }}|{{ with .CurrentSection }}CurrentSection: {{ .Path }}{{ end }}|{{ .RelPermalink }}{{ end }}
@@ -1215,7 +1217,7 @@ title: %q
}
b := newTestSitesBuilder(t).WithConfigFile("toml", config)
- b.WithLogger(loggers.NewWarningLogger())
+ b.WithLogger(loggers.NewDefault())
b.WithTemplates("_default/single.html", `{{ range .Resources }}
{{ .ResourceType }}|{{ .Title }}|
diff --git a/hugolib/pages_capture.go b/hugolib/pages_capture.go
index 8a2b875ea..c57c707de 100644
--- a/hugolib/pages_capture.go
+++ b/hugolib/pages_capture.go
@@ -21,6 +21,7 @@ import (
"reflect"
"github.com/gohugoio/hugo/common/herrors"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/parser/pageparser"
@@ -29,7 +30,6 @@ import (
"github.com/gohugoio/hugo/source"
- "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/hugofs"
"github.com/spf13/afero"
)
diff --git a/hugolib/pages_capture_test.go b/hugolib/pages_capture_test.go
index 8c1023a15..c771d30ee 100644
--- a/hugolib/pages_capture_test.go
+++ b/hugolib/pages_capture_test.go
@@ -53,7 +53,7 @@ func TestPagesCapture(t *testing.T) {
t.Run("Collect", func(t *testing.T) {
c := qt.New(t)
proc := &testPagesCollectorProcessor{}
- coll := newPagesCollector(sourceSpec, nil, loggers.NewErrorLogger(), nil, proc)
+ coll := newPagesCollector(sourceSpec, nil, loggers.NewDefault(), nil, proc)
c.Assert(coll.Collect(), qt.IsNil)
// 2 bundles, 3 pages.
c.Assert(len(proc.items), qt.Equals, 5)
diff --git a/hugolib/renderstring_test.go b/hugolib/renderstring_test.go
index af66156e6..e0a4cd036 100644
--- a/hugolib/renderstring_test.go
+++ b/hugolib/renderstring_test.go
@@ -16,6 +16,7 @@ package hugolib
import (
"testing"
+ "github.com/bep/logg"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/loggers"
)
@@ -80,13 +81,13 @@ func TestRenderStringOnListPage(t *testing.T) {
// Issue 9433
func TestRenderStringOnPageNotBackedByAFile(t *testing.T) {
t.Parallel()
- logger := loggers.NewWarningLogger()
+ logger := loggers.NewDefault()
b := newTestSitesBuilder(t).WithLogger(logger).WithConfigFile("toml", `
disableKinds = ["page", "section", "taxonomy", "term"]
`)
b.WithTemplates("index.html", `{{ .RenderString "**Hello**" }}`).WithContent("p1.md", "")
b.BuildE(BuildCfg{})
- b.Assert(int(logger.LogCounters().WarnCounter.Count()), qt.Equals, 0)
+ b.Assert(logger.LoggCount(logg.LevelWarn), qt.Equals, 0)
}
func TestRenderStringWithShortcode(t *testing.T) {
diff --git a/hugolib/resource_chain_test.go b/hugolib/resource_chain_test.go
index 823f7db45..da90634aa 100644
--- a/hugolib/resource_chain_test.go
+++ b/hugolib/resource_chain_test.go
@@ -588,7 +588,7 @@ XML: {{ $xml.body }}
}
t.Parallel()
- b := newTestSitesBuilder(t).WithLogger(loggers.NewErrorLogger())
+ b := newTestSitesBuilder(t).WithLogger(loggers.NewDefault())
b.WithContent("_index.md", `
---
title: Home
diff --git a/hugolib/site.go b/hugolib/site.go
index 035f543ee..fca8bca75 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -17,7 +17,6 @@ import (
"context"
"fmt"
"io"
- "log"
"mime"
"net/url"
"path"
@@ -27,6 +26,7 @@ import (
"strings"
"time"
+ "github.com/bep/logg"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/hugio"
@@ -285,7 +285,7 @@ func (s *Site) isEnabled(kind string) bool {
type siteRefLinker struct {
s *Site
- errorLogger *log.Logger
+ errorLogger logg.LevelLogger
notFoundURL string
}
@@ -302,11 +302,11 @@ func newSiteRefLinker(s *Site) (siteRefLinker, error) {
func (s siteRefLinker) logNotFound(ref, what string, p page.Page, position text.Position) {
if position.IsValid() {
- s.errorLogger.Printf("[%s] REF_NOT_FOUND: Ref %q: %s: %s", s.s.Lang(), ref, position.String(), what)
+ s.errorLogger.Logf("[%s] REF_NOT_FOUND: Ref %q: %s: %s", s.s.Lang(), ref, position.String(), what)
} else if p == nil {
- s.errorLogger.Printf("[%s] REF_NOT_FOUND: Ref %q: %s", s.s.Lang(), ref, what)
+ s.errorLogger.Logf("[%s] REF_NOT_FOUND: Ref %q: %s", s.s.Lang(), ref, what)
} else {
- s.errorLogger.Printf("[%s] REF_NOT_FOUND: Ref %q from page %q: %s", s.s.Lang(), ref, p.Pathc(), what)
+ s.errorLogger.Logf("[%s] REF_NOT_FOUND: Ref %q from page %q: %s", s.s.Lang(), ref, p.Pathc(), what)
}
}
@@ -507,9 +507,6 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro
i18nChanged bool
sourceFilesChanged = make(map[string]bool)
-
- // prevent spamming the log on changes
- logger = helpers.NewDistinctErrorLogger()
)
var cacheBusters []func(string) bool
@@ -531,7 +528,7 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro
switch id.Type {
case files.ComponentFolderContent:
- logger.Println("Source changed", ev)
+ s.Log.Println("Source changed", ev)
sourceChanged = append(sourceChanged, ev)
case files.ComponentFolderLayouts:
tmplChanged = true
@@ -539,16 +536,16 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro
tmplAdded = true
}
if tmplAdded {
- logger.Println("Template added", ev)
+ s.Log.Println("Template added", ev)
} else {
- logger.Println("Template changed", ev)
+ s.Log.Println("Template changed", ev)
}
case files.ComponentFolderData:
- logger.Println("Data changed", ev)
+ s.Log.Println("Data changed", ev)
dataChanged = true
case files.ComponentFolderI18n:
- logger.Println("i18n changed", ev)
+ s.Log.Println("i18n changed", ev)
i18nChanged = true
}
diff --git a/hugolib/site_new.go b/hugolib/site_new.go
index 911414121..c6c5cd2a3 100644
--- a/hugolib/site_new.go
+++ b/hugolib/site_new.go
@@ -18,10 +18,12 @@ import (
"errors"
"fmt"
"html/template"
+ "os"
"sort"
"time"
radix "github.com/armon/go-radix"
+ "github.com/bep/logg"
"github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/maps"
@@ -100,19 +102,41 @@ func (s *Site) Debug() {
func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
conf := cfg.Configs.GetFirstLanguageConfig()
- logger := cfg.Logger
- if logger == nil {
- logger = loggers.NewErrorLogger()
+ var logger loggers.Logger
+ if cfg.TestLogger != nil {
+ logger = cfg.TestLogger
+ } else {
+ var logHookLast func(e *logg.Entry) error
+ if cfg.Configs.Base.PanicOnWarning {
+ logHookLast = loggers.PanicOnWarningHook
+ }
+ if cfg.LogOut == nil {
+ cfg.LogOut = os.Stdout
+ }
+ if cfg.LogLevel == 0 {
+ cfg.LogLevel = logg.LevelWarn
+ }
+
+ logOpts := loggers.Options{
+ Level: cfg.LogLevel,
+ Distinct: true, // This will drop duplicate log warning and errors.
+ HandlerPost: logHookLast,
+ Stdout: cfg.LogOut,
+ Stderr: cfg.LogOut,
+ StoreErrors: conf.Running(),
+ SuppresssStatements: conf.IgnoredErrors(),
+ }
+ logger = loggers.New(logOpts)
}
- ignorableLogger := loggers.NewIgnorableLogger(logger, conf.IgnoredErrors())
firstSiteDeps := &deps.Deps{
Fs: cfg.Fs,
- Log: ignorableLogger,
+ Log: logger,
Conf: conf,
TemplateProvider: tplimpl.DefaultTemplateProvider,
TranslationProvider: i18n.NewTranslationProvider(),
}
+
if err := firstSiteDeps.Init(); err != nil {
return nil, err
}
@@ -128,7 +152,7 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
k := language.Lang
conf := confm.LanguageConfigMap[k]
- frontmatterHandler, err := pagemeta.NewFrontmatterHandler(cfg.Logger, conf.Frontmatter)
+ frontmatterHandler, err := pagemeta.NewFrontmatterHandler(firstSiteDeps.Log, conf.Frontmatter)
if err != nil {
return nil, err
}
@@ -209,6 +233,7 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
}
return h, err
+
}
func newHugoSitesNew(cfg deps.DepsCfg, d *deps.Deps, sites []*Site) (*HugoSites, error) {
diff --git a/hugolib/site_render.go b/hugolib/site_render.go
index f076b98dd..c4c3f389b 100644
--- a/hugolib/site_render.go
+++ b/hugolib/site_render.go
@@ -181,7 +181,7 @@ func (s *Site) logMissingLayout(name, layout, kind, outputFormat string) {
msg += ": " + errMsg
- log.Printf(msg, args...)
+ log.Logf(msg, args...)
}
// renderPaginator must be run after the owning Page has been rendered.
diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go
index 7cf1a5596..e1fd537a8 100644
--- a/hugolib/testhelpers_test.go
+++ b/hugolib/testhelpers_test.go
@@ -33,6 +33,7 @@ import (
"github.com/fsnotify/fsnotify"
"github.com/gohugoio/hugo/common/hexec"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/deps"
@@ -47,7 +48,6 @@ import (
"github.com/gohugoio/hugo/resources/resource"
qt "github.com/frankban/quicktest"
- "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/hugofs"
)
@@ -552,7 +552,7 @@ func (s *sitesBuilder) CreateSitesE() error {
if depsCfg.Configs.IsZero() {
depsCfg.Configs = s.Configs
}
- depsCfg.Logger = s.logger
+ depsCfg.TestLogger = s.logger
sites, err := NewHugoSites(depsCfg)
diff --git a/langs/i18n/i18n.go b/langs/i18n/i18n.go
index a9b7b4c97..1ebd039cd 100644
--- a/langs/i18n/i18n.go
+++ b/langs/i18n/i18n.go
@@ -24,7 +24,6 @@ import (
"github.com/gohugoio/hugo/common/hreflect"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config"
- "github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/go-i18n/v2/i18n"
@@ -32,8 +31,6 @@ import (
type translateFunc func(ctx context.Context, translationID string, templateData any) string
-var i18nWarningLogger = helpers.NewDistinctErrorLogger()
-
// Translator handles i18n translations.
type Translator struct {
translateFuncs map[string]translateFunc
@@ -123,7 +120,7 @@ func (t Translator) initFuncs(bndl *i18n.Bundle) {
}
if t.cfg.LogI18nWarnings() {
- i18nWarningLogger.Printf("i18n|MISSING_TRANSLATION|%s|%s", currentLangStr, translationID)
+ t.logger.Warnf("i18n|MISSING_TRANSLATION|%s|%s", currentLangStr, translationID)
}
if enableMissingTranslationPlaceholders {
diff --git a/langs/i18n/i18n_test.go b/langs/i18n/i18n_test.go
index 1ac6144dd..8629c35fc 100644
--- a/langs/i18n/i18n_test.go
+++ b/langs/i18n/i18n_test.go
@@ -19,12 +19,13 @@ import (
"path/filepath"
"testing"
+ "github.com/bep/logg"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/config/testconfig"
"github.com/gohugoio/hugo/tpl/tplimpl"
- "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/resources/page"
"github.com/spf13/afero"
@@ -34,7 +35,7 @@ import (
"github.com/gohugoio/hugo/config"
)
-var logger = loggers.NewErrorLogger()
+var logger = loggers.NewDefault()
type i18nTest struct {
name string
@@ -406,7 +407,7 @@ other = "{{ . }} miesiąca"
for _, variant := range test.variants {
c.Assert(f(ctx, test.id, variant.Key), qt.Equals, variant.Value, qt.Commentf("input: %v", variant.Key))
- c.Assert(int(d.Log.LogCounters().WarnCounter.Count()), qt.Equals, 0)
+ c.Assert(d.Log.LoggCount(logg.LevelWarn), qt.Equals, 0)
}
})
diff --git a/markup/asciidocext/convert_test.go b/markup/asciidocext/convert_test.go
index cdc981263..459686139 100644
--- a/markup/asciidocext/convert_test.go
+++ b/markup/asciidocext/convert_test.go
@@ -44,7 +44,7 @@ func TestAsciidoctorDefaultArgs(t *testing.T) {
p, err := asciidocext.Provider.New(
converter.ProviderConfig{
Conf: conf,
- Logger: loggers.NewErrorLogger(),
+ Logger: loggers.NewDefault(),
},
)
c.Assert(err, qt.IsNil)
@@ -76,7 +76,7 @@ func TestAsciidoctorNonDefaultArgs(t *testing.T) {
p, err := asciidocext.Provider.New(
converter.ProviderConfig{
Conf: conf,
- Logger: loggers.NewErrorLogger(),
+ Logger: loggers.NewDefault(),
},
)
c.Assert(err, qt.IsNil)
@@ -106,7 +106,7 @@ func TestAsciidoctorDisallowedArgs(t *testing.T) {
p, err := asciidocext.Provider.New(
converter.ProviderConfig{
Conf: conf,
- Logger: loggers.NewErrorLogger(),
+ Logger: loggers.NewDefault(),
},
)
c.Assert(err, qt.IsNil)
@@ -130,7 +130,7 @@ func TestAsciidoctorArbitraryExtension(t *testing.T) {
p, err := asciidocext.Provider.New(
converter.ProviderConfig{
Conf: conf,
- Logger: loggers.NewErrorLogger(),
+ Logger: loggers.NewDefault(),
},
)
c.Assert(err, qt.IsNil)
@@ -164,7 +164,7 @@ func TestAsciidoctorDisallowedExtension(t *testing.T) {
p, err := asciidocext.Provider.New(
converter.ProviderConfig{
Conf: conf,
- Logger: loggers.NewErrorLogger(),
+ Logger: loggers.NewDefault(),
},
)
c.Assert(err, qt.IsNil)
@@ -195,7 +195,7 @@ trace = false
p, err := asciidocext.Provider.New(
converter.ProviderConfig{
Conf: conf,
- Logger: loggers.NewErrorLogger(),
+ Logger: loggers.NewDefault(),
},
)
c.Assert(err, qt.IsNil)
@@ -232,7 +232,7 @@ extensions = ["asciidoctor-html5s", "asciidoctor-diagram"]
p, err := asciidocext.Provider.New(
converter.ProviderConfig{
Conf: conf,
- Logger: loggers.NewErrorLogger(),
+ Logger: loggers.NewDefault(),
},
)
c.Assert(err, qt.IsNil)
@@ -272,7 +272,7 @@ my-attribute-name = "my value"
p, err := asciidocext.Provider.New(
converter.ProviderConfig{
Conf: conf,
- Logger: loggers.NewErrorLogger(),
+ Logger: loggers.NewDefault(),
},
)
c.Assert(err, qt.IsNil)
@@ -311,7 +311,7 @@ allow = ['asciidoctor']
p, err := asciidocext.Provider.New(
converter.ProviderConfig{
- Logger: loggers.NewErrorLogger(),
+ Logger: loggers.NewDefault(),
Conf: conf,
Exec: hexec.New(securityConfig),
},
diff --git a/markup/goldmark/convert_test.go b/markup/goldmark/convert_test.go
index 666529877..a2f7b9597 100644
--- a/markup/goldmark/convert_test.go
+++ b/markup/goldmark/convert_test.go
@@ -46,7 +46,7 @@ noclasses=false
func convert(c *qt.C, conf config.AllProvider, content string) converter.ResultRender {
pconf := converter.ProviderConfig{
- Logger: loggers.NewErrorLogger(),
+ Logger: loggers.NewDefault(),
Conf: conf,
}
@@ -442,7 +442,7 @@ LINE5
conf := testconfig.GetTestConfig(nil, cfg)
pcfg := converter.ProviderConfig{
Conf: conf,
- Logger: loggers.NewErrorLogger(),
+ Logger: loggers.NewDefault(),
}
p, err := goldmark.Provider.New(
pcfg,
diff --git a/markup/goldmark/toc_test.go b/markup/goldmark/toc_test.go
index 78811cfb4..f7f7bb7a0 100644
--- a/markup/goldmark/toc_test.go
+++ b/markup/goldmark/toc_test.go
@@ -17,12 +17,11 @@ import (
"strings"
"testing"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config/testconfig"
"github.com/gohugoio/hugo/markup/converter/hooks"
"github.com/gohugoio/hugo/markup/goldmark"
- "github.com/gohugoio/hugo/common/loggers"
-
"github.com/gohugoio/hugo/markup/converter"
qt "github.com/frankban/quicktest"
@@ -56,7 +55,7 @@ And then some.
p, err := goldmark.Provider.New(
converter.ProviderConfig{
Conf: testconfig.GetTestConfig(nil, nil),
- Logger: loggers.NewErrorLogger(),
+ Logger: loggers.NewDefault(),
})
c.Assert(err, qt.IsNil)
conv, err := p.New(converter.DocumentContext{})
@@ -86,12 +85,12 @@ func TestEscapeToc(t *testing.T) {
safeP, _ := goldmark.Provider.New(
converter.ProviderConfig{
Conf: safeConf(),
- Logger: loggers.NewErrorLogger(),
+ Logger: loggers.NewDefault(),
})
unsafeP, _ := goldmark.Provider.New(
converter.ProviderConfig{
Conf: unsafeConf(),
- Logger: loggers.NewErrorLogger(),
+ Logger: loggers.NewDefault(),
})
safeConv, _ := safeP.New(converter.DocumentContext{})
unsafeConv, _ := unsafeP.New(converter.DocumentContext{})
diff --git a/markup/org/convert.go b/markup/org/convert.go
index 802b9aa5a..141269f1d 100644
--- a/markup/org/convert.go
+++ b/markup/org/convert.go
@@ -16,6 +16,7 @@ package org
import (
"bytes"
+ "log"
"github.com/gohugoio/hugo/identity"
@@ -46,7 +47,7 @@ type orgConverter struct {
func (c *orgConverter) Convert(ctx converter.RenderContext) (converter.ResultRender, error) {
logger := c.cfg.Logger
config := org.New()
- config.Log = logger.Warn()
+ config.Log = log.Default() // TODO(bep)
config.ReadFile = func(filename string) ([]byte, error) {
return afero.ReadFile(c.cfg.ContentFs, filename)
}
diff --git a/markup/org/convert_test.go b/markup/org/convert_test.go
index 08841b2d7..1422585af 100644
--- a/markup/org/convert_test.go
+++ b/markup/org/convert_test.go
@@ -29,7 +29,7 @@ import (
func TestConvert(t *testing.T) {
c := qt.New(t)
p, err := org.Provider.New(converter.ProviderConfig{
- Logger: loggers.NewErrorLogger(),
+ Logger: loggers.NewDefault(),
Conf: testconfig.GetTestConfig(afero.NewMemMapFs(), nil),
})
c.Assert(err, qt.IsNil)
diff --git a/markup/pandoc/convert_test.go b/markup/pandoc/convert_test.go
index f549d5f4f..6a1535946 100644
--- a/markup/pandoc/convert_test.go
+++ b/markup/pandoc/convert_test.go
@@ -32,7 +32,7 @@ func TestConvert(t *testing.T) {
c := qt.New(t)
sc := security.DefaultConfig
sc.Exec.Allow = security.NewWhitelist("pandoc")
- p, err := Provider.New(converter.ProviderConfig{Exec: hexec.New(sc), Logger: loggers.NewErrorLogger()})
+ p, err := Provider.New(converter.ProviderConfig{Exec: hexec.New(sc), Logger: loggers.NewDefault()})
c.Assert(err, qt.IsNil)
conv, err := p.New(converter.DocumentContext{})
c.Assert(err, qt.IsNil)
diff --git a/markup/rst/convert_test.go b/markup/rst/convert_test.go
index 5d2882de1..9e98d0405 100644
--- a/markup/rst/convert_test.go
+++ b/markup/rst/convert_test.go
@@ -35,7 +35,7 @@ func TestConvert(t *testing.T) {
p, err := Provider.New(
converter.ProviderConfig{
- Logger: loggers.NewErrorLogger(),
+ Logger: loggers.NewDefault(),
Exec: hexec.New(sc),
})
c.Assert(err, qt.IsNil)
diff --git a/modules/client.go b/modules/client.go
index 59f6b25d3..5d8daf926 100644
--- a/modules/client.go
+++ b/modules/client.go
@@ -30,6 +30,7 @@ import (
"github.com/gohugoio/hugo/common/collections"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/hexec"
+ "github.com/gohugoio/hugo/common/loggers"
hglob "github.com/gohugoio/hugo/hugofs/glob"
@@ -39,8 +40,6 @@ import (
"github.com/gohugoio/hugo/hugofs/files"
- "github.com/gohugoio/hugo/common/loggers"
-
"github.com/gohugoio/hugo/config"
"github.com/rogpeppe/go-internal/module"
@@ -98,7 +97,7 @@ func NewClient(cfg ClientConfig) *Client {
logger := cfg.Logger
if logger == nil {
- logger = loggers.NewWarningLogger()
+ logger = loggers.NewDefault()
}
var noVendor glob.Glob
diff --git a/parser/metadecoders/decoder.go b/parser/metadecoders/decoder.go
index a65731ae4..93eb32e47 100644
--- a/parser/metadecoders/decoder.go
+++ b/parser/metadecoders/decoder.go
@@ -18,6 +18,7 @@ import (
"encoding/csv"
"encoding/json"
"fmt"
+ "log"
"regexp"
"strings"
@@ -28,7 +29,6 @@ import (
toml "github.com/pelletier/go-toml/v2"
"github.com/spf13/afero"
"github.com/spf13/cast"
- jww "github.com/spf13/jwalterweatherman"
yaml "gopkg.in/yaml.v2"
)
@@ -231,7 +231,7 @@ func parseORGDate(s string) string {
func (d Decoder) unmarshalORG(data []byte, v any) error {
config := org.New()
- config.Log = jww.WARN
+ config.Log = log.Default() // TODO(bep)
document := config.Parse(bytes.NewReader(data), "")
if document.Error != nil {
return document.Error
@@ -242,7 +242,7 @@ func (d Decoder) unmarshalORG(data []byte, v any) error {
if strings.HasSuffix(k, "[]") {
frontMatter[k[:len(k)-2]] = strings.Fields(v)
} else if k == "tags" || k == "categories" || k == "aliases" {
- jww.WARN.Printf("Please use '#+%s[]:' notation, automatic conversion is deprecated.", k)
+ log.Printf("warn: Please use '#+%s[]:' notation, automatic conversion is deprecated.", k)
frontMatter[k] = strings.Fields(v)
} else if k == "date" || k == "lastmod" || k == "publishdate" || k == "expirydate" {
frontMatter[k] = parseORGDate(v)
diff --git a/resources/page/pagemeta/page_frontmatter.go b/resources/page/pagemeta/page_frontmatter.go
index d827bfbad..98ab6b222 100644
--- a/resources/page/pagemeta/page_frontmatter.go
+++ b/resources/page/pagemeta/page_frontmatter.go
@@ -18,9 +18,9 @@ import (
"time"
"github.com/gohugoio/hugo/common/htime"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/paths"
- "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/resources/resource"
@@ -270,7 +270,7 @@ func toLowerSlice(in any) []string {
// If no logger is provided, one will be created.
func NewFrontmatterHandler(logger loggers.Logger, frontMatterConfig FrontmatterConfig) (FrontMatterHandler, error) {
if logger == nil {
- logger = loggers.NewErrorLogger()
+ logger = loggers.NewDefault()
}
allDateKeys := make(map[string]bool)
diff --git a/resources/page/testhelpers_page_test.go b/resources/page/testhelpers_page_test.go
index c462e176f..95124cb58 100644
--- a/resources/page/testhelpers_page_test.go
+++ b/resources/page/testhelpers_page_test.go
@@ -30,7 +30,7 @@ func newTestPathSpecFor(cfg config.Provider) *helpers.PathSpec {
mfs := afero.NewMemMapFs()
conf := testconfig.GetTestConfig(mfs, cfg)
fs := hugofs.NewFrom(mfs, conf.BaseConfig())
- ps, err := helpers.NewPathSpec(fs, conf, loggers.NewErrorLogger())
+ ps, err := helpers.NewPathSpec(fs, conf, loggers.NewDefault())
if err != nil {
panic(err)
}
diff --git a/resources/resource_spec.go b/resources/resource_spec.go
index 5ecb021fe..3e1b53205 100644
--- a/resources/resource_spec.go
+++ b/resources/resource_spec.go
@@ -31,6 +31,7 @@ import (
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/hexec"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/identity"
@@ -39,7 +40,6 @@ import (
"github.com/gohugoio/hugo/resources/postpub"
"github.com/gohugoio/hugo/cache/filecache"
- "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/resources/images"
"github.com/gohugoio/hugo/resources/page"
@@ -75,7 +75,7 @@ func NewSpec(
}
if logger == nil {
- logger = loggers.NewErrorLogger()
+ logger = loggers.NewDefault()
}
permalinks, err := page.NewPermalinkExpander(s.URLize, conf.Permalinks)
diff --git a/resources/resource_transformers/babel/babel.go b/resources/resource_transformers/babel/babel.go
index ff19d9dda..5f8fcb00f 100644
--- a/resources/resource_transformers/babel/babel.go
+++ b/resources/resource_transformers/babel/babel.go
@@ -122,10 +122,10 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
}
var configFile string
- logger := t.rs.Logger
+ infol := t.rs.Logger.InfoCommand(binaryName)
+ infoW := loggers.LevelLoggerToWriter(infol)
var errBuf bytes.Buffer
- infoW := loggers.LoggerToWriterWithPrefix(logger.Info(), "babel")
if t.options.Config != "" {
configFile = t.options.Config
@@ -149,7 +149,7 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
var cmdArgs []any
if configFile != "" {
- logger.Infoln("babel: use config file", configFile)
+ infol.Logf("use config file %q", configFile)
cmdArgs = []any{"--config-file", configFile}
}
diff --git a/resources/resource_transformers/babel/integration_test.go b/resources/resource_transformers/babel/integration_test.go
index 164e7fd40..44a13f103 100644
--- a/resources/resource_transformers/babel/integration_test.go
+++ b/resources/resource_transformers/babel/integration_test.go
@@ -16,8 +16,7 @@ package babel_test
import (
"testing"
- jww "github.com/spf13/jwalterweatherman"
-
+ "github.com/bep/logg"
"github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/hugolib"
)
@@ -82,7 +81,7 @@ Transpiled3: {{ $transpiled.Permalink }}
TxtarString: files,
NeedsOsFS: true,
NeedsNpmInstall: true,
- LogLevel: jww.LevelInfo,
+ LogLevel: logg.LevelInfo,
}).Build()
b.AssertLogContains("babel: Hugo Environment: production")
diff --git a/resources/resource_transformers/postcss/integration_test.go b/resources/resource_transformers/postcss/integration_test.go
index cfe5f8a2c..c920fe17d 100644
--- a/resources/resource_transformers/postcss/integration_test.go
+++ b/resources/resource_transformers/postcss/integration_test.go
@@ -16,11 +16,11 @@ package postcss_test
import (
"fmt"
"path/filepath"
+ "runtime"
"strings"
"testing"
- jww "github.com/spf13/jwalterweatherman"
-
+ "github.com/bep/logg"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/hugofs"
@@ -124,7 +124,7 @@ func TestTransformPostCSS(t *testing.T) {
T: c,
NeedsOsFS: true,
NeedsNpmInstall: true,
- LogLevel: jww.LevelInfo,
+ LogLevel: logg.LevelInfo,
WorkingDir: tempDir,
TxtarString: files,
}).Build()
@@ -148,6 +148,11 @@ func TestTransformPostCSSError(t *testing.T) {
t.Skip("Skip long running test when running locally")
}
+ if runtime.GOOS == "windows" {
+ //TODO(bep) This has started to fail on Windows with Go 1.19 on GitHub Actions for some mysterious reason.
+ t.Skip("Skip on Windows")
+ }
+
c := qt.New(t)
s, err := hugolib.NewIntegrationTestBuilder(
@@ -176,7 +181,7 @@ func TestTransformPostCSSImportError(t *testing.T) {
T: c,
NeedsOsFS: true,
NeedsNpmInstall: true,
- LogLevel: jww.LevelInfo,
+ LogLevel: logg.LevelInfo,
TxtarString: strings.ReplaceAll(postCSSIntegrationTestFiles, `@import "components/all.css";`, `@import "components/doesnotexist.css";`),
}).BuildE()
@@ -201,7 +206,7 @@ func TestTransformPostCSSImporSkipInlineImportsNotFound(t *testing.T) {
T: c,
NeedsOsFS: true,
NeedsNpmInstall: true,
- LogLevel: jww.LevelInfo,
+ LogLevel: logg.LevelInfo,
TxtarString: files,
}).Build()
@@ -233,7 +238,7 @@ func TestTransformPostCSSResourceCacheWithPathInBaseURL(t *testing.T) {
T: c,
NeedsOsFS: true,
NeedsNpmInstall: true,
- LogLevel: jww.LevelInfo,
+ LogLevel: logg.LevelInfo,
TxtarString: files,
WorkingDir: tempDir,
}).Build()
diff --git a/resources/resource_transformers/postcss/postcss.go b/resources/resource_transformers/postcss/postcss.go
index 376d72182..083607246 100644
--- a/resources/resource_transformers/postcss/postcss.go
+++ b/resources/resource_transformers/postcss/postcss.go
@@ -27,13 +27,12 @@ import (
"github.com/gohugoio/hugo/common/collections"
"github.com/gohugoio/hugo/common/hexec"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/text"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/common/hugo"
- "github.com/gohugoio/hugo/common/loggers"
-
"github.com/gohugoio/hugo/resources/internal"
"github.com/spf13/afero"
"github.com/spf13/cast"
@@ -151,10 +150,12 @@ func (t *postcssTransformation) Key() internal.ResourceTransformationKey {
func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationCtx) error {
const binaryName = "postcss"
+ infol := t.rs.Logger.InfoCommand(binaryName)
+ infoW := loggers.LevelLoggerToWriter(infol)
+
ex := t.rs.ExecHelper
var configFile string
- logger := t.rs.Logger
var options Options
if t.optionsm != nil {
@@ -185,7 +186,7 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC
var cmdArgs []any
if configFile != "" {
- logger.Infoln("postcss: use config file", configFile)
+ infol.Logf("use config file %q", configFile)
cmdArgs = []any{"--config", configFile}
}
@@ -194,7 +195,6 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC
}
var errBuf bytes.Buffer
- infoW := loggers.LoggerToWriterWithPrefix(logger.Info(), "postcss")
stderr := io.MultiWriter(infoW, &errBuf)
cmdArgs = append(cmdArgs, hexec.WithStderr(stderr))
@@ -401,7 +401,6 @@ func (imp *importResolver) shouldImport(s string) bool {
}
func (imp *importResolver) toFileError(output string) error {
- output = strings.TrimSpace(loggers.RemoveANSIColours(output))
inErr := errors.New(output)
match := cssSyntaxErrorRe.FindStringSubmatch(output)
diff --git a/resources/resource_transformers/postcss/postcss_test.go b/resources/resource_transformers/postcss/postcss_test.go
index 6901d69de..dd0695cd1 100644
--- a/resources/resource_transformers/postcss/postcss_test.go
+++ b/resources/resource_transformers/postcss/postcss_test.go
@@ -18,9 +18,9 @@ import (
"strings"
"testing"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/htesting/hqt"
- "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/helpers"
"github.com/spf13/afero"
@@ -95,7 +95,7 @@ LOCAL_STYLE
mainStyles,
"styles.css",
Options{},
- fs, loggers.NewErrorLogger(),
+ fs, loggers.NewDefault(),
)
r, err := imp.resolve()
@@ -144,7 +144,7 @@ LOCAL_STYLE
@import "e.css";
@import "missing.css";`
- logger := loggers.NewErrorLogger()
+ logger := loggers.NewDefault()
for i := 0; i < b.N; i++ {
b.StopTimer()
diff --git a/resources/resource_transformers/tocss/dartsass/client.go b/resources/resource_transformers/tocss/dartsass/client.go
index 9ae317886..63278f0db 100644
--- a/resources/resource_transformers/tocss/dartsass/client.go
+++ b/resources/resource_transformers/tocss/dartsass/client.go
@@ -58,6 +58,8 @@ func New(fs *filesystems.SourceFilesystem, rs *resources.Spec) (*Client, error)
transpiler *godartsass.Transpiler
transpilerv1 *godartsassv1.Transpiler
err error
+ infol = rs.Logger.InfoCommand("Dart Sass")
+ warnl = rs.Logger.WarnCommand("Dart Sass")
)
if hugo.IsDartSassV2() {
@@ -68,10 +70,10 @@ func New(fs *filesystems.SourceFilesystem, rs *resources.Spec) (*Client, error)
switch event.Type {
case godartsass.LogEventTypeDebug:
// Log as Info for now, we may adjust this if it gets too chatty.
- rs.Logger.Infof("Dart Sass: %s", message)
+ infol.Logf(message)
default:
// The rest are either deprecations or @warn statements.
- rs.Logger.Warnf("Dart Sass: %s", message)
+ warnl.Logf(message)
}
},
})
@@ -84,10 +86,10 @@ func New(fs *filesystems.SourceFilesystem, rs *resources.Spec) (*Client, error)
switch event.Type {
case godartsassv1.LogEventTypeDebug:
// Log as Info for now, we may adjust this if it gets too chatty.
- rs.Logger.Infof("Dart Sass: %s", message)
+ infol.Logf(message)
default:
// The rest are either deprecations or @warn statements.
- rs.Logger.Warnf("Dart Sass: %s", message)
+ warnl.Logf(message)
}
},
})
diff --git a/resources/resource_transformers/tocss/dartsass/integration_test.go b/resources/resource_transformers/tocss/dartsass/integration_test.go
index d76592336..c370a1cc8 100644
--- a/resources/resource_transformers/tocss/dartsass/integration_test.go
+++ b/resources/resource_transformers/tocss/dartsass/integration_test.go
@@ -17,10 +17,10 @@ import (
"strings"
"testing"
+ "github.com/bep/logg"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/hugolib"
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/dartsass"
- jww "github.com/spf13/jwalterweatherman"
)
func TestTransformIncludePaths(t *testing.T) {
@@ -288,11 +288,11 @@ T1: {{ $r.Content }}
T: t,
TxtarString: files,
NeedsOsFS: true,
- LogLevel: jww.LevelInfo,
+ LogLevel: logg.LevelInfo,
}).Build()
- b.AssertLogMatches(`WARN.*Dart Sass: foo`)
- b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:1:0: bar`)
+ b.AssertLogMatches(`Dart Sass: foo`)
+ b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:1:0: bar`)
}
@@ -513,20 +513,20 @@ T1: {{ $r.Content }}
T: t,
TxtarString: files,
NeedsOsFS: true,
- LogLevel: jww.LevelInfo,
+ LogLevel: logg.LevelInfo,
}).Build()
- b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:3:0: color`)
- b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:4:0: color`)
- b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:5:0: color`)
- b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:6:0: number`)
- b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:7:0: number`)
- b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:8:0: number`)
- b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:9:0: string`)
- b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:10:0: string`)
- b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:11:0: string`)
- b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:12:0: number`)
- b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:13:0: number`)
- b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:14:0: number`)
+ b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:3:0: color`)
+ b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:4:0: color`)
+ b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:5:0: color`)
+ b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:6:0: number`)
+ b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:7:0: number`)
+ b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:8:0: number`)
+ b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:9:0: string`)
+ b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:10:0: string`)
+ b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:11:0: string`)
+ b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:12:0: number`)
+ b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:13:0: number`)
+ b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:14:0: number`)
}
diff --git a/tpl/collections/collections.go b/tpl/collections/collections.go
index 35a87394a..04b777dfb 100644
--- a/tpl/collections/collections.go
+++ b/tpl/collections/collections.go
@@ -30,7 +30,6 @@ import (
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/deps"
- "github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/tpl/compare"
"github.com/spf13/cast"
@@ -393,7 +392,7 @@ func (ns *Namespace) IsSet(c any, key any) (bool, error) {
return av.MapIndex(kv).IsValid(), nil
}
default:
- helpers.DistinctErrorLog.Printf("WARNING: calling IsSet with unsupported type %q (%T) will always return false.\n", av.Kind(), c)
+ ns.deps.Log.Warnf("calling IsSet with unsupported type %q (%T) will always return false.\n", av.Kind(), c)
}
return false, nil
diff --git a/tpl/data/data.go b/tpl/data/data.go
index 251cf1a4f..380c25685 100644
--- a/tpl/data/data.go
+++ b/tpl/data/data.go
@@ -30,7 +30,6 @@ import (
"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/common/constants"
- "github.com/gohugoio/hugo/common/loggers"
"github.com/spf13/cast"
@@ -92,7 +91,7 @@ func (ns *Namespace) GetCSV(sep string, args ...any) (d [][]string, err error) {
if security.IsAccessDenied(err) {
return nil, err
}
- ns.deps.Log.(loggers.IgnorableLogger).Errorsf(constants.ErrRemoteGetCSV, "Failed to get CSV resource %q: %s", url, err)
+ ns.deps.Log.Errorsf(constants.ErrRemoteGetCSV, "Failed to get CSV resource %q: %s", url, err)
return nil, nil
}
@@ -128,7 +127,7 @@ func (ns *Namespace) GetJSON(args ...any) (any, error) {
if security.IsAccessDenied(err) {
return nil, err
}
- ns.deps.Log.(loggers.IgnorableLogger).Errorsf(constants.ErrRemoteGetJSON, "Failed to get JSON resource %q: %s", url, err)
+ ns.deps.Log.Errorsf(constants.ErrRemoteGetJSON, "Failed to get JSON resource %q: %s", url, err)
return nil, nil
}
diff --git a/tpl/data/data_test.go b/tpl/data/data_test.go
index f10b88a32..c51dbbd82 100644
--- a/tpl/data/data_test.go
+++ b/tpl/data/data_test.go
@@ -22,6 +22,7 @@ import (
"strings"
"testing"
+ "github.com/bep/logg"
"github.com/gohugoio/hugo/common/maps"
qt "github.com/frankban/quicktest"
@@ -108,13 +109,13 @@ func TestGetCSV(t *testing.T) {
got, err := ns.GetCSV(test.sep, test.url)
if _, ok := test.expect.(bool); ok {
- c.Assert(int(ns.deps.Log.LogCounters().ErrorCounter.Count()), qt.Equals, 1)
+ c.Assert(int(ns.deps.Log.LoggCount(logg.LevelError)), qt.Equals, 1)
c.Assert(got, qt.IsNil)
return
}
c.Assert(err, qt.IsNil, msg)
- c.Assert(int(ns.deps.Log.LogCounters().ErrorCounter.Count()), qt.Equals, 0)
+ c.Assert(int(ns.deps.Log.LoggCount(logg.LevelError)), qt.Equals, 0)
c.Assert(got, qt.Not(qt.IsNil), msg)
c.Assert(got, qt.DeepEquals, test.expect, msg)
})
@@ -200,11 +201,11 @@ func TestGetJSON(t *testing.T) {
got, _ := ns.GetJSON(test.url)
if _, ok := test.expect.(bool); ok {
- c.Assert(int(ns.deps.Log.LogCounters().ErrorCounter.Count()), qt.Equals, 1)
+ c.Assert(int(ns.deps.Log.LoggCount(logg.LevelError)), qt.Equals, 1)
return
}
- c.Assert(int(ns.deps.Log.LogCounters().ErrorCounter.Count()), qt.Equals, 0, msg)
+ c.Assert(int(ns.deps.Log.LoggCount(logg.LevelError)), qt.Equals, 0, msg)
c.Assert(got, qt.Not(qt.IsNil), msg)
c.Assert(got, qt.DeepEquals, test.expect)
@@ -283,7 +284,7 @@ func TestHeaders(t *testing.T) {
err := fn("http://example.org/api", "?foo", test.headers)
c.Assert(err, qt.IsNil)
- c.Assert(int(ns.deps.Log.LogCounters().ErrorCounter.Count()), qt.Equals, 0)
+ c.Assert(int(ns.deps.Log.LoggCount(logg.LevelError)), qt.Equals, 0)
test.assert(c, headers.String())
}
diff --git a/tpl/data/resources_test.go b/tpl/data/resources_test.go
index ad4ab20f4..d452a2a43 100644
--- a/tpl/data/resources_test.go
+++ b/tpl/data/resources_test.go
@@ -15,6 +15,9 @@ package data
import (
"bytes"
+
+ "github.com/gohugoio/hugo/common/loggers"
+
"net/http"
"net/http/httptest"
"net/url"
@@ -29,7 +32,6 @@ import (
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/cache/filecache"
- "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/hugofs"
@@ -182,7 +184,7 @@ func TestScpGetRemoteParallel(t *testing.T) {
func newDeps(cfg config.Provider) *deps.Deps {
conf := testconfig.GetTestConfig(nil, cfg)
- logger := loggers.NewIgnorableLogger(loggers.NewErrorLogger(), nil)
+ logger := loggers.NewDefault()
fs := hugofs.NewFrom(afero.NewMemMapFs(), conf.BaseConfig())
d := &deps.Deps{
diff --git a/tpl/fmt/fmt.go b/tpl/fmt/fmt.go
index 0667bcedd..c0d75e425 100644
--- a/tpl/fmt/fmt.go
+++ b/tpl/fmt/fmt.go
@@ -16,27 +16,22 @@ package fmt
import (
_fmt "fmt"
+ "sort"
+ "github.com/bep/logg"
"github.com/gohugoio/hugo/common/loggers"
-
"github.com/gohugoio/hugo/deps"
- "github.com/gohugoio/hugo/helpers"
+ "github.com/spf13/cast"
)
// New returns a new instance of the fmt-namespaced template functions.
func New(d *deps.Deps) *Namespace {
- ignorableLogger, ok := d.Log.(loggers.IgnorableLogger)
- if !ok {
- ignorableLogger = loggers.NewIgnorableLogger(d.Log, nil)
- }
-
- distinctLogger := helpers.NewDistinctLogger(d.Log)
ns := &Namespace{
- distinctLogger: ignorableLogger.Apply(distinctLogger),
+ logger: d.Log,
}
d.BuildStartListeners.Add(func() {
- ns.distinctLogger.Reset()
+ ns.logger.Reset()
})
return ns
@@ -44,7 +39,7 @@ func New(d *deps.Deps) *Namespace {
// Namespace provides template functions for the "fmt" namespace.
type Namespace struct {
- distinctLogger loggers.IgnorableLogger
+ logger loggers.Logger
}
// Print returns a string representation of args.
@@ -65,7 +60,7 @@ func (ns *Namespace) Println(args ...any) string {
// Errorf formats args according to a format specifier and logs an ERROR.
// It returns an empty string.
func (ns *Namespace) Errorf(format string, args ...any) string {
- ns.distinctLogger.Errorf(format, args...)
+ ns.logger.Errorf(format, args...)
return ""
}
@@ -73,13 +68,41 @@ func (ns *Namespace) Errorf(format string, args ...any) string {
// an information text that the error with the given id can be suppressed in config.
// It returns an empty string.
func (ns *Namespace) Erroridf(id, format string, args ...any) string {
- ns.distinctLogger.Errorsf(id, format, args...)
+ ns.logger.Errorsf(id, format, args...)
return ""
}
// Warnf formats args according to a format specifier and logs a WARNING.
// It returns an empty string.
func (ns *Namespace) Warnf(format string, args ...any) string {
- ns.distinctLogger.Warnf(format, args...)
+ ns.logger.Warnf(format, args...)
+ return ""
+}
+
+// Warnmf is epxermimental and subject to change at any time.
+func (ns *Namespace) Warnmf(m any, format string, args ...any) string {
+ return ns.logmf(ns.logger.Warn(), m, format, args...)
+}
+
+// Errormf is epxermimental and subject to change at any time.
+func (ns *Namespace) Errormf(m any, format string, args ...any) string {
+ return ns.logmf(ns.logger.Error(), m, format, args...)
+}
+
+func (ns *Namespace) logmf(l logg.LevelLogger, m any, format string, args ...any) string {
+ mm := cast.ToStringMap(m)
+ fields := make(logg.Fields, len(mm))
+ i := 0
+ for k, v := range mm {
+ fields[i] = logg.Field{Name: k, Value: v}
+ i++
+ }
+ // Sort the fields to make the output deterministic.
+ sort.Slice(fields, func(i, j int) bool {
+ return fields[i].Name < fields[j].Name
+ })
+
+ l.WithFields(fields).Logf(format, args...)
+
return ""
}
diff --git a/transform/livereloadinject/livereloadinject.go b/transform/livereloadinject/livereloadinject.go
index d57ba4d24..a29a64ebb 100644
--- a/transform/livereloadinject/livereloadinject.go
+++ b/transform/livereloadinject/livereloadinject.go
@@ -20,7 +20,8 @@ import (
"net/url"
"strings"
- "github.com/gohugoio/hugo/helpers"
+ "github.com/gohugoio/hugo/common/loggers"
+
"github.com/gohugoio/hugo/transform"
)
@@ -90,7 +91,7 @@ func New(baseURL url.URL) transform.Transformer {
c = append(c[:i], append(script, c[i:]...)...)
if _, err := ft.To().Write(c); err != nil {
- helpers.DistinctWarnLog.Println("Failed to inject LiveReload script:", err)
+ loggers.Log().Warnf("Failed to inject LiveReload script:", err)
}
return nil
}
diff --git a/transform/metainject/hugogenerator.go b/transform/metainject/hugogenerator.go
index fd3a6a4ab..43a477354 100644
--- a/transform/metainject/hugogenerator.go
+++ b/transform/metainject/hugogenerator.go
@@ -19,7 +19,7 @@ import (
"regexp"
"github.com/gohugoio/hugo/common/hugo"
- "github.com/gohugoio/hugo/helpers"
+ "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/transform"
)
@@ -33,7 +33,7 @@ func HugoGenerator(ft transform.FromTo) error {
b := ft.From().Bytes()
if metaTagsCheck.Match(b) {
if _, err := ft.To().Write(b); err != nil {
- helpers.DistinctWarnLog.Println("Failed to inject Hugo generator tag:", err)
+ loggers.Log().Warnf("Failed to inject Hugo generator tag: %s", err)
}
return nil
}
@@ -49,7 +49,7 @@ func HugoGenerator(ft transform.FromTo) error {
}
if _, err := ft.To().Write(newcontent); err != nil {
- helpers.DistinctWarnLog.Println("Failed to inject Hugo generator tag:", err)
+ loggers.Log().Warnf("Failed to inject Hugo generator tag: %s", err)
}
return nil