diff --git a/cache/filecache/integration_test.go b/cache/filecache/integration_test.go index a59ea048d42..a8a45988e02 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 f7b71197330..79d4c55cff9 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" @@ -93,7 +92,8 @@ type rootCommand struct { Println func(a ...interface{}) Out io.Writer - logger loggers.Logger + logger loggers.Logger + loggerDistinct loggers.Logger // The main cache busting key for the caches below. configVersionID atomic.Int32 @@ -405,52 +405,61 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error { } _, running := runner.Command.(*serverCommand) var err error - r.logger, err = r.createLogger(running) + l1, l2, err := r.createBaseLoggers(running) if err != nil { return err } + r.logger = l1 + r.loggerDistinct = l2 - loggers.PanicOnWarning.Store(r.panicOnWarning) + // TODO1 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}) return nil } -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 - } +func (r *rootCommand) createBaseLoggers(running bool) (loggers.Logger, loggers.Logger, error) { + 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) + return nil, 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 + } + } + + optsLogger := loggers.Options{ + Level: level, + Stdout: r.Out, + Stderr: r.Out, + StoreErrors: running, } - loggers.InitGlobalLogger(stdoutThreshold, jww.LevelWarn, outHandle, io.Discard) - helpers.InitLoggers() - return loggers.NewLogger(stdoutThreshold, jww.LevelWarn, outHandle, io.Discard, running), nil + optsLoggerDistinct := optsLogger + optsLoggerDistinct.Distinct = true + + l1, l2 := loggers.New(optsLogger), loggers.New(optsLoggerDistinct) + return l1, l2, nil + } func (r *rootCommand) Reset() { diff --git a/commands/deploy.go b/commands/deploy.go index 8dae4bd8824..82127adf4c7 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 95dbb1ca898..c6b273e8b06 100644 --- a/commands/hugobuilder.go +++ b/commands/hugobuilder.go @@ -26,6 +26,7 @@ import ( "sync" "time" + "github.com/bep/logg" "github.com/bep/simplecobra" "github.com/fsnotify/fsnotify" "github.com/gohugoio/hugo/common/herrors" @@ -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) } // 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 @@ -548,6 +550,7 @@ func (c *hugoBuilder) fullBuild(noBuildLock bool) error { if !c.r.quiet { fmt.Println("Start building sites … ") fmt.Println(hugo.BuildVersionString()) + fmt.Println() if terminal.IsTerminal(os.Stdout) { defer func() { fmt.Print(showCursor + clearLine) @@ -746,7 +749,7 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher, return } - c.r.logger.Infoln("Received System Events:", evs) + c.r.logger.Println("Received System Events:", evs) staticEvents := []fsnotify.Event{} dynamicEvents := []fsnotify.Event{} diff --git a/commands/import.go b/commands/import.go index 30ada15f8f9..f2c56a9a1e9 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 80036618c2b..3a0468e10be 100644 --- a/commands/server.go +++ b/commands/server.go @@ -654,8 +654,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"] = c.r.logger.Errors() + m["Version"] = hugo.BuildVersionString() ferrors := herrors.UnwrapFileErrorsWithErrorContext(c.errState.buildErr()) m["Files"] = ferrors @@ -861,6 +861,10 @@ 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 + c.r.loggerDistinct = h.LogDistinct + if isMultiHost { for _, l := range conf.configs.ConfigLangs() { baseURLs = append(baseURLs, l.BaseURL().String()) @@ -1066,8 +1070,7 @@ func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error { } }) - // prevent spamming the log on changes - logger := helpers.NewDistinctErrorLogger() + logger := s.c.r.loggerDistinct for _, ev := range staticEvents { // Due to our approach of layering both directories and the content's rendered output diff --git a/common/loggers/defaulthandler.go b/common/loggers/defaulthandler.go new file mode 100644 index 00000000000..2e9c4246c65 --- /dev/null +++ b/common/loggers/defaulthandler.go @@ -0,0 +1,107 @@ +// 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" + "sync" + + "github.com/bep/logg" + + "github.com/fatih/color" +) + +var bold = color.New(color.Bold) + +// Colors mapping. +var Colors = [...]*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), +} + +// Strings mapping. +var Strings = [...]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 := Colors[e.Level] + level := Strings[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 + } + + const cmdName = "cmd" + + var prefix string + for _, field := range e.Fields { + if field.Name == cmdName { + 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 field.Name == cmdName { + continue + } + fmt.Fprintf(w, " %s %v", color.Sprint(field.Name), field.Value) + } + + fmt.Fprintln(w) + + return nil +} diff --git a/common/loggers/helpers.go b/common/loggers/helpers.go new file mode 100644 index 00000000000..9ce3cfffa86 --- /dev/null +++ b/common/loggers/helpers.go @@ -0,0 +1,47 @@ +// 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" + "os" + "runtime" + "time" + + "github.com/mattn/go-isatty" +) + +// FormatBuildDuration formats a duration to a string on the form expected in "Total in ..." etc. +func FormatBuildDuration(d time.Duration) string { + if d.Milliseconds() < 2000 { + return fmt.Sprintf("%dms", d.Milliseconds()) + } + return fmt.Sprintf("%.2fs", d.Seconds()) +} + +// IsTerminal return true if the file descriptor is terminal and the TERM +// environment variable isn't a dumb one. +func IsTerminal(f *os.File) bool { + if runtime.GOOS == "windows" { + return false + } + if os.Getenv("CI") != "" { + return true + } + + fd := f.Fd() + return os.Getenv("TERM") != "dumb" && (isatty.IsTerminal(fd) || isatty.IsCygwinTerminal(fd)) +} diff --git a/common/loggers/ignorableLogger.go b/common/loggers/ignorableLogger.go deleted file mode 100644 index c8aba560e8a..00000000000 --- 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 00000000000..6ee046e05e3 --- /dev/null +++ b/common/loggers/logger.go @@ -0,0 +1,279 @@ +// 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" +) + +const ( + // FieldNameCmd is the name of the field that holds the command name. + FieldNameCmd = "cmd" + // Used to suppress statements. + FieldNameStatementID = "statement_id" +) + +// Options defines options for the logger. +type Options struct { + Level logg.Level + Stdout io.Writer + Stderr io.Writer + Distinct bool + StoreErrors bool + 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 IsTerminal(os.Stdout) { + logHandler = newDefaultHandler(opts.Stdout, opts.Stderr) + } else { + logHandler = newNoColoursHandler(opts.Stdout, opts.Stderr, nil) + } + + errorsw := &strings.Builder{} + logCounters := newLogLevelCounter() + handlers := []logg.Handler{ + logHandler, + logCounters, + } + + if opts.StoreErrors { + h := newNoColoursHandler(io.Discard, errorsw, func(e *logg.Entry) bool { + return e.Level >= logg.LevelError + }) + + handlers = append(handlers, h) + } + + logHandler = multi.New(handlers...) + + if opts.Distinct { + logHandler = newStopHandler(newLogOnceHandler(logg.LevelWarn), 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) + + return &logAdapter{ + logCounters: logCounters, + errors: errorsw, + 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{ + 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 + 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.logCounters.mu.Lock() + defer l.logCounters.mu.Unlock() + l.logCounters.counters = make(map[logg.Level]int) + l.errors.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 00000000000..5e705b518c3 --- /dev/null +++ b/common/loggers/logger_test.go @@ -0,0 +1,100 @@ +// 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 TestOptionStoreErrors(t *testing.T) { + c := qt.New(t) + + opts := loggers.Options{ + StoreErrors: true, + } + + l := loggers.New(opts) + l.Errorln("error 1") + l.Errorln("error 2") + + errorsStr := l.Errors() + + c.Assert(errorsStr, qt.Contains, "error 1") +} + +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 TestReset(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.Reset() + + errorsStr := l.Errors() + + c.Assert(errorsStr, qt.Equals, "") + c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 0) +} diff --git a/common/loggers/loggers.go b/common/loggers/loggers.go deleted file mode 100644 index fbbbca4354b..00000000000 --- 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 a7bd1ae1272..00000000000 --- 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/common/loggers/mischandlers.go b/common/loggers/mischandlers.go new file mode 100644 index 00000000000..37958de7433 --- /dev/null +++ b/common/loggers/mischandlers.go @@ -0,0 +1,129 @@ +// 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" +) + +var stopError = fmt.Errorf("stop") + +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 +} + +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 { + return nil + } + h.mu.Lock() + defer h.mu.Unlock() + hash := identity.HashUint64(e.Message, e.Fields) + if h.seen[hash] { + return stopError + } + h.seen[hash] = true + return nil +} + +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 + }) +} diff --git a/common/loggers/nocolourshandler.go b/common/loggers/nocolourshandler.go new file mode 100644 index 00000000000..af0e5ce0eec --- /dev/null +++ b/common/loggers/nocolourshandler.go @@ -0,0 +1,83 @@ +// 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" + "sync" + + "github.com/bep/logg" +) + +// newNoColoursHandler creates a new NoColoursHandler +func newNoColoursHandler(outWriter, errWriter io.Writer, predicate func(*logg.Entry) bool) *noColoursHandler { + if predicate == nil { + predicate = func(e *logg.Entry) bool { return true } + } + return &noColoursHandler{ + 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 +} + +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 + } + + const cmdName = "cmd" + + var prefix string + for _, field := range e.Fields { + if field.Name == cmdName { + prefix = fmt.Sprint(field.Value) + break + } + } + + if prefix != "" { + prefix = prefix + ": " + } + + fmt.Fprintf(w, "%s%s", prefix, e.Message) + for _, field := range e.Fields { + if field.Name == cmdName { + continue + } + fmt.Fprintf(w, " %s %q", field.Name, field.Value) + } + fmt.Fprintln(w) + + return nil +} diff --git a/config/allconfig/load.go b/config/allconfig/load.go index eca9d06dfa9..a4919d2cf7d 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()} diff --git a/config/commonConfig.go b/config/commonConfig.go index bd3e235bdfc..ac1dd39fab0 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 106069bdcef..b8130eb0ddb 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 db88996a9c1..60a3da36382 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 fe874fbbd22..66eece10b18 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 39462de96fe..e5309debea5 100644 --- a/deps/deps.go +++ b/deps/deps.go @@ -25,7 +25,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. @@ -37,7 +36,8 @@ type Deps struct { Log loggers.Logger `json:"-"` // Used to log errors that may repeat itself many times. - LogDistinct loggers.Logger + // TODO1 make it 1 logger. + LogDistinct loggers.Logger `json:"-"` ExecHelper *hexec.Exec @@ -117,15 +117,21 @@ func (d *Deps) Init() error { } if d.Log == nil { - d.Log = loggers.NewErrorLogger() + d.Log = loggers.NewDefault() } if d.LogDistinct == nil { - d.LogDistinct = helpers.NewDistinctLogger(d.Log) + d.LogDistinct = loggers.New( + loggers.Options{ + Distinct: true, + }, + ) } if d.globalErrHandler == nil { - d.globalErrHandler = &globalErrHandler{} + d.globalErrHandler = &globalErrHandler{ + logger: d.Log, + } } if d.BuildState == nil { @@ -228,6 +234,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 +254,7 @@ func (e *globalErrHandler) SendError(err error) { } return } - - jww.ERROR.Println(err) + e.logger.Errorln(err) } func (e *globalErrHandler) StartErrorCollector() chan error { diff --git a/go.mod b/go.mod index 6371423e0f0..92939a0867b 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 7ba2c773bed..ffadc9c48a8 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 50f7920f6b5..e50f205ed99 100644 --- a/helpers/general.go +++ b/helpers/general.go @@ -24,13 +24,12 @@ import ( "path/filepath" "sort" "strings" - "sync" "unicode" "unicode/utf8" - "github.com/gohugoio/hugo/common/loggers" - + "github.com/bep/logg" "github.com/gohugoio/hugo/common/hugo" + "github.com/gohugoio/hugo/common/loggers" "github.com/spf13/afero" @@ -254,141 +253,19 @@ 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() + DistinctErrorLog logg.LevelLogger + DistinctWarnLog logg.LevelLogger ) -// InitLoggers resets the global distinct loggers. -func InitLoggers() { - DistinctErrorLog.Reset() - DistinctWarnLog.Reset() +func init() { + l := loggers.New( + loggers.Options{ + Distinct: true, + }, + ) + DistinctErrorLog = l.Logger().WithLevel(logg.LevelError) + DistinctWarnLog = l.Logger().WithLevel(logg.LevelWarn) } // Deprecated informs about a deprecation, but only once for a given set of arguments' values. @@ -398,13 +275,13 @@ 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) + DistinctErrorLog.Logf("%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() { + if false { // TODO1 !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) + DistinctWarnLog.Logf("%s is deprecated and will be removed in a future release. %s%s", item, alternative, warnPanicMessage) } } diff --git a/helpers/general_test.go b/helpers/general_test.go index 9b2e4fc5833..82741102724 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 be8983fdb86..6c5f62488ea 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 d3cad5e7432..af559844fdd 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 e00dcf1a8e4..d0a8baaaa0d 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 e847174c672..e883f892e83 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 d10f140bd98..5165edb0413 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 124c9f4ca4f..e03107ada2c 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 5a308720752..02d25fa9be9 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 ed0d36de168..d5c94799d50 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 ee4ef798ae2..3353f508e5f 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 290eebe82b6..6774d749e25 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,7 +352,7 @@ 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() + // TODO1 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 c801ae3dff6..de3336a2f67 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" @@ -164,7 +165,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,7 +196,6 @@ func (h *HugoSites) initRebuild(config *BuildCfg) error { h.reset(config) h.resetLogs() - helpers.InitLoggers() return nil } @@ -238,7 +238,7 @@ func (h *HugoSites) assemble(bcfg *BuildCfg) error { func (h *HugoSites) timeTrack(start time.Time, name string) { elapsed := time.Since(start) - h.Log.Infof("%s in %v ms\n", name, int(1000*elapsed.Seconds())) + h.Log.Info().WithField("step", name).WithField("duration", elapsed).Logf("running") } func (h *HugoSites) render(config *BuildCfg) error { diff --git a/hugolib/hugo_sites_build_test.go b/hugolib/hugo_sites_build_test.go index b2798c86365..3c82a60b68b 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 0daa766ac96..5f696a5f709 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,10 +292,16 @@ 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) + logOpts := loggers.Options{ + Level: s.Cfg.LogLevel, + Stdout: &s.logBuff, + Stderr: &s.logBuff, + } + + logger := loggers.New(logOpts) isBinaryRe := regexp.MustCompile(`^(.*)(\.png|\.jpg)$`) @@ -528,7 +534,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 688cf255846..4f6a448d214 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_test.go b/hugolib/page_test.go index 79b6401dc37..6b817551ee9 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 2ec3718f0c1..33822bde709 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{Logger: loggers.NewDefault(), 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 8a2b875eaed..c57c707de1e 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 8c1023a15bc..c771d30eee9 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 af66156e692..e0a4cd0369a 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 823f7db45f8..da90634aa14 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 035f543ee2c..2646f1847a3 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) } } @@ -509,7 +509,7 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro sourceFilesChanged = make(map[string]bool) // prevent spamming the log on changes - logger = helpers.NewDistinctErrorLogger() + logger = s.LogDistinct ) var cacheBusters []func(string) bool diff --git a/hugolib/site_new.go b/hugolib/site_new.go index 911414121e0..8b3e051474b 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,15 +102,29 @@ func (s *Site) Debug() { func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) { conf := cfg.Configs.GetFirstLanguageConfig() - logger := cfg.Logger - if logger == nil { - logger = loggers.NewErrorLogger() + // TODO1 remove Logger from DepsCfg, add LogLevel and LogOut. + logOpts := loggers.Options{ + Stdout: os.Stdout, + Stderr: os.Stdout, + StoreErrors: conf.Running(), + SuppresssStatements: conf.IgnoredErrors(), } - ignorableLogger := loggers.NewIgnorableLogger(logger, conf.IgnoredErrors()) + + level := logg.LevelWarn + if cfg.Logger != nil { + logOpts.Stdout = cfg.Logger.Out() + logOpts.Stderr = cfg.Logger.Out() + level = cfg.Logger.Level() + } + logOpts.Level = level + + logDistinctOpts := logOpts + logOpts.Distinct = true firstSiteDeps := &deps.Deps{ Fs: cfg.Fs, - Log: ignorableLogger, + Log: loggers.New(logOpts), + LogDistinct: loggers.New(logDistinctOpts), Conf: conf, TemplateProvider: tplimpl.DefaultTemplateProvider, TranslationProvider: i18n.NewTranslationProvider(), @@ -209,6 +225,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 f076b98ddf1..c4c3f389ba8 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 7cf1a55965b..d2766def323 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" ) diff --git a/langs/i18n/i18n.go b/langs/i18n/i18n.go index a9b7b4c9755..1ebd039cd88 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 1ac6144ddaa..8629c35fc0a 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/langs/i18n/translationProvider.go b/langs/i18n/translationProvider.go index 2c3c15710d3..a1c134dca82 100644 --- a/langs/i18n/translationProvider.go +++ b/langs/i18n/translationProvider.go @@ -76,7 +76,7 @@ func (tp *TranslationProvider) NewResource(dst *deps.Deps) error { } } - tp.t = NewTranslator(bundle, dst.Conf, dst.Log) + tp.t = NewTranslator(bundle, dst.Conf, dst.LogDistinct) dst.Translate = tp.t.Func(dst.Conf.Language().Lang) diff --git a/markup/asciidocext/convert_test.go b/markup/asciidocext/convert_test.go index cdc98126325..459686139e6 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 66652987705..a2f7b959776 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 78811cfb4c0..f7f7bb7a0b2 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 802b9aa5a22..141269f1d89 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 08841b2d739..1422585afdd 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 f549d5f4ff8..6a1535946c5 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 5d2882de15b..9e98d0405c5 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 59f6b25d3d4..5d8daf92623 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 a65731ae4ca..93eb32e475a 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 d827bfbad29..98ab6b2225c 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 c462e176f24..95124cb5883 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 5ecb021fe3a..3e1b5320561 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 ff19d9dda4b..5f8fcb00f7e 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 164e7fd40be..44a13f103dc 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 cfe5f8a2ca6..1909ffa35ca 100644 --- a/resources/resource_transformers/postcss/integration_test.go +++ b/resources/resource_transformers/postcss/integration_test.go @@ -19,8 +19,7 @@ import ( "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 +123,7 @@ func TestTransformPostCSS(t *testing.T) { T: c, NeedsOsFS: true, NeedsNpmInstall: true, - LogLevel: jww.LevelInfo, + LogLevel: logg.LevelInfo, WorkingDir: tempDir, TxtarString: files, }).Build() @@ -176,7 +175,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 +200,7 @@ func TestTransformPostCSSImporSkipInlineImportsNotFound(t *testing.T) { T: c, NeedsOsFS: true, NeedsNpmInstall: true, - LogLevel: jww.LevelInfo, + LogLevel: logg.LevelInfo, TxtarString: files, }).Build() @@ -233,7 +232,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 376d72182f0..c2d3a12c8cc 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", 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 6901d69defa..dd0695cd1e7 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 9ae3178869d..63278f0db24 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 d765923360c..c370a1cc88b 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/testscripts/commands/deploy.txt b/testscripts/unfinished/deploy.txt similarity index 100% rename from testscripts/commands/deploy.txt rename to testscripts/unfinished/deploy.txt diff --git a/tpl/collections/collections.go b/tpl/collections/collections.go index 35a87394afe..4ed260d9489 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.LogDistinct.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 251cf1a4f78..380c25685f6 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 f10b88a320d..c51dbbd8231 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 ad4ab20f446..d452a2a439a 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 0667bcedd39..19f1db87fc4 100644 --- a/tpl/fmt/fmt.go +++ b/tpl/fmt/fmt.go @@ -18,21 +18,13 @@ import ( _fmt "fmt" "github.com/gohugoio/hugo/common/loggers" - "github.com/gohugoio/hugo/deps" - "github.com/gohugoio/hugo/helpers" ) // 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), + distinctLogger: d.LogDistinct, } d.BuildStartListeners.Add(func() { @@ -44,7 +36,7 @@ func New(d *deps.Deps) *Namespace { // Namespace provides template functions for the "fmt" namespace. type Namespace struct { - distinctLogger loggers.IgnorableLogger + distinctLogger loggers.Logger } // Print returns a string representation of args. diff --git a/transform/livereloadinject/livereloadinject.go b/transform/livereloadinject/livereloadinject.go index d57ba4d244d..ffc09a10700 100644 --- a/transform/livereloadinject/livereloadinject.go +++ b/transform/livereloadinject/livereloadinject.go @@ -90,7 +90,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) + helpers.DistinctWarnLog.Logf("Failed to inject LiveReload script:", err) } return nil } diff --git a/transform/metainject/hugogenerator.go b/transform/metainject/hugogenerator.go index fd3a6a4abaf..803d26bbb42 100644 --- a/transform/metainject/hugogenerator.go +++ b/transform/metainject/hugogenerator.go @@ -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) + helpers.DistinctWarnLog.Logf("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) + helpers.DistinctWarnLog.Logf("Failed to inject Hugo generator tag: %s", err) } return nil