Replace the old log setup, with structured logging etc.

Fixes #11124
This commit is contained in:
Bjørn Erik Pedersen 2023-06-16 08:17:42 +02:00
parent 0e79446586
commit 7c9fada778
80 changed files with 1273 additions and 1082 deletions

View file

@ -16,11 +16,10 @@ package filecache_test
import ( import (
"path/filepath" "path/filepath"
jww "github.com/spf13/jwalterweatherman"
"testing" "testing"
"time" "time"
"github.com/bep/logg"
qt "github.com/frankban/quicktest" qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/htesting" "github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/hugolib" "github.com/gohugoio/hugo/hugolib"
@ -80,7 +79,7 @@ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAA
` `
b := hugolib.NewIntegrationTestBuilder( 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() ).Build()
b.Assert(b.GCCount, qt.Equals, 0) b.Assert(b.GCCount, qt.Equals, 0)

View file

@ -28,12 +28,11 @@ import (
"syscall" "syscall"
"time" "time"
jww "github.com/spf13/jwalterweatherman"
"go.uber.org/automaxprocs/maxprocs" "go.uber.org/automaxprocs/maxprocs"
"github.com/bep/clock" "github.com/bep/clock"
"github.com/bep/lazycache" "github.com/bep/lazycache"
"github.com/bep/logg"
"github.com/bep/overlayfs" "github.com/bep/overlayfs"
"github.com/bep/simplecobra" "github.com/bep/simplecobra"
@ -114,7 +113,6 @@ type rootCommand struct {
baseURL string baseURL string
gc bool gc bool
poll string poll string
panicOnWarning bool
forceSyncStatic bool forceSyncStatic bool
printPathWarnings bool printPathWarnings bool
printUnusedTemplates bool printUnusedTemplates bool
@ -308,7 +306,7 @@ func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commo
func (r *rootCommand) HugFromConfig(conf *commonConfig) (*hugolib.HugoSites, error) { func (r *rootCommand) HugFromConfig(conf *commonConfig) (*hugolib.HugoSites, error) {
h, _, err := r.hugoSites.GetOrCreate(r.configVersionID.Load(), func(key int32) (*hugolib.HugoSites, error) { h, _, err := r.hugoSites.GetOrCreate(r.configVersionID.Load(), func(key int32) (*hugolib.HugoSites, error) {
depsCfg := deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, Logger: r.logger} depsCfg := deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, LogOut: r.logger.Out(), LogLevel: r.logger.Level()}
return hugolib.NewHugoSites(depsCfg) return hugolib.NewHugoSites(depsCfg)
}) })
return h, err return h, err
@ -320,7 +318,7 @@ func (r *rootCommand) Hugo(cfg config.Provider) (*hugolib.HugoSites, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
depsCfg := deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, Logger: r.logger} depsCfg := deps.DepsCfg{Configs: conf.configs, Fs: conf.fs, LogOut: r.logger.Out(), LogLevel: r.logger.Level()}
return hugolib.NewHugoSites(depsCfg) return hugolib.NewHugoSites(depsCfg)
}) })
return h, err return h, err
@ -410,7 +408,6 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
return err return err
} }
loggers.PanicOnWarning.Store(r.panicOnWarning)
r.commonConfigs = lazycache.New[int32, *commonConfig](lazycache.Options{MaxEntries: 5}) r.commonConfigs = lazycache.New[int32, *commonConfig](lazycache.Options{MaxEntries: 5})
r.hugoSites = lazycache.New[int32, *hugolib.HugoSites](lazycache.Options{MaxEntries: 5}) r.hugoSites = lazycache.New[int32, *hugolib.HugoSites](lazycache.Options{MaxEntries: 5})
@ -418,43 +415,48 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
} }
func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) { func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) {
var ( level := logg.LevelWarn
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
}
if r.logLevel != "" { if r.logLevel != "" {
switch strings.ToLower(r.logLevel) { switch strings.ToLower(r.logLevel) {
case "debug": case "debug":
stdoutThreshold = jww.LevelDebug level = logg.LevelDebug
case "info": case "info":
stdoutThreshold = jww.LevelInfo level = logg.LevelInfo
case "warn", "warning": case "warn", "warning":
stdoutThreshold = jww.LevelWarn level = logg.LevelWarn
case "error": case "error":
stdoutThreshold = jww.LevelError level = logg.LevelError
default: default:
return nil, fmt.Errorf("invalid log level: %q, must be one of debug, warn, info or error", r.logLevel) return nil, fmt.Errorf("invalid log level: %q, must be one of debug, warn, info or error", r.logLevel)
} }
} else {
if r.verbose {
helpers.Deprecated("--verbose", "use --logLevel info", false)
level = logg.LevelInfo
}
if r.debug {
helpers.Deprecated("--debug", "use --logLevel debug", false)
level = logg.LevelDebug
}
} }
loggers.InitGlobalLogger(stdoutThreshold, jww.LevelWarn, outHandle, io.Discard) optsLogger := loggers.Options{
helpers.InitLoggers() Distinct: true,
return loggers.NewLogger(stdoutThreshold, jww.LevelWarn, outHandle, io.Discard, running), nil Level: level,
Stdout: r.Out,
Stderr: r.Out,
StoreErrors: running,
}
return loggers.New(optsLogger), nil
} }
func (r *rootCommand) Reset() { func (r *rootCommand) Reset() {
r.logger.Reset() r.logger.Reset()
loggers.Log().Reset()
} }
// IsTestRun reports whether the command is running as a test. // IsTestRun reports whether the command is running as a test.
@ -530,7 +532,7 @@ func applyLocalFlagsBuild(cmd *cobra.Command, r *rootCommand) {
cmd.Flags().StringP("layoutDir", "l", "", "filesystem path to layout directory") cmd.Flags().StringP("layoutDir", "l", "", "filesystem path to layout directory")
cmd.Flags().BoolVar(&r.gc, "gc", false, "enable to run some cleanup tasks (remove unused cache files) after the build") cmd.Flags().BoolVar(&r.gc, "gc", false, "enable to run some cleanup tasks (remove unused cache files) after the build")
cmd.Flags().StringVar(&r.poll, "poll", "", "set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes") cmd.Flags().StringVar(&r.poll, "poll", "", "set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes")
cmd.Flags().BoolVar(&r.panicOnWarning, "panicOnWarning", false, "panic on first WARNING log") cmd.Flags().Bool("panicOnWarning", false, "panic on first WARNING log")
cmd.Flags().Bool("templateMetrics", false, "display metrics about template executions") cmd.Flags().Bool("templateMetrics", false, "display metrics about template executions")
cmd.Flags().Bool("templateMetricsHints", false, "calculate some improvement hints when combined with --templateMetrics") cmd.Flags().Bool("templateMetricsHints", false, "calculate some improvement hints when combined with --templateMetrics")
cmd.Flags().BoolVar(&r.forceSyncStatic, "forceSyncStatic", false, "copy all files when static is changed.") cmd.Flags().BoolVar(&r.forceSyncStatic, "forceSyncStatic", false, "copy all files when static is changed.")

View file

@ -52,7 +52,7 @@ documentation.
if err != nil { if err != nil {
return err 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 { if err != nil {
return err return err
} }

View file

@ -26,11 +26,13 @@ import (
"sync" "sync"
"time" "time"
"github.com/bep/logg"
"github.com/bep/simplecobra" "github.com/bep/simplecobra"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
"github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/htime" "github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/common/terminal" "github.com/gohugoio/hugo/common/terminal"
"github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/common/types"
@ -68,7 +70,6 @@ type hugoBuilder struct {
onConfigLoaded func(reloaded bool) error onConfigLoaded func(reloaded bool) error
fastRenderMode bool fastRenderMode bool
buildWatch bool
showErrorInBrowser bool showErrorInBrowser bool
errState hugoBuilderErrState errState hugoBuilderErrState
@ -131,7 +132,7 @@ func (e *hugoBuilderErrState) wasErr() bool {
} }
func (c *hugoBuilder) errCount() int { func (c *hugoBuilder) errCount() int {
return int(c.r.logger.LogCounters().ErrorCounter.Count()) return c.r.logger.LoggCount(logg.LevelError) + loggers.Log().LoggCount(logg.LevelError)
} }
// getDirList provides NewWatcher() with a list of directories to watch for changes. // 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 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 { for _, configFile := range configFiles {
watcher.Add(configFile) watcher.Add(configFile)
configSet[configFile] = true configSet[configFile] = true
@ -461,6 +462,7 @@ func (c *hugoBuilder) copyStatic() (map[string]uint64, error) {
} }
func (c *hugoBuilder) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint64, error) { func (c *hugoBuilder) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint64, error) {
infol := c.r.logger.InfoCommand("copy static")
publishDir := helpers.FilePathSeparator publishDir := helpers.FilePathSeparator
if sourceFs.PublishFolder != "" { if sourceFs.PublishFolder != "" {
@ -484,13 +486,13 @@ func (c *hugoBuilder) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint
syncer.SrcFs = fs syncer.SrcFs = fs
if syncer.Delete { 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 { syncer.DeleteFilter = func(f os.FileInfo) bool {
return f.IsDir() && strings.HasPrefix(f.Name(), ".") 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). // because we are using a baseFs (to get the union right).
// set sync src to root // set sync src to root
@ -545,14 +547,13 @@ func (c *hugoBuilder) fullBuild(noBuildLock bool) error {
langCount map[string]uint64 langCount map[string]uint64
) )
if !c.r.quiet { c.r.logger.Println("Start building sites … ")
fmt.Println("Start building sites … ") c.r.logger.Println(hugo.BuildVersionString())
fmt.Println(hugo.BuildVersionString()) c.r.logger.Println()
if terminal.IsTerminal(os.Stdout) { if terminal.IsTerminal(os.Stdout) {
defer func() { defer func() {
fmt.Print(showCursor + clearLine) fmt.Print(showCursor + clearLine)
}() }()
}
} }
copyStaticFunc := func() error { copyStaticFunc := func() error {

View file

@ -19,12 +19,11 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"log"
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
jww "github.com/spf13/jwalterweatherman"
"strconv" "strconv"
"strings" "strings"
"time" "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 { func (c *importCommand) convertJekyllPost(path, relPath, targetDir string, draft bool) error {
jww.TRACE.Println("Converting", path) log.Println("Converting", path)
filename := filepath.Base(path) filename := filepath.Base(path)
postDate, postName, err := c.parseJekyllFilename(filename) postDate, postName, err := c.parseJekyllFilename(filename)
@ -308,7 +307,7 @@ func (c *importCommand) convertJekyllPost(path, relPath, targetDir string, draft
return nil return nil
} }
jww.TRACE.Println(filename, postDate, postName) log.Println(filename, postDate, postName)
targetFile := filepath.Join(targetDir, relPath) targetFile := filepath.Join(targetDir, relPath)
targetParentDir := filepath.Dir(targetFile) targetParentDir := filepath.Dir(targetFile)
@ -367,7 +366,7 @@ func (c *importCommand) copyJekyllFilesAndFolders(jekyllRoot, dest string, jekyl
if _, ok := jekyllPostDirs[entry.Name()]; !ok { if _, ok := jekyllPostDirs[entry.Name()]; !ok {
err = hugio.CopyDir(fs, sfp, dfp, nil) err = hugio.CopyDir(fs, sfp, dfp, nil)
if err != 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] != '_' { if !isExcept && entry.Name()[0] != '.' && entry.Name()[0] != '_' {
err = hugio.CopyFile(fs, sfp, dfp) err = hugio.CopyFile(fs, sfp, dfp)
if err != nil { if err != nil {
jww.ERROR.Println(err) c.r.logger.Errorln(err)
} }
} }
} }

View file

@ -67,7 +67,6 @@ import (
) )
var ( var (
logErrorRe = regexp.MustCompile(`(?s)ERROR \d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} `)
logDuplicateTemplateExecuteRe = regexp.MustCompile(`: template: .*?:\d+:\d+: executing ".*?"`) logDuplicateTemplateExecuteRe = regexp.MustCompile(`: template: .*?:\d+:\d+: executing ".*?"`)
logDuplicateTemplateParseRe = regexp.MustCompile(`: template: .*?:\d+:\d*`) logDuplicateTemplateParseRe = regexp.MustCompile(`: template: .*?:\d+:\d*`)
) )
@ -106,9 +105,7 @@ func newServerCommand() *serverCommand {
// Flags. // Flags.
var uninstall bool var uninstall bool
var c *serverCommand c := &serverCommand{
c = &serverCommand{
quit: make(chan bool), quit: make(chan bool),
commands: []simplecobra.Commander{ commands: []simplecobra.Commander{
&simpleCommand{ &simpleCommand{
@ -654,8 +651,8 @@ func (c *serverCommand) getErrorWithContext() any {
m := make(map[string]any) m := make(map[string]any)
//xwm["Error"] = errors.New(cleanErrorLog(removeErrorPrefixFromLog(c.r.logger.Errors()))) m["Error"] = cleanErrorLog(c.r.logger.Errors())
m["Error"] = errors.New(cleanErrorLog(removeErrorPrefixFromLog(c.r.logger.Errors())))
m["Version"] = hugo.BuildVersionString() m["Version"] = hugo.BuildVersionString()
ferrors := herrors.UnwrapFileErrorsWithErrorContext(c.errState.buildErr()) ferrors := herrors.UnwrapFileErrorsWithErrorContext(c.errState.buildErr())
m["Files"] = ferrors m["Files"] = ferrors
@ -861,6 +858,9 @@ func (c *serverCommand) serve() error {
return err return err
} }
// We need the server to share the same logger as the Hugo build (for error counts etc.)
c.r.logger = h.Log
if isMultiHost { if isMultiHost {
for _, l := range conf.configs.ConfigLangs() { for _, l := range conf.configs.ConfigLangs() {
baseURLs = append(baseURLs, l.BaseURL().String()) baseURLs = append(baseURLs, l.BaseURL().String())
@ -1066,8 +1066,7 @@ func (s *staticSyncer) syncsStaticEvents(staticEvents []fsnotify.Event) error {
} }
}) })
// prevent spamming the log on changes logger := s.c.r.logger
logger := helpers.NewDistinctErrorLogger()
for _, ev := range staticEvents { for _, ev := range staticEvents {
// Due to our approach of layering both directories and the content's rendered output // Due to our approach of layering both directories and the content's rendered output
@ -1206,10 +1205,6 @@ func pickOneWriteOrCreatePath(events []fsnotify.Event) string {
return name return name
} }
func removeErrorPrefixFromLog(content string) string {
return logErrorRe.ReplaceAllLiteralString(content, "")
}
func formatByteCount(b uint64) string { func formatByteCount(b uint64) string {
const unit = 1000 const unit = 1000
if b < unit { if b < unit {

View file

@ -1,79 +0,0 @@
// Copyright 2023 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package commands
import (
"context"
"fmt"
"github.com/bep/simplecobra"
"github.com/spf13/cobra"
)
func newSimpleTemplateCommand() simplecobra.Commander {
return &simpleCommand{
name: "template",
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
return nil
},
withc: func(cmd *cobra.Command, r *rootCommand) {
},
}
}
func newTemplateCommand() *templateCommand {
return &templateCommand{
commands: []simplecobra.Commander{},
}
}
type templateCommand struct {
r *rootCommand
commands []simplecobra.Commander
}
func (c *templateCommand) Commands() []simplecobra.Commander {
return c.commands
}
func (c *templateCommand) Name() string {
return "template"
}
func (c *templateCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error {
conf, err := c.r.ConfigFromProvider(c.r.configVersionID.Load(), flagsToCfg(cd, nil))
if err != nil {
return err
}
fmt.Println("templateCommand.Run", conf)
return nil
}
func (c *templateCommand) Init(cd *simplecobra.Commandeer) error {
cmd := cd.CobraCommand
cmd.Short = "Print the site configuration"
cmd.Long = `Print the site configuration, both default and custom settings.`
return nil
}
func (c *templateCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
c.r = cd.Root.Command.(*rootCommand)
return nil
}

View file

@ -0,0 +1,106 @@
// Copyright 2023 The Hugo Authors. All rights reserved.
// Some functions in this file (see comments) is based on the Go source code,
// copyright The Go Authors and governed by a BSD-style license.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// package loggers contains some basic logging setup.
package loggers
import (
"fmt"
"io"
"strings"
"sync"
"github.com/bep/logg"
"github.com/fatih/color"
)
var bold = color.New(color.Bold)
// levelColor mapping.
var levelColor = [...]*color.Color{
logg.LevelDebug: color.New(color.FgWhite),
logg.LevelInfo: color.New(color.FgBlue),
logg.LevelWarn: color.New(color.FgYellow),
logg.LevelError: color.New(color.FgRed),
}
// levelString mapping.
var levelString = [...]string{
logg.LevelDebug: "DEBUG",
logg.LevelInfo: "INFO ",
logg.LevelWarn: "WARN ",
logg.LevelError: "ERROR",
}
// newDefaultHandler handler.
func newDefaultHandler(outWriter, errWriter io.Writer) logg.Handler {
return &defaultHandler{
outWriter: outWriter,
errWriter: errWriter,
Padding: 0,
}
}
// Default Handler implementation.
// Based on https://github.com/apex/log/blob/master/handlers/cli/cli.go
type defaultHandler struct {
mu sync.Mutex
outWriter io.Writer // Defaults to os.Stdout.
errWriter io.Writer // Defaults to os.Stderr.
Padding int
}
// HandleLog implements logg.Handler.
func (h *defaultHandler) HandleLog(e *logg.Entry) error {
color := levelColor[e.Level]
level := levelString[e.Level]
h.mu.Lock()
defer h.mu.Unlock()
var w io.Writer
if e.Level > logg.LevelInfo {
w = h.errWriter
} else {
w = h.outWriter
}
var prefix string
for _, field := range e.Fields {
if field.Name == FieldNameCmd {
prefix = fmt.Sprint(field.Value)
break
}
}
if prefix != "" {
prefix = prefix + ": "
}
color.Fprintf(w, "%s %s%s", bold.Sprintf("%*s", h.Padding+1, level), color.Sprint(prefix), e.Message)
for _, field := range e.Fields {
if strings.HasPrefix(field.Name, reservedFieldNamePrefix) {
continue
}
fmt.Fprintf(w, " %s %v", color.Sprint(field.Name), field.Value)
}
fmt.Fprintln(w)
return nil
}

View file

@ -0,0 +1,158 @@
// Copyright 2023 The Hugo Authors. All rights reserved.
// Some functions in this file (see comments) is based on the Go source code,
// copyright The Go Authors and governed by a BSD-style license.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package loggers
import (
"fmt"
"strings"
"sync"
"github.com/bep/logg"
"github.com/gohugoio/hugo/identity"
)
// PanicOnWarningHook panics on warnings.
var PanicOnWarningHook = func(e *logg.Entry) error {
if e.Level != logg.LevelWarn {
return nil
}
panic(e.Message)
}
func newLogLevelCounter() *logLevelCounter {
return &logLevelCounter{
counters: make(map[logg.Level]int),
}
}
func newLogOnceHandler(threshold logg.Level) *logOnceHandler {
return &logOnceHandler{
threshold: threshold,
seen: make(map[uint64]bool),
}
}
func newStopHandler(h ...logg.Handler) *stopHandler {
return &stopHandler{
handlers: h,
}
}
func newSuppressStatementsHandler(statements map[string]bool) *suppressStatementsHandler {
return &suppressStatementsHandler{
statements: statements,
}
}
type logLevelCounter struct {
mu sync.RWMutex
counters map[logg.Level]int
}
func (h *logLevelCounter) HandleLog(e *logg.Entry) error {
h.mu.Lock()
defer h.mu.Unlock()
h.counters[e.Level]++
return nil
}
var stopError = fmt.Errorf("stop")
type logOnceHandler struct {
threshold logg.Level
mu sync.Mutex
seen map[uint64]bool
}
func (h *logOnceHandler) HandleLog(e *logg.Entry) error {
if e.Level < h.threshold {
// We typically only want to enable this for warnings and above.
// The common use case is that many go routines may log the same error.
return nil
}
h.mu.Lock()
defer h.mu.Unlock()
hash := identity.HashUint64(e.Level, e.Message, e.Fields)
if h.seen[hash] {
return stopError
}
h.seen[hash] = true
return nil
}
func (h *logOnceHandler) reset() {
h.mu.Lock()
defer h.mu.Unlock()
h.seen = make(map[uint64]bool)
}
type stopHandler struct {
handlers []logg.Handler
}
// HandleLog implements logg.Handler.
func (h *stopHandler) HandleLog(e *logg.Entry) error {
for _, handler := range h.handlers {
if err := handler.HandleLog(e); err != nil {
if err == stopError {
return nil
}
return err
}
}
return nil
}
type suppressStatementsHandler struct {
statements map[string]bool
}
func (h *suppressStatementsHandler) HandleLog(e *logg.Entry) error {
for _, field := range e.Fields {
if field.Name == FieldNameStatementID {
if h.statements[field.Value.(string)] {
return stopError
}
}
}
return nil
}
// replacer creates a new log handler that does string replacement in log messages.
func replacer(repl *strings.Replacer) logg.Handler {
return logg.HandlerFunc(func(e *logg.Entry) error {
e.Message = repl.Replace(e.Message)
for i, field := range e.Fields {
if s, ok := field.Value.(string); ok {
e.Fields[i].Value = repl.Replace(s)
}
}
return nil
})
}
// whiteSpaceTrimmer creates a new log handler that trims whitespace from log messages and string fields.
func whiteSpaceTrimmer() logg.Handler {
return logg.HandlerFunc(func(e *logg.Entry) error {
e.Message = strings.TrimSpace(e.Message)
for i, field := range e.Fields {
if s, ok := field.Value.(string); ok {
e.Fields[i].Value = strings.TrimSpace(s)
}
}
return nil
})
}

View file

@ -0,0 +1,90 @@
// Copyright 2023 The Hugo Authors. All rights reserved.
// Some functions in this file (see comments) is based on the Go source code,
// copyright The Go Authors and governed by a BSD-style license.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package loggers
import (
"fmt"
"io"
"strings"
"sync"
"github.com/bep/logg"
)
// newNoColoursHandler creates a new NoColoursHandler
func newNoColoursHandler(outWriter, errWriter io.Writer, noLevelPrefix bool, predicate func(*logg.Entry) bool) *noColoursHandler {
if predicate == nil {
predicate = func(e *logg.Entry) bool { return true }
}
return &noColoursHandler{
noLevelPrefix: noLevelPrefix,
outWriter: outWriter,
errWriter: errWriter,
predicate: predicate,
}
}
type noColoursHandler struct {
mu sync.Mutex
outWriter io.Writer // Defaults to os.Stdout.
errWriter io.Writer // Defaults to os.Stderr.
predicate func(*logg.Entry) bool
noLevelPrefix bool
}
func (h *noColoursHandler) HandleLog(e *logg.Entry) error {
if !h.predicate(e) {
return nil
}
h.mu.Lock()
defer h.mu.Unlock()
var w io.Writer
if e.Level > logg.LevelInfo {
w = h.errWriter
} else {
w = h.outWriter
}
var prefix string
for _, field := range e.Fields {
if field.Name == FieldNameCmd {
prefix = fmt.Sprint(field.Value)
break
}
}
if prefix != "" {
prefix = prefix + ": "
}
if h.noLevelPrefix {
fmt.Fprintf(w, "%s%s", prefix, e.Message)
} else {
fmt.Fprintf(w, "%s %s%s", levelString[e.Level], prefix, e.Message)
}
for _, field := range e.Fields {
if strings.HasPrefix(field.Name, reservedFieldNamePrefix) {
continue
}
fmt.Fprintf(w, " %s %q", field.Name, field.Value)
}
fmt.Fprintln(w)
return nil
}

View file

@ -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,
}
}

303
common/loggers/logger.go Normal file
View file

@ -0,0 +1,303 @@
// Copyright 2023 The Hugo Authors. All rights reserved.
// Some functions in this file (see comments) is based on the Go source code,
// copyright The Go Authors and governed by a BSD-style license.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package loggers
import (
"fmt"
"io"
"os"
"strings"
"time"
"github.com/bep/logg"
"github.com/bep/logg/handlers/multi"
"github.com/gohugoio/hugo/common/terminal"
)
var (
reservedFieldNamePrefix = "__h_field_"
// FieldNameCmd is the name of the field that holds the command name.
FieldNameCmd = reservedFieldNamePrefix + "_cmd"
// Used to suppress statements.
FieldNameStatementID = reservedFieldNamePrefix + "__h_field_statement_id"
)
// Options defines options for the logger.
type Options struct {
Level logg.Level
Stdout io.Writer
Stderr io.Writer
Distinct bool
StoreErrors bool
HandlerPost func(e *logg.Entry) error
SuppresssStatements map[string]bool
}
// New creates a new logger with the given options.
func New(opts Options) Logger {
if opts.Stdout == nil {
opts.Stdout = os.Stdout
}
if opts.Stderr == nil {
opts.Stderr = os.Stdout
}
if opts.Level == 0 {
opts.Level = logg.LevelWarn
}
var logHandler logg.Handler
if terminal.PrintANSIColors(os.Stdout) {
logHandler = newDefaultHandler(opts.Stdout, opts.Stderr)
} else {
logHandler = newNoColoursHandler(opts.Stdout, opts.Stderr, false, nil)
}
errorsw := &strings.Builder{}
logCounters := newLogLevelCounter()
handlers := []logg.Handler{
whiteSpaceTrimmer(),
logHandler,
logCounters,
}
if opts.HandlerPost != nil {
var hookHandler logg.HandlerFunc = func(e *logg.Entry) error {
opts.HandlerPost(e)
return nil
}
handlers = append(handlers, hookHandler)
}
if opts.StoreErrors {
h := newNoColoursHandler(io.Discard, errorsw, true, func(e *logg.Entry) bool {
return e.Level >= logg.LevelError
})
handlers = append(handlers, h)
}
logHandler = multi.New(handlers...)
var logOnce *logOnceHandler
if opts.Distinct {
logOnce = newLogOnceHandler(logg.LevelWarn)
logHandler = newStopHandler(logOnce, logHandler)
}
if opts.SuppresssStatements != nil && len(opts.SuppresssStatements) > 0 {
logHandler = newStopHandler(newSuppressStatementsHandler(opts.SuppresssStatements), logHandler)
}
logger := logg.New(
logg.Options{
Level: opts.Level,
Handler: logHandler,
},
)
l := logger.WithLevel(opts.Level)
reset := func() {
logCounters.mu.Lock()
defer logCounters.mu.Unlock()
logCounters.counters = make(map[logg.Level]int)
errorsw.Reset()
if logOnce != nil {
logOnce.reset()
}
}
return &logAdapter{
logCounters: logCounters,
errors: errorsw,
reset: reset,
out: opts.Stdout,
level: opts.Level,
logger: logger,
debugl: l.WithLevel(logg.LevelDebug),
infol: l.WithLevel(logg.LevelInfo),
warnl: l.WithLevel(logg.LevelWarn),
errorl: l.WithLevel(logg.LevelError),
}
}
// NewDefault creates a new logger with the default options.
func NewDefault() Logger {
opts := Options{
Distinct: true,
Level: logg.LevelWarn,
Stdout: os.Stdout,
Stderr: os.Stdout,
}
return New(opts)
}
func LevelLoggerToWriter(l logg.LevelLogger) io.Writer {
return logWriter{l: l}
}
type Logger interface {
Debugf(format string, v ...any)
Debugln(v ...any)
Error() logg.LevelLogger
Errorf(format string, v ...any)
Errorln(v ...any)
Errors() string
Errorsf(id, format string, v ...any)
Info() logg.LevelLogger
InfoCommand(command string) logg.LevelLogger
Infof(format string, v ...any)
Infoln(v ...any)
Level() logg.Level
LoggCount(logg.Level) int
Logger() logg.Logger
Out() io.Writer
Printf(format string, v ...any)
Println(v ...any)
PrintTimerIfDelayed(start time.Time, name string)
Reset()
Warn() logg.LevelLogger
WarnCommand(command string) logg.LevelLogger
Warnf(format string, v ...any)
Warnln(v ...any)
}
type logAdapter struct {
logCounters *logLevelCounter
errors *strings.Builder
reset func()
out io.Writer
level logg.Level
logger logg.Logger
debugl logg.LevelLogger
infol logg.LevelLogger
warnl logg.LevelLogger
errorl logg.LevelLogger
}
func (l *logAdapter) Debugf(format string, v ...any) {
l.debugl.Logf(format, v...)
}
func (l *logAdapter) Debugln(v ...any) {
l.debugl.Logf(l.sprint(v...))
}
func (l *logAdapter) Info() logg.LevelLogger {
return l.infol
}
func (l *logAdapter) InfoCommand(command string) logg.LevelLogger {
return l.infol.WithField(FieldNameCmd, command)
}
func (l *logAdapter) Infof(format string, v ...any) {
l.infol.Logf(format, v...)
}
func (l *logAdapter) Infoln(v ...any) {
l.infol.Logf(l.sprint(v...))
}
func (l *logAdapter) Level() logg.Level {
return l.level
}
func (l *logAdapter) LoggCount(level logg.Level) int {
l.logCounters.mu.RLock()
defer l.logCounters.mu.RUnlock()
return l.logCounters.counters[level]
}
func (l *logAdapter) Logger() logg.Logger {
return l.logger
}
func (l *logAdapter) Out() io.Writer {
return l.out
}
// PrintTimerIfDelayed prints a time statement to the FEEDBACK logger
// if considerable time is spent.
func (l *logAdapter) PrintTimerIfDelayed(start time.Time, name string) {
elapsed := time.Since(start)
milli := int(1000 * elapsed.Seconds())
if milli < 500 {
return
}
l.Printf("%s in %v ms", name, milli)
}
func (l *logAdapter) Printf(format string, v ...any) {
fmt.Fprintf(l.out, format, v...)
}
func (l *logAdapter) Println(v ...any) {
fmt.Fprintln(l.out, v...)
}
func (l *logAdapter) Reset() {
l.reset()
}
func (l *logAdapter) Warn() logg.LevelLogger {
return l.warnl
}
func (l *logAdapter) Warnf(format string, v ...any) {
l.warnl.Logf(format, v...)
}
func (l *logAdapter) WarnCommand(command string) logg.LevelLogger {
return l.warnl.WithField(FieldNameCmd, command)
}
func (l *logAdapter) Warnln(v ...any) {
l.warnl.Logf(l.sprint(v...))
}
func (l *logAdapter) Error() logg.LevelLogger {
return l.errorl
}
func (l *logAdapter) Errorf(format string, v ...any) {
l.errorl.Logf(format, v...)
}
func (l *logAdapter) Errorln(v ...any) {
l.errorl.Logf(l.sprint(v...))
}
func (l *logAdapter) Errors() string {
return l.errors.String()
}
func (l *logAdapter) Errorsf(id, format string, v ...any) {
l.errorl.WithField(FieldNameStatementID, id).Logf(format, v...)
}
func (l *logAdapter) sprint(v ...any) string {
return strings.TrimRight(fmt.Sprintln(v...), "\n")
}
type logWriter struct {
l logg.LevelLogger
}
func (w logWriter) Write(p []byte) (n int, err error) {
w.l.Logf("%s", p)
return len(p), nil
}

View file

@ -0,0 +1,156 @@
// Copyright 2023 The Hugo Authors. All rights reserved.
// Some functions in this file (see comments) is based on the Go source code,
// copyright The Go Authors and governed by a BSD-style license.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package loggers_test
import (
"io"
"strings"
"testing"
"github.com/bep/logg"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/loggers"
)
func TestLogDistinct(t *testing.T) {
c := qt.New(t)
opts := loggers.Options{
Distinct: true,
StoreErrors: true,
Stdout: io.Discard,
Stderr: io.Discard,
}
l := loggers.New(opts)
for i := 0; i < 10; i++ {
l.Errorln("error 1")
l.Errorln("error 2")
l.Warnln("warn 1")
}
c.Assert(strings.Count(l.Errors(), "error 1"), qt.Equals, 1)
c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 2)
c.Assert(l.LoggCount(logg.LevelWarn), qt.Equals, 1)
}
func TestHookLast(t *testing.T) {
c := qt.New(t)
opts := loggers.Options{
HandlerPost: func(e *logg.Entry) error {
panic(e.Message)
},
Stdout: io.Discard,
Stderr: io.Discard,
}
l := loggers.New(opts)
c.Assert(func() { l.Warnln("warn 1") }, qt.PanicMatches, "warn 1")
}
func TestOptionStoreErrors(t *testing.T) {
c := qt.New(t)
var sb strings.Builder
opts := loggers.Options{
StoreErrors: true,
Stderr: &sb,
Stdout: &sb,
}
l := loggers.New(opts)
l.Errorln("error 1")
l.Errorln("error 2")
errorsStr := l.Errors()
c.Assert(errorsStr, qt.Contains, "error 1")
c.Assert(errorsStr, qt.Not(qt.Contains), "ERROR")
c.Assert(sb.String(), qt.Contains, "error 1")
c.Assert(sb.String(), qt.Contains, "ERROR")
}
func TestLogCount(t *testing.T) {
c := qt.New(t)
opts := loggers.Options{
StoreErrors: true,
}
l := loggers.New(opts)
l.Errorln("error 1")
l.Errorln("error 2")
l.Warnln("warn 1")
c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 2)
c.Assert(l.LoggCount(logg.LevelWarn), qt.Equals, 1)
c.Assert(l.LoggCount(logg.LevelInfo), qt.Equals, 0)
}
func TestSuppressStatements(t *testing.T) {
c := qt.New(t)
opts := loggers.Options{
StoreErrors: true,
SuppresssStatements: map[string]bool{
"error-1": true,
},
}
l := loggers.New(opts)
l.Error().WithField(loggers.FieldNameStatementID, "error-1").Logf("error 1")
l.Errorln("error 2")
errorsStr := l.Errors()
c.Assert(errorsStr, qt.Not(qt.Contains), "error 1")
c.Assert(errorsStr, qt.Contains, "error 2")
c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 1)
}
func TestReset(t *testing.T) {
c := qt.New(t)
opts := loggers.Options{
StoreErrors: true,
Distinct: true,
Stdout: io.Discard,
Stderr: io.Discard,
}
l := loggers.New(opts)
for i := 0; i < 3; i++ {
l.Errorln("error 1")
l.Errorln("error 2")
l.Errorln("error 1")
c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 2)
l.Reset()
errorsStr := l.Errors()
c.Assert(errorsStr, qt.Equals, "")
c.Assert(l.LoggCount(logg.LevelError), qt.Equals, 0)
}
}

View file

@ -0,0 +1,53 @@
// Copyright 2023 The Hugo Authors. All rights reserved.
// Some functions in this file (see comments) is based on the Go source code,
// copyright The Go Authors and governed by a BSD-style license.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package loggers
import (
"sync"
"github.com/bep/logg"
)
func InitGlobalLogger(panicOnWarnings bool) {
logMu.Lock()
defer logMu.Unlock()
var logHookLast func(e *logg.Entry) error
if panicOnWarnings {
logHookLast = PanicOnWarningHook
}
log = New(
Options{
Distinct: true,
HandlerPost: logHookLast,
},
)
}
var logMu sync.Mutex
func Log() Logger {
logMu.Lock()
defer logMu.Unlock()
return log
}
// The global logger.
var log Logger
func init() {
InitGlobalLogger(false)
}

View file

@ -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,
}
}

View file

@ -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 |")
}

View file

@ -490,6 +490,9 @@ type RootConfig struct {
// ENable to print warnings for multiple files published to the same destination. // ENable to print warnings for multiple files published to the same destination.
LogPathWarnings bool LogPathWarnings bool
// Enable to panic on warning log entries. This may make it easier to detect the source.
PanicOnWarning bool
// The configured environment. Default is "development" for server and "production" for build. // The configured environment. Default is "development" for server and "production" for build.
Environment string Environment string

View file

@ -46,7 +46,7 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) {
} }
if d.Logger == nil { if d.Logger == nil {
d.Logger = loggers.NewErrorLogger() d.Logger = loggers.NewDefault()
} }
l := &configLoader{ConfigSourceDescriptor: d, cfg: config.New()} l := &configLoader{ConfigSourceDescriptor: d, cfg: config.New()}
@ -90,8 +90,9 @@ func LoadConfig(d ConfigSourceDescriptor) (*Configs, error) {
return nil, fmt.Errorf("failed to init config: %w", err) return nil, fmt.Errorf("failed to init config: %w", err)
} }
// This is unfortunate, but this is a global setting. // This is unfortunate, but these are global settings.
tpl.SetSecurityAllowActionJSTmpl(configs.Base.Security.GoTemplates.AllowActionJSTmpl) tpl.SetSecurityAllowActionJSTmpl(configs.Base.Security.GoTemplates.AllowActionJSTmpl)
loggers.InitGlobalLogger(configs.Base.PanicOnWarning)
return configs, nil return configs, nil

View file

@ -19,6 +19,7 @@ import (
"sort" "sort"
"strings" "strings"
"github.com/bep/logg"
"github.com/gobwas/glob" "github.com/gobwas/glob"
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/common/types"
@ -306,6 +307,7 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
if c.compiledSource != nil { if c.compiledSource != nil {
return nil return nil
} }
source := c.Source source := c.Source
target := c.Target target := c.Target
sourceRe, err := regexp.Compile(source) 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) return fmt.Errorf("failed to compile cache buster source %q: %w", c.Source, err)
} }
var compileErr error var compileErr error
debugl := logger.Logger().WithLevel(logg.LevelDebug).WithField(loggers.FieldNameCmd, "cachebuster")
c.compiledSource = func(s string) func(string) bool { c.compiledSource = func(s string) func(string) bool {
m := sourceRe.FindStringSubmatch(s) m := sourceRe.FindStringSubmatch(s)
matchString := "no match" matchString := "no match"
@ -320,7 +324,7 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
if match { if match {
matchString = "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 { if !match {
return nil return nil
} }
@ -341,7 +345,7 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
if match { if match {
matchString = "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 return match
} }

View file

@ -92,7 +92,7 @@ status = 301
s, err := DecodeServer(cfg) s, err := DecodeServer(cfg)
c.Assert(err, qt.IsNil) 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{ c.Assert(s.MatchHeaders("/foo.jpg"), qt.DeepEquals, []types.KeyValueStr{
{Key: "X-Content-Type-Options", Value: "nosniff"}, {Key: "X-Content-Type-Options", Value: "nosniff"},
@ -145,7 +145,7 @@ func TestBuildConfigCacheBusters(t *testing.T) {
c := qt.New(t) c := qt.New(t)
cfg := New() cfg := New()
conf := DecodeBuildConfig(cfg) conf := DecodeBuildConfig(cfg)
l := loggers.NewInfoLogger() l := loggers.NewDefault()
c.Assert(conf.CompileConfig(l), qt.IsNil) c.Assert(conf.CompileConfig(l), qt.IsNil)
m, err := conf.MatchCacheBuster(l, "assets/foo/main.js") m, err := conf.MatchCacheBuster(l, "assets/foo/main.js")

View file

@ -37,10 +37,10 @@ import (
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/gobwas/glob" "github.com/gobwas/glob"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/media"
"github.com/spf13/afero" "github.com/spf13/afero"
jww "github.com/spf13/jwalterweatherman"
"golang.org/x/text/unicode/norm" "golang.org/x/text/unicode/norm"
"gocloud.dev/blob" "gocloud.dev/blob"
@ -56,9 +56,10 @@ type Deployer struct {
bucket *blob.Bucket bucket *blob.Bucket
mediaTypes media.Types // Hugo's MediaType to guess ContentType 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 target *Target // the target to deploy to
@ -73,7 +74,7 @@ type deploySummary struct {
const metaMD5Hash = "md5chksum" // the meta key to store md5hash in const metaMD5Hash = "md5chksum" // the meta key to store md5hash in
// New constructs a new *Deployer. // 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) dcfg := cfg.GetConfigSection(deploymentConfigKey).(DeployConfig)
targetName := dcfg.Target targetName := dcfg.Target
@ -112,12 +113,16 @@ func (d *Deployer) openBucket(ctx context.Context) (*blob.Bucket, error) {
if d.bucket != nil { if d.bucket != nil {
return 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) return blob.OpenBucket(ctx, d.target.URL)
} }
// Deploy deploys the site to a target. // Deploy deploys the site to a target.
func (d *Deployer) Deploy(ctx context.Context) error { func (d *Deployer) Deploy(ctx context.Context) error {
if d.logger == nil {
d.logger = loggers.NewDefault()
}
bucket, err := d.openBucket(ctx) bucket, err := d.openBucket(ctx)
if err != nil { if err != nil {
return err return err
@ -132,33 +137,33 @@ func (d *Deployer) Deploy(ctx context.Context) error {
if d.target != nil { if d.target != nil {
include, exclude = d.target.includeGlob, d.target.excludeGlob 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 { if err != nil {
return err 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) d.summary.NumLocal = len(local)
// Load remote files from the target. // Load remote files from the target.
remote, err := walkRemote(ctx, bucket, include, exclude) remote, err := d.walkRemote(ctx, bucket, include, exclude)
if err != nil { if err != nil {
return err 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) d.summary.NumRemote = len(remote)
// Diff local vs remote to see what changes need to be applied. // 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.NumUploads = len(uploads)
d.summary.NumDeletes = len(deletes) d.summary.NumDeletes = len(deletes)
if len(uploads)+len(deletes) == 0 { if len(uploads)+len(deletes) == 0 {
if !d.quiet { if !d.quiet {
jww.FEEDBACK.Println("No changes required.") d.logger.Println("No changes required.")
} }
return nil return nil
} }
if !d.quiet { if !d.quiet {
jww.FEEDBACK.Println(summarizeChanges(uploads, deletes)) d.logger.Println(summarizeChanges(uploads, deletes))
} }
// Ask for confirmation before proceeding. // Ask for confirmation before proceeding.
@ -192,14 +197,14 @@ func (d *Deployer) Deploy(ctx context.Context) error {
for _, upload := range uploads { for _, upload := range uploads {
if d.cfg.DryRun { if d.cfg.DryRun {
if !d.quiet { if !d.quiet {
jww.FEEDBACK.Printf("[DRY RUN] Would upload: %v\n", upload) d.logger.Printf("[DRY RUN] Would upload: %v\n", upload)
} }
continue continue
} }
sem <- struct{}{} sem <- struct{}{}
go func(upload *fileToUpload) { go func(upload *fileToUpload) {
if err := doSingleUpload(ctx, bucket, upload); err != nil { if err := d.doSingleUpload(ctx, bucket, upload); err != nil {
errMu.Lock() errMu.Lock()
defer errMu.Unlock() defer errMu.Unlock()
errs = append(errs, err) 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 { 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 d.summary.NumDeletes = 0
} else { } else {
// Apply deletes in parallel. // Apply deletes in parallel.
@ -223,16 +228,16 @@ func (d *Deployer) Deploy(ctx context.Context) error {
for _, del := range deletes { for _, del := range deletes {
if d.cfg.DryRun { if d.cfg.DryRun {
if !d.quiet { if !d.quiet {
jww.FEEDBACK.Printf("[DRY RUN] Would delete %s\n", del) d.logger.Printf("[DRY RUN] Would delete %s\n", del)
} }
continue continue
} }
sem <- struct{}{} sem <- struct{}{}
go func(del string) { 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 err := bucket.Delete(ctx, del); err != nil {
if gcerrors.Code(err) == gcerrors.NotFound { 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 { } else {
errMu.Lock() errMu.Lock()
defer errMu.Unlock() defer errMu.Unlock()
@ -250,24 +255,24 @@ func (d *Deployer) Deploy(ctx context.Context) error {
if len(errs) > 0 { if len(errs) > 0 {
if !d.quiet { if !d.quiet {
jww.FEEDBACK.Printf("Encountered %d errors.\n", len(errs)) d.logger.Printf("Encountered %d errors.\n", len(errs))
} }
return errs[0] return errs[0]
} }
if !d.quiet { if !d.quiet {
jww.FEEDBACK.Println("Success!") d.logger.Println("Success!")
} }
if d.cfg.InvalidateCDN { if d.cfg.InvalidateCDN {
if d.target.CloudFrontDistributionID != "" { if d.target.CloudFrontDistributionID != "" {
if d.cfg.DryRun { if d.cfg.DryRun {
if !d.quiet { 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 { } else {
jww.FEEDBACK.Println("Invalidating CloudFront CDN...") d.logger.Println("Invalidating CloudFront CDN...")
if err := InvalidateCloudFront(ctx, d.target.CloudFrontDistributionID); err != nil { 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 return err
} }
} }
@ -275,17 +280,17 @@ func (d *Deployer) Deploy(ctx context.Context) error {
if d.target.GoogleCloudCDNOrigin != "" { if d.target.GoogleCloudCDNOrigin != "" {
if d.cfg.DryRun { if d.cfg.DryRun {
if !d.quiet { 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 { } else {
jww.FEEDBACK.Println("Invalidating Google Cloud CDN...") d.logger.Println("Invalidating Google Cloud CDN...")
if err := InvalidateGoogleCloudCDN(ctx, d.target.GoogleCloudCDNOrigin); err != nil { 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 return err
} }
} }
} }
jww.FEEDBACK.Println("Success!") d.logger.Println("Success!")
} }
return nil return nil
} }
@ -300,8 +305,8 @@ func summarizeChanges(uploads []*fileToUpload, deletes []string) string {
} }
// doSingleUpload executes a single file upload. // doSingleUpload executes a single file upload.
func doSingleUpload(ctx context.Context, bucket *blob.Bucket, upload *fileToUpload) error { func (d *Deployer) doSingleUpload(ctx context.Context, bucket *blob.Bucket, upload *fileToUpload) error {
jww.INFO.Printf("Uploading %v...\n", upload) d.logger.Infof("Uploading %v...\n", upload)
opts := &blob.WriterOptions{ opts := &blob.WriterOptions{
CacheControl: upload.Local.CacheControl(), CacheControl: upload.Local.CacheControl(),
ContentEncoding: upload.Local.ContentEncoding(), 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, // walkLocal walks the source directory and returns a flat list of files,
// using localFile.SlashPath as the map keys. // 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{} retval := map[string]*localFile{}
err := afero.Walk(fs, "", func(path string, info os.FileInfo, err error) error { err := afero.Walk(fs, "", func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
@ -509,11 +514,11 @@ func walkLocal(fs afero.Fs, matchers []*Matcher, include, exclude glob.Glob, med
// Check include/exclude matchers. // Check include/exclude matchers.
slashpath := filepath.ToSlash(path) slashpath := filepath.ToSlash(path)
if include != nil && !include.Match(slashpath) { 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 return nil
} }
if exclude != nil && exclude.Match(slashpath) { 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 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. // 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{} retval := map[string]*blob.ListObject{}
iter := bucket.List(nil) iter := bucket.List(nil)
for { for {
@ -552,11 +557,11 @@ func walkRemote(ctx context.Context, bucket *blob.Bucket, include, exclude glob.
} }
// Check include/exclude matchers. // Check include/exclude matchers.
if include != nil && !include.Match(obj.Key) { 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 continue
} }
if exclude != nil && exclude.Match(obj.Key) { 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 continue
} }
// If the remote didn't give us an MD5, use remote attributes MD5, if that doesn't exist compute one. // 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 // findDiffs diffs localFiles vs remoteFiles to see what changes should be
// applied to the remote target. It returns a slice of *fileToUpload and a // applied to the remote target. It returns a slice of *fileToUpload and a
// slice of paths for files to delete. // 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 uploads []*fileToUpload
var deletes []string var deletes []string
@ -680,10 +685,10 @@ func findDiffs(localFiles map[string]*localFile, remoteFiles map[string]*blob.Li
reason = reasonNotFound reason = reasonNotFound
} }
if upload { 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}) uploads = append(uploads, &fileToUpload{lf, reason})
} else { } 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)
} }
} }

View file

@ -30,6 +30,7 @@ import (
"sort" "sort"
"testing" "testing"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/media"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts" "github.com/google/go-cmp/cmp/cmpopts"
@ -197,7 +198,8 @@ func TestFindDiffs(t *testing.T) {
for _, r := range tc.Remote { for _, r := range tc.Remote {
remote[r.Key] = r 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] gotUpdates = applyOrdering(nil, gotUpdates)[0]
sort.Slice(gotDeletes, func(i, j int) bool { return gotDeletes[i] < gotDeletes[j] }) sort.Slice(gotDeletes, func(i, j int) bool { return gotDeletes[i] < gotDeletes[j] })
if diff := cmp.Diff(gotUpdates, tc.WantUpdates, cmpopts.IgnoreUnexported(localFile{})); diff != "" { if diff := cmp.Diff(gotUpdates, tc.WantUpdates, cmpopts.IgnoreUnexported(localFile{})); diff != "" {
@ -249,7 +251,8 @@ func TestWalkLocal(t *testing.T) {
fd.Close() 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) t.Fatal(err)
} else { } else {
expect := map[string]any{} expect := map[string]any{}
@ -1026,3 +1029,9 @@ func verifyRemote(ctx context.Context, bucket *blob.Bucket, local []*fileData) (
} }
return diff, nil return diff, nil
} }
func newDeployer() *Deployer {
return &Deployer{
logger: loggers.NewDefault(),
}
}

32
deps/deps.go vendored
View file

@ -3,12 +3,14 @@ package deps
import ( import (
"context" "context"
"fmt" "fmt"
"io"
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"github.com/bep/logg"
"github.com/gohugoio/hugo/common/hexec" "github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config"
@ -25,7 +27,6 @@ import (
"github.com/gohugoio/hugo/source" "github.com/gohugoio/hugo/source"
"github.com/gohugoio/hugo/tpl" "github.com/gohugoio/hugo/tpl"
"github.com/spf13/afero" "github.com/spf13/afero"
jww "github.com/spf13/jwalterweatherman"
) )
// Deps holds dependencies used by many. // Deps holds dependencies used by many.
@ -36,9 +37,6 @@ type Deps struct {
// The logger to use. // The logger to use.
Log loggers.Logger `json:"-"` Log loggers.Logger `json:"-"`
// Used to log errors that may repeat itself many times.
LogDistinct loggers.Logger
ExecHelper *hexec.Exec ExecHelper *hexec.Exec
// The templates to use. This will usually implement the full tpl.TemplateManager. // The templates to use. This will usually implement the full tpl.TemplateManager.
@ -117,15 +115,13 @@ func (d *Deps) Init() error {
} }
if d.Log == nil { if d.Log == nil {
d.Log = loggers.NewErrorLogger() d.Log = loggers.NewDefault()
}
if d.LogDistinct == nil {
d.LogDistinct = helpers.NewDistinctLogger(d.Log)
} }
if d.globalErrHandler == nil { if d.globalErrHandler == nil {
d.globalErrHandler = &globalErrHandler{} d.globalErrHandler = &globalErrHandler{
logger: d.Log,
}
} }
if d.BuildState == nil { if d.BuildState == nil {
@ -228,6 +224,8 @@ func (d *Deps) Compile(prototype *Deps) error {
} }
type globalErrHandler struct { type globalErrHandler struct {
logger loggers.Logger
// Channel for some "hard to get to" build errors // Channel for some "hard to get to" build errors
buildErrors chan error buildErrors chan error
// Used to signal that the build is done. // Used to signal that the build is done.
@ -246,8 +244,7 @@ func (e *globalErrHandler) SendError(err error) {
} }
return return
} }
e.logger.Errorln(err)
jww.ERROR.Println(err)
} }
func (e *globalErrHandler) StartErrorCollector() chan error { func (e *globalErrHandler) StartErrorCollector() chan error {
@ -312,9 +309,16 @@ func (d *Deps) Close() error {
// on a global level, i.e. logging etc. // on a global level, i.e. logging etc.
// Nil values will be given default values. // Nil values will be given default values.
type DepsCfg struct { type DepsCfg struct {
// The logger to use. Only set in some tests.
// TODO(bep) get rid of this.
TestLogger loggers.Logger
// The Logger to use. // The logging level to use.
Logger loggers.Logger LogLevel logg.Level
// Where to write the logs.
// Currently we typically write everything to stdout.
LogOut io.Writer
// The file systems to use // The file systems to use
Fs *hugofs.Fs Fs *hugofs.Fs

6
go.mod
View file

@ -16,6 +16,7 @@ require (
github.com/bep/gowebp v0.2.0 github.com/bep/gowebp v0.2.0
github.com/bep/helpers v0.4.0 github.com/bep/helpers v0.4.0
github.com/bep/lazycache v0.2.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/mclib v1.20400.20402
github.com/bep/overlayfs v0.6.0 github.com/bep/overlayfs v0.6.0
github.com/bep/simplecobra v0.3.2 github.com/bep/simplecobra v0.3.2
@ -25,6 +26,7 @@ require (
github.com/disintegration/gift v1.2.1 github.com/disintegration/gift v1.2.1
github.com/dustin/go-humanize v1.0.1 github.com/dustin/go-humanize v1.0.1
github.com/evanw/esbuild v0.18.3 github.com/evanw/esbuild v0.18.3
github.com/fatih/color v1.15.0
github.com/fortytw2/leaktest v1.3.0 github.com/fortytw2/leaktest v1.3.0
github.com/frankban/quicktest v1.14.5 github.com/frankban/quicktest v1.14.5
github.com/fsnotify/fsnotify v1.6.0 github.com/fsnotify/fsnotify v1.6.0
@ -58,7 +60,6 @@ require (
github.com/spf13/cast v1.5.1 github.com/spf13/cast v1.5.1
github.com/spf13/cobra v1.7.0 github.com/spf13/cobra v1.7.0
github.com/spf13/fsync v0.9.0 github.com/spf13/fsync v0.9.0
github.com/spf13/jwalterweatherman v1.1.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/tdewolff/minify/v2 v2.12.6 github.com/tdewolff/minify/v2 v2.12.6
github.com/tdewolff/parse/v2 v2.6.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/ssooidc v1.14.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.18.3 // 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/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/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // 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/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
github.com/mailru/easyjson v0.7.7 // 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/mattn/go-runewidth v0.0.9 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/perimeterx/marshmallow v1.1.4 // indirect github.com/perimeterx/marshmallow v1.1.4 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // 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 github.com/russross/blackfriday/v2 v2.1.0 // indirect
go.opencensus.io v0.24.0 // indirect go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.10.0 // indirect golang.org/x/crypto v0.10.0 // indirect

12
go.sum
View file

@ -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/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 h1:vfOA6+wVb6pPQEiXow9f/too92vNTLe9MuwO13PfI0M=
github.com/bep/clock v0.3.0/go.mod h1:6Gz2lapnJ9vxpvPxQ2u6FcXFRoj4kkiqQ6pm0ERZlwk= 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 h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo=
github.com/bep/debounce v1.2.0/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= github.com/bep/debounce v1.2.0/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/bep/gitmap v1.1.2 h1:zk04w1qc1COTZPPYWDQHvns3y1afOsdRfraFQ3qI840= 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/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 h1:HKrlZTrDxHIrNKqmnurH42ryxkngCMYLfBpyu40VcwY=
github.com/bep/lazycache v0.2.0/go.mod h1:xUIsoRD824Vx0Q/n57+ZO7kmbEhMBOnTjM/iPixNGbg= 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 h1:olpCE2WSPpOAbFE1R4hnftSEmQ34+xzy2HRzd0m69rA=
github.com/bep/mclib v1.20400.20402/go.mod h1:pkrk9Kyfqg34Uj6XlDq9tdEFJBiL1FvCoCgVKRzw1EY= github.com/bep/mclib v1.20400.20402/go.mod h1:pkrk9Kyfqg34Uj6XlDq9tdEFJBiL1FvCoCgVKRzw1EY=
github.com/bep/overlayfs v0.6.0 h1:sgLcq/qtIzbaQNl2TldGXOkHvqeZB025sPvHOQL+DYo= 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.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.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 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.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.2/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= 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.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.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.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.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.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 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.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.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.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 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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= 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.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-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.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/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 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 h1:f9CEt3DOB2mnHxZaftmEOFWjABEvKM/xpf3cUwJrGOY=
github.com/spf13/fsync v0.9.0/go.mod h1:fNtJEfG3HiltN3y4cPOz6MLjos9+2pIEqLIgszqhp/0= 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.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/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 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= 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-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-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-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-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.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View file

@ -24,13 +24,11 @@ import (
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
"sync"
"unicode" "unicode"
"unicode/utf8" "unicode/utf8"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/loggers"
"github.com/spf13/afero" "github.com/spf13/afero"
@ -254,143 +252,6 @@ func compareStringSlices(a, b []string) bool {
return true return true
} }
// DistinctLogger ignores duplicate log statements.
type DistinctLogger struct {
loggers.Logger
sync.RWMutex
m map[string]bool
}
func (l *DistinctLogger) Reset() {
l.Lock()
defer l.Unlock()
l.m = make(map[string]bool)
}
// Println will log the string returned from fmt.Sprintln given the arguments,
// but not if it has been logged before.
func (l *DistinctLogger) Println(v ...any) {
// fmt.Sprint doesn't add space between string arguments
logStatement := strings.TrimSpace(fmt.Sprintln(v...))
l.printIfNotPrinted("println", logStatement, func() {
l.Logger.Println(logStatement)
})
}
// Printf will log the string returned from fmt.Sprintf given the arguments,
// but not if it has been logged before.
func (l *DistinctLogger) Printf(format string, v ...any) {
logStatement := fmt.Sprintf(format, v...)
l.printIfNotPrinted("printf", logStatement, func() {
l.Logger.Printf(format, v...)
})
}
func (l *DistinctLogger) Debugf(format string, v ...any) {
logStatement := fmt.Sprintf(format, v...)
l.printIfNotPrinted("debugf", logStatement, func() {
l.Logger.Debugf(format, v...)
})
}
func (l *DistinctLogger) Debugln(v ...any) {
logStatement := fmt.Sprint(v...)
l.printIfNotPrinted("debugln", logStatement, func() {
l.Logger.Debugln(v...)
})
}
func (l *DistinctLogger) Infof(format string, v ...any) {
logStatement := fmt.Sprintf(format, v...)
l.printIfNotPrinted("info", logStatement, func() {
l.Logger.Infof(format, v...)
})
}
func (l *DistinctLogger) Infoln(v ...any) {
logStatement := fmt.Sprint(v...)
l.printIfNotPrinted("infoln", logStatement, func() {
l.Logger.Infoln(v...)
})
}
func (l *DistinctLogger) Warnf(format string, v ...any) {
logStatement := fmt.Sprintf(format, v...)
l.printIfNotPrinted("warnf", logStatement, func() {
l.Logger.Warnf(format, v...)
})
}
func (l *DistinctLogger) Warnln(v ...any) {
logStatement := fmt.Sprint(v...)
l.printIfNotPrinted("warnln", logStatement, func() {
l.Logger.Warnln(v...)
})
}
func (l *DistinctLogger) Errorf(format string, v ...any) {
logStatement := fmt.Sprint(v...)
l.printIfNotPrinted("errorf", logStatement, func() {
l.Logger.Errorf(format, v...)
})
}
func (l *DistinctLogger) Errorln(v ...any) {
logStatement := fmt.Sprint(v...)
l.printIfNotPrinted("errorln", logStatement, func() {
l.Logger.Errorln(v...)
})
}
func (l *DistinctLogger) hasPrinted(key string) bool {
l.RLock()
defer l.RUnlock()
_, found := l.m[key]
return found
}
func (l *DistinctLogger) printIfNotPrinted(level, logStatement string, print func()) {
key := level + logStatement
if l.hasPrinted(key) {
return
}
l.Lock()
defer l.Unlock()
l.m[key] = true // Placing this after print() can cause duplicate warning entries to be logged when --panicOnWarning is true.
print()
}
// NewDistinctErrorLogger creates a new DistinctLogger that logs ERRORs
func NewDistinctErrorLogger() loggers.Logger {
return &DistinctLogger{m: make(map[string]bool), Logger: loggers.NewErrorLogger()}
}
// NewDistinctLogger creates a new DistinctLogger that logs to the provided logger.
func NewDistinctLogger(logger loggers.Logger) loggers.Logger {
return &DistinctLogger{m: make(map[string]bool), Logger: logger}
}
// NewDistinctWarnLogger creates a new DistinctLogger that logs WARNs
func NewDistinctWarnLogger() loggers.Logger {
return &DistinctLogger{m: make(map[string]bool), Logger: loggers.NewWarningLogger()}
}
var (
// DistinctErrorLog can be used to avoid spamming the logs with errors.
DistinctErrorLog = NewDistinctErrorLogger()
// DistinctWarnLog can be used to avoid spamming the logs with warnings.
DistinctWarnLog = NewDistinctWarnLogger()
)
// InitLoggers resets the global distinct loggers.
func InitLoggers() {
DistinctErrorLog.Reset()
DistinctWarnLog.Reset()
}
// Deprecated informs about a deprecation, but only once for a given set of arguments' values. // Deprecated informs about a deprecation, but only once for a given set of arguments' values.
// If the err flag is enabled, it logs as an ERROR (will exit with -1) and the text will // If the err flag is enabled, it logs as an ERROR (will exit with -1) and the text will
// point at the next Hugo release. // point at the next Hugo release.
@ -398,13 +259,9 @@ func InitLoggers() {
// plenty of time to fix their templates. // plenty of time to fix their templates.
func Deprecated(item, alternative string, err bool) { func Deprecated(item, alternative string, err bool) {
if err { if err {
DistinctErrorLog.Errorf("%s is deprecated and will be removed in Hugo %s. %s", item, hugo.CurrentVersion.Next().ReleaseVersion(), alternative) loggers.Log().Errorf("%s is deprecated and will be removed in Hugo %s. %s", item, hugo.CurrentVersion.Next().ReleaseVersion(), alternative)
} else { } else {
var warnPanicMessage string loggers.Log().Warnf("%s is deprecated and will be removed in a future release. %s%s", item, alternative)
if !loggers.PanicOnWarning.Load() {
warnPanicMessage = "\n\nRe-run Hugo with the flag --panicOnWarning to get a better error message."
}
DistinctWarnLog.Warnf("%s is deprecated and will be removed in a future release. %s%s", item, alternative, warnPanicMessage)
} }
} }

View file

@ -18,9 +18,7 @@ import (
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
"time"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/helpers"
qt "github.com/frankban/quicktest" 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) { func TestFirstUpper(t *testing.T) {
for i, this := range []struct { for i, this := range []struct {
in string in string

View file

@ -23,7 +23,7 @@ func newTestPathSpecFromCfgAndLang(cfg config.Provider, lang string) *helpers.Pa
} }
} }
fs := hugofs.NewFrom(mfs, conf.BaseConfig()) 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 { if err != nil {
panic(err) panic(err)
} }
@ -41,7 +41,7 @@ func newTestPathSpec(configKeyValues ...any) *helpers.PathSpec {
func newTestContentSpec(cfg config.Provider) *helpers.ContentSpec { func newTestContentSpec(cfg config.Provider) *helpers.ContentSpec {
fs := afero.NewMemMapFs() fs := afero.NewMemMapFs()
conf := testconfig.GetTestConfig(fs, cfg) 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 { if err != nil {
panic(err) panic(err)
} }

View file

@ -19,7 +19,6 @@ import (
"path/filepath" "path/filepath"
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
"github.com/spf13/afero" "github.com/spf13/afero"
) )

View file

@ -18,8 +18,8 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/bep/logg"
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/htesting" "github.com/gohugoio/hugo/htesting"
"github.com/spf13/afero" "github.com/spf13/afero"
@ -64,11 +64,11 @@ func TestNoSymlinkFs(t *testing.T) {
blogDir := filepath.Join(workDir, "blog") blogDir := filepath.Join(workDir, "blog")
blogFile1 := filepath.Join(blogDir, "a.txt") blogFile1 := filepath.Join(blogDir, "a.txt")
logger := loggers.NewWarningLogger() logger := loggers.NewDefault()
for _, bfs := range []afero.Fs{NewBaseFileDecorator(Os), Os} { for _, bfs := range []afero.Fs{NewBaseFileDecorator(Os), Os} {
for _, allowFiles := range []bool{false, true} { for _, allowFiles := range []bool{false, true} {
logger.LogCounters().WarnCounter.Reset() logger.Reset()
fs := NewNoSymlinkFs(bfs, logger, allowFiles) fs := NewNoSymlinkFs(bfs, logger, allowFiles)
ls := fs.(afero.Lstater) ls := fs.(afero.Lstater)
symlinkedDir := filepath.Join(workDir, "symlinkdedir") symlinkedDir := filepath.Join(workDir, "symlinkdedir")
@ -139,7 +139,7 @@ func TestNoSymlinkFs(t *testing.T) {
_, err = f.Readdir(-1) _, err = f.Readdir(-1)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
f.Close() f.Close()
c.Assert(logger.LogCounters().WarnCounter.Count(), qt.Equals, uint64(1)) c.Assert(logger.LoggCount(logg.LevelWarn), qt.Equals, 1)
} }
} }

View file

@ -86,7 +86,7 @@ func NewWalkway(cfg WalkwayConfig) *Walkway {
logger := cfg.Logger logger := cfg.Logger
if logger == nil { if logger == nil {
logger = loggers.NewWarningLogger() logger = loggers.NewDefault()
} }
return &Walkway{ return &Walkway{

View file

@ -25,7 +25,6 @@ import (
"strings" "strings"
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/output" "github.com/gohugoio/hugo/output"
"github.com/gohugoio/hugo/publisher" "github.com/gohugoio/hugo/publisher"
"github.com/gohugoio/hugo/resources/page" "github.com/gohugoio/hugo/resources/page"

View file

@ -18,9 +18,8 @@ import (
"runtime" "runtime"
"testing" "testing"
"github.com/gohugoio/hugo/common/loggers"
qt "github.com/frankban/quicktest" qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/loggers"
) )
const pageWithAlias = `--- const pageWithAlias = `---
@ -117,7 +116,7 @@ func TestAliasTemplate(t *testing.T) {
} }
func TestTargetPathHTMLRedirectAlias(t *testing.T) { func TestTargetPathHTMLRedirectAlias(t *testing.T) {
h := newAliasHandler(nil, loggers.NewErrorLogger(), false) h := newAliasHandler(nil, loggers.NewDefault(), false)
errIsNilForThisOS := runtime.GOOS != "windows" errIsNilForThisOS := runtime.GOOS != "windows"

View file

@ -20,6 +20,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/bep/logg"
"github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/config/allconfig" "github.com/gohugoio/hugo/config/allconfig"
@ -933,7 +934,7 @@ LanguageCode: {{ eq site.LanguageCode site.Language.LanguageCode }}|{{ site.Lang
).Build() ).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", ` b.AssertFileContent("public/index.html", `
AllPages: 4| AllPages: 4|

View file

@ -29,9 +29,9 @@ import (
"github.com/gohugoio/hugo/hugofs/glob" "github.com/gohugoio/hugo/hugofs/glob"
"github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/common/loggers"
"github.com/rogpeppe/go-internal/lockedfile" "github.com/rogpeppe/go-internal/lockedfile"
"github.com/gohugoio/hugo/hugofs/files" "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) { func NewBase(p *paths.Paths, logger loggers.Logger, options ...func(*BaseFs) error) (*BaseFs, error) {
fs := p.Fs fs := p.Fs
if logger == nil { if logger == nil {
logger = loggers.NewWarningLogger() logger = loggers.NewDefault()
} }
publishFs := hugofs.NewBaseFileDecorator(fs.PublishDir) publishFs := hugofs.NewBaseFileDecorator(fs.PublishDir)

View file

@ -22,16 +22,16 @@ import (
"testing" "testing"
"time" "time"
"github.com/bep/logg"
"github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/modules/npm" "github.com/gohugoio/hugo/modules/npm"
"github.com/gohugoio/hugo/common/loggers"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/gohugoio/hugo/hugofs/files" "github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/htesting" "github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/hugofs"
@ -646,14 +646,14 @@ min_version = 0.55.0
`) `)
logger := loggers.NewWarningLogger() logger := loggers.NewDefault()
b.WithLogger(logger) b.WithLogger(logger)
b.Build(BuildCfg{}) b.Build(BuildCfg{})
c := qt.New(t) 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) { func TestModulesSymlinks(t *testing.T) {
@ -727,7 +727,7 @@ weight = 2
` `
b := newTestSitesBuilder(t).WithNothingAdded().WithWorkingDir(workingDir) b := newTestSitesBuilder(t).WithNothingAdded().WithWorkingDir(workingDir)
b.WithLogger(loggers.NewErrorLogger()) b.WithLogger(loggers.NewDefault())
b.Fs = fs b.Fs = fs
b.WithConfigFile("toml", config) b.WithConfigFile("toml", config)

View file

@ -23,6 +23,7 @@ import (
"sync" "sync"
"sync/atomic" "sync/atomic"
"github.com/bep/logg"
"github.com/gohugoio/hugo/config/allconfig" "github.com/gohugoio/hugo/config/allconfig"
"github.com/gohugoio/hugo/hugofs/glob" "github.com/gohugoio/hugo/hugofs/glob"
@ -42,7 +43,6 @@ import (
"github.com/gohugoio/hugo/source" "github.com/gohugoio/hugo/source"
"github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/lazy" "github.com/gohugoio/hugo/lazy"
@ -265,7 +265,7 @@ func (h *HugoSites) NumLogErrors() int {
if h == nil { if h == nil {
return 0 return 0
} }
return int(h.Log.LogCounters().ErrorCounter.Count()) return h.Log.LoggCount(logg.LevelError)
} }
func (h *HugoSites) PrintProcessingStats(w io.Writer) { func (h *HugoSites) PrintProcessingStats(w io.Writer) {
@ -352,10 +352,8 @@ func (h *HugoSites) reset(config *BuildCfg) {
// resetLogs resets the log counters etc. Used to do a new build on the same sites. // resetLogs resets the log counters etc. Used to do a new build on the same sites.
func (h *HugoSites) resetLogs() { func (h *HugoSites) resetLogs() {
h.Log.Reset() h.Log.Reset()
loggers.GlobalErrorCounter.Reset()
for _, s := range h.Sites { for _, s := range h.Sites {
s.Deps.Log.Reset() s.Deps.Log.Reset()
s.Deps.LogDistinct.Reset()
} }
} }

View file

@ -22,6 +22,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/bep/logg"
"github.com/gohugoio/hugo/langs" "github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/publisher" "github.com/gohugoio/hugo/publisher"
@ -65,6 +66,8 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
defer unlock() defer unlock()
} }
infol := h.Log.InfoCommand("build")
errCollector := h.StartErrorCollector() errCollector := h.StartErrorCollector()
errs := make(chan error) errs := make(chan error)
@ -120,11 +123,11 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
return nil return nil
} }
if err := h.process(conf, init, events...); err != nil { if err := h.process(infol, conf, init, events...); err != nil {
return fmt.Errorf("process: %w", err) return fmt.Errorf("process: %w", err)
} }
if err := h.assemble(conf); err != nil { if err := h.assemble(infol, conf); err != nil {
return fmt.Errorf("assemble: %w", err) return fmt.Errorf("assemble: %w", err)
} }
@ -137,10 +140,10 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
} }
if prepareErr == nil { if prepareErr == nil {
if err := h.render(conf); err != nil { if err := h.render(infol, conf); err != nil {
h.SendError(fmt.Errorf("render: %w", err)) h.SendError(fmt.Errorf("render: %w", err))
} }
if err := h.postProcess(); err != nil { if err := h.postProcess(infol); err != nil {
h.SendError(fmt.Errorf("postProcess: %w", err)) h.SendError(fmt.Errorf("postProcess: %w", err))
} }
} }
@ -164,7 +167,7 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
return err return err
} }
errorCount := h.Log.LogCounters().ErrorCounter.Count() errorCount := h.Log.LoggCount(logg.LevelError)
if errorCount > 0 { if errorCount > 0 {
return fmt.Errorf("logged %d error(s)", errorCount) return fmt.Errorf("logged %d error(s)", errorCount)
} }
@ -195,13 +198,12 @@ func (h *HugoSites) initRebuild(config *BuildCfg) error {
h.reset(config) h.reset(config)
h.resetLogs() h.resetLogs()
helpers.InitLoggers()
return nil return nil
} }
func (h *HugoSites) process(config *BuildCfg, init func(config *BuildCfg) error, events ...fsnotify.Event) error { func (h *HugoSites) process(l logg.LevelLogger, config *BuildCfg, init func(config *BuildCfg) error, events ...fsnotify.Event) error {
defer h.timeTrack(time.Now(), "process") defer h.timeTrack(l, time.Now(), "process")
// We should probably refactor the Site and pull up most of the logic from there to here, // We should probably refactor the Site and pull up most of the logic from there to here,
// but that seems like a daunting task. // but that seems like a daunting task.
@ -218,8 +220,8 @@ func (h *HugoSites) process(config *BuildCfg, init func(config *BuildCfg) error,
return firstSite.process(*config) return firstSite.process(*config)
} }
func (h *HugoSites) assemble(bcfg *BuildCfg) error { func (h *HugoSites) assemble(l logg.LevelLogger, bcfg *BuildCfg) error {
defer h.timeTrack(time.Now(), "assemble") defer h.timeTrack(l, time.Now(), "assemble")
if !bcfg.whatChanged.source { if !bcfg.whatChanged.source {
return nil return nil
@ -236,13 +238,13 @@ func (h *HugoSites) assemble(bcfg *BuildCfg) error {
return nil return nil
} }
func (h *HugoSites) timeTrack(start time.Time, name string) { func (h *HugoSites) timeTrack(l logg.LevelLogger, start time.Time, name string) {
elapsed := time.Since(start) elapsed := time.Since(start)
h.Log.Infof("%s in %v ms\n", name, int(1000*elapsed.Seconds())) l.WithField("step", name).WithField("duration", elapsed).Logf("running")
} }
func (h *HugoSites) render(config *BuildCfg) error { func (h *HugoSites) render(l logg.LevelLogger, config *BuildCfg) error {
defer h.timeTrack(time.Now(), "render") defer h.timeTrack(l, time.Now(), "render")
if _, err := h.init.layouts.Do(context.Background()); err != nil { if _, err := h.init.layouts.Do(context.Background()); err != nil {
return err return err
} }
@ -312,8 +314,8 @@ func (h *HugoSites) render(config *BuildCfg) error {
return nil return nil
} }
func (h *HugoSites) postProcess() error { func (h *HugoSites) postProcess(l logg.LevelLogger) error {
defer h.timeTrack(time.Now(), "postProcess") defer h.timeTrack(l, time.Now(), "postProcess")
// Make sure to write any build stats to disk first so it's available // Make sure to write any build stats to disk first so it's available
// to the post processors. // to the post processors.

View file

@ -1394,7 +1394,7 @@ other = %q
} }
func TestRebuildOnAssetChange(t *testing.T) { func TestRebuildOnAssetChange(t *testing.T) {
b := newTestSitesBuilder(t).Running().WithLogger(loggers.NewInfoLogger()) b := newTestSitesBuilder(t).Running().WithLogger(loggers.NewDefault())
b.WithTemplatesAdded("index.html", ` b.WithTemplatesAdded("index.html", `
{{ (resources.Get "data.json").Content }} {{ (resources.Get "data.json").Content }}
`) `)

View file

@ -13,7 +13,7 @@ import (
"sync" "sync"
"testing" "testing"
jww "github.com/spf13/jwalterweatherman" "github.com/bep/logg"
qt "github.com/frankban/quicktest" qt "github.com/frankban/quicktest"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
@ -292,11 +292,9 @@ func (s *IntegrationTestBuilder) initBuilder() error {
} }
if s.Cfg.LogLevel == 0 { if s.Cfg.LogLevel == 0 {
s.Cfg.LogLevel = jww.LevelWarn s.Cfg.LogLevel = logg.LevelWarn
} }
logger := loggers.NewBasicLoggerForWriter(s.Cfg.LogLevel, &s.logBuff)
isBinaryRe := regexp.MustCompile(`^(.*)(\.png|\.jpg)$`) isBinaryRe := regexp.MustCompile(`^(.*)(\.png|\.jpg)$`)
const dataSourceFilenamePrefix = "sourcefilename:" const dataSourceFilenamePrefix = "sourcefilename:"
@ -350,7 +348,7 @@ func (s *IntegrationTestBuilder) initBuilder() error {
Flags: flags, Flags: flags,
ConfigDir: configDir, ConfigDir: configDir,
Fs: afs, Fs: afs,
Logger: logger, Logger: loggers.NewDefault(),
Environ: s.Cfg.Environ, Environ: s.Cfg.Environ,
}, },
) )
@ -364,7 +362,7 @@ func (s *IntegrationTestBuilder) initBuilder() error {
s.Assert(err, qt.IsNil) s.Assert(err, qt.IsNil)
depsCfg := deps.DepsCfg{Configs: res, Fs: fs, Logger: logger} depsCfg := deps.DepsCfg{Configs: res, Fs: fs, LogLevel: s.Cfg.LogLevel, LogOut: &s.logBuff}
sites, err := NewHugoSites(depsCfg) sites, err := NewHugoSites(depsCfg)
if err != nil { if err != nil {
initErr = err initErr = err
@ -528,7 +526,7 @@ type IntegrationTestConfig struct {
// Will print the log buffer after the build // Will print the log buffer after the build
Verbose bool Verbose bool
LogLevel jww.Threshold LogLevel logg.Level
// Whether it needs the real file system (e.g. for js.Build tests). // Whether it needs the real file system (e.g. for js.Build tests).
NeedsOsFS bool NeedsOsFS bool

View file

@ -20,7 +20,6 @@ import (
"testing" "testing"
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/hugofs/files" "github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/htesting" "github.com/gohugoio/hugo/htesting"
@ -39,7 +38,7 @@ func TestMountFilters(t *testing.T) {
for _, component := range files.ComponentFolders { for _, component := range files.ComponentFolders {
b.Assert(os.MkdirAll(filepath.Join(workingDir, component), 0777), qt.IsNil) 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(` b.WithConfigFile("toml", fmt.Sprintf(`
workingDir = %q workingDir = %q

View file

@ -99,7 +99,7 @@ func newPageFromMeta(
meta map[string]any, meta map[string]any,
metaProvider *pageMeta) (*pageState, error) { metaProvider *pageMeta) (*pageState, error) {
if metaProvider.f == nil { if metaProvider.f == nil {
metaProvider.f = page.NewZeroFile(metaProvider.s.LogDistinct) metaProvider.f = page.NewZeroFile(metaProvider.s.Log)
} }
ps, err := newPageBase(metaProvider) ps, err := newPageBase(metaProvider)

View file

@ -17,7 +17,6 @@ import (
"context" "context"
"fmt" "fmt"
"html/template" "html/template"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"testing" "testing"
@ -36,7 +35,6 @@ import (
"github.com/gohugoio/hugo/resources/page" "github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/hugo/resources/resource" "github.com/gohugoio/hugo/resources/resource"
"github.com/spf13/jwalterweatherman"
qt "github.com/frankban/quicktest" qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/deps" "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) { func TestRenderStringForRegularPageTranslations(t *testing.T) {
c := qt.New(t) c := qt.New(t)
b := newTestSitesBuilder(t) b := newTestSitesBuilder(t)
b.WithLogger(loggers.NewBasicLoggerForWriter(jwalterweatherman.LevelError, os.Stderr)) b.WithLogger(loggers.NewDefault())
b.WithConfigFile("toml", b.WithConfigFile("toml",
`baseurl = "https://example.org/" `baseurl = "https://example.org/"
@ -800,7 +798,7 @@ home = ["HTML", "JSON"]`)
// Issue 8919 // Issue 8919
func TestContentProviderWithCustomOutputFormat(t *testing.T) { func TestContentProviderWithCustomOutputFormat(t *testing.T) {
b := newTestSitesBuilder(t) b := newTestSitesBuilder(t)
b.WithLogger(loggers.NewBasicLoggerForWriter(jwalterweatherman.LevelDebug, os.Stderr)) b.WithLogger(loggers.NewDefault())
b.WithConfigFile("toml", `baseURL = 'http://example.org/' b.WithConfigFile("toml", `baseURL = 'http://example.org/'
title = 'My New Hugo Site' title = 'My New Hugo Site'
@ -1437,7 +1435,7 @@ Content:{{ .Content }}
// https://github.com/gohugoio/hugo/issues/5781 // https://github.com/gohugoio/hugo/issues/5781
func TestPageWithZeroFile(t *testing.T) { 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{}) WithTemplatesAdded("index.html", "{{ .File.Filename }}{{ with .File }}{{ .Dir }}{{ end }}").Build(BuildCfg{})
} }

View file

@ -20,6 +20,9 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"regexp" "regexp"
"github.com/gohugoio/hugo/common/loggers"
"strings" "strings"
"testing" "testing"
@ -31,7 +34,6 @@ import (
"github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/resources/page" "github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/hugo/htesting" "github.com/gohugoio/hugo/htesting"
@ -93,7 +95,7 @@ func TestPageBundlerSiteRegular(t *testing.T) {
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
b := newTestSitesBuilderFromDepsCfg(c, deps.DepsCfg{Logger: loggers.NewErrorLogger(), Fs: fs, Configs: configs}).WithNothingAdded() b := newTestSitesBuilderFromDepsCfg(c, deps.DepsCfg{Fs: fs, Configs: configs}).WithNothingAdded()
b.Build(BuildCfg{}) b.Build(BuildCfg{})
@ -1044,7 +1046,7 @@ title: %q
} }
b := newTestSitesBuilder(t).WithConfigFile("toml", config) b := newTestSitesBuilder(t).WithConfigFile("toml", config)
b.WithLogger(loggers.NewWarningLogger()) b.WithLogger(loggers.NewDefault())
b.WithTemplates("_default/list.html", `{{ range .Site.Pages }} b.WithTemplates("_default/list.html", `{{ range .Site.Pages }}
{{ .Kind }}|{{ .Path }}|{{ with .CurrentSection }}CurrentSection: {{ .Path }}{{ end }}|{{ .RelPermalink }}{{ end }} {{ .Kind }}|{{ .Path }}|{{ with .CurrentSection }}CurrentSection: {{ .Path }}{{ end }}|{{ .RelPermalink }}{{ end }}
@ -1215,7 +1217,7 @@ title: %q
} }
b := newTestSitesBuilder(t).WithConfigFile("toml", config) b := newTestSitesBuilder(t).WithConfigFile("toml", config)
b.WithLogger(loggers.NewWarningLogger()) b.WithLogger(loggers.NewDefault())
b.WithTemplates("_default/single.html", `{{ range .Resources }} b.WithTemplates("_default/single.html", `{{ range .Resources }}
{{ .ResourceType }}|{{ .Title }}| {{ .ResourceType }}|{{ .Title }}|

View file

@ -21,6 +21,7 @@ import (
"reflect" "reflect"
"github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/parser/pageparser" "github.com/gohugoio/hugo/parser/pageparser"
@ -29,7 +30,6 @@ import (
"github.com/gohugoio/hugo/source" "github.com/gohugoio/hugo/source"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/hugofs"
"github.com/spf13/afero" "github.com/spf13/afero"
) )

View file

@ -53,7 +53,7 @@ func TestPagesCapture(t *testing.T) {
t.Run("Collect", func(t *testing.T) { t.Run("Collect", func(t *testing.T) {
c := qt.New(t) c := qt.New(t)
proc := &testPagesCollectorProcessor{} proc := &testPagesCollectorProcessor{}
coll := newPagesCollector(sourceSpec, nil, loggers.NewErrorLogger(), nil, proc) coll := newPagesCollector(sourceSpec, nil, loggers.NewDefault(), nil, proc)
c.Assert(coll.Collect(), qt.IsNil) c.Assert(coll.Collect(), qt.IsNil)
// 2 bundles, 3 pages. // 2 bundles, 3 pages.
c.Assert(len(proc.items), qt.Equals, 5) c.Assert(len(proc.items), qt.Equals, 5)

View file

@ -16,6 +16,7 @@ package hugolib
import ( import (
"testing" "testing"
"github.com/bep/logg"
qt "github.com/frankban/quicktest" qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
) )
@ -80,13 +81,13 @@ func TestRenderStringOnListPage(t *testing.T) {
// Issue 9433 // Issue 9433
func TestRenderStringOnPageNotBackedByAFile(t *testing.T) { func TestRenderStringOnPageNotBackedByAFile(t *testing.T) {
t.Parallel() t.Parallel()
logger := loggers.NewWarningLogger() logger := loggers.NewDefault()
b := newTestSitesBuilder(t).WithLogger(logger).WithConfigFile("toml", ` b := newTestSitesBuilder(t).WithLogger(logger).WithConfigFile("toml", `
disableKinds = ["page", "section", "taxonomy", "term"] disableKinds = ["page", "section", "taxonomy", "term"]
`) `)
b.WithTemplates("index.html", `{{ .RenderString "**Hello**" }}`).WithContent("p1.md", "") b.WithTemplates("index.html", `{{ .RenderString "**Hello**" }}`).WithContent("p1.md", "")
b.BuildE(BuildCfg{}) 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) { func TestRenderStringWithShortcode(t *testing.T) {

View file

@ -588,7 +588,7 @@ XML: {{ $xml.body }}
} }
t.Parallel() t.Parallel()
b := newTestSitesBuilder(t).WithLogger(loggers.NewErrorLogger()) b := newTestSitesBuilder(t).WithLogger(loggers.NewDefault())
b.WithContent("_index.md", ` b.WithContent("_index.md", `
--- ---
title: Home title: Home

View file

@ -17,7 +17,6 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"log"
"mime" "mime"
"net/url" "net/url"
"path" "path"
@ -27,6 +26,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/bep/logg"
"github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/htime" "github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/hugio" "github.com/gohugoio/hugo/common/hugio"
@ -285,7 +285,7 @@ func (s *Site) isEnabled(kind string) bool {
type siteRefLinker struct { type siteRefLinker struct {
s *Site s *Site
errorLogger *log.Logger errorLogger logg.LevelLogger
notFoundURL string 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) { func (s siteRefLinker) logNotFound(ref, what string, p page.Page, position text.Position) {
if position.IsValid() { 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 { } 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 { } else {
s.errorLogger.Printf("[%s] REF_NOT_FOUND: Ref %q from page %q: %s", s.s.Lang(), ref, p.Pathc(), what) s.errorLogger.Logf("[%s] REF_NOT_FOUND: Ref %q from page %q: %s", s.s.Lang(), ref, p.Pathc(), what)
} }
} }
@ -507,9 +507,6 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro
i18nChanged bool i18nChanged bool
sourceFilesChanged = make(map[string]bool) sourceFilesChanged = make(map[string]bool)
// prevent spamming the log on changes
logger = helpers.NewDistinctErrorLogger()
) )
var cacheBusters []func(string) bool var cacheBusters []func(string) bool
@ -531,7 +528,7 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro
switch id.Type { switch id.Type {
case files.ComponentFolderContent: case files.ComponentFolderContent:
logger.Println("Source changed", ev) s.Log.Println("Source changed", ev)
sourceChanged = append(sourceChanged, ev) sourceChanged = append(sourceChanged, ev)
case files.ComponentFolderLayouts: case files.ComponentFolderLayouts:
tmplChanged = true tmplChanged = true
@ -539,16 +536,16 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro
tmplAdded = true tmplAdded = true
} }
if tmplAdded { if tmplAdded {
logger.Println("Template added", ev) s.Log.Println("Template added", ev)
} else { } else {
logger.Println("Template changed", ev) s.Log.Println("Template changed", ev)
} }
case files.ComponentFolderData: case files.ComponentFolderData:
logger.Println("Data changed", ev) s.Log.Println("Data changed", ev)
dataChanged = true dataChanged = true
case files.ComponentFolderI18n: case files.ComponentFolderI18n:
logger.Println("i18n changed", ev) s.Log.Println("i18n changed", ev)
i18nChanged = true i18nChanged = true
} }

View file

@ -18,10 +18,12 @@ import (
"errors" "errors"
"fmt" "fmt"
"html/template" "html/template"
"os"
"sort" "sort"
"time" "time"
radix "github.com/armon/go-radix" radix "github.com/armon/go-radix"
"github.com/bep/logg"
"github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/maps"
@ -100,19 +102,41 @@ func (s *Site) Debug() {
func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) { func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
conf := cfg.Configs.GetFirstLanguageConfig() conf := cfg.Configs.GetFirstLanguageConfig()
logger := cfg.Logger var logger loggers.Logger
if logger == nil { if cfg.TestLogger != nil {
logger = loggers.NewErrorLogger() logger = cfg.TestLogger
} else {
var logHookLast func(e *logg.Entry) error
if cfg.Configs.Base.PanicOnWarning {
logHookLast = loggers.PanicOnWarningHook
}
if cfg.LogOut == nil {
cfg.LogOut = os.Stdout
}
if cfg.LogLevel == 0 {
cfg.LogLevel = logg.LevelWarn
}
logOpts := loggers.Options{
Level: cfg.LogLevel,
Distinct: true, // This will drop duplicate log warning and errors.
HandlerPost: logHookLast,
Stdout: cfg.LogOut,
Stderr: cfg.LogOut,
StoreErrors: conf.Running(),
SuppresssStatements: conf.IgnoredErrors(),
}
logger = loggers.New(logOpts)
} }
ignorableLogger := loggers.NewIgnorableLogger(logger, conf.IgnoredErrors())
firstSiteDeps := &deps.Deps{ firstSiteDeps := &deps.Deps{
Fs: cfg.Fs, Fs: cfg.Fs,
Log: ignorableLogger, Log: logger,
Conf: conf, Conf: conf,
TemplateProvider: tplimpl.DefaultTemplateProvider, TemplateProvider: tplimpl.DefaultTemplateProvider,
TranslationProvider: i18n.NewTranslationProvider(), TranslationProvider: i18n.NewTranslationProvider(),
} }
if err := firstSiteDeps.Init(); err != nil { if err := firstSiteDeps.Init(); err != nil {
return nil, err return nil, err
} }
@ -128,7 +152,7 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
k := language.Lang k := language.Lang
conf := confm.LanguageConfigMap[k] conf := confm.LanguageConfigMap[k]
frontmatterHandler, err := pagemeta.NewFrontmatterHandler(cfg.Logger, conf.Frontmatter) frontmatterHandler, err := pagemeta.NewFrontmatterHandler(firstSiteDeps.Log, conf.Frontmatter)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -209,6 +233,7 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
} }
return h, err return h, err
} }
func newHugoSitesNew(cfg deps.DepsCfg, d *deps.Deps, sites []*Site) (*HugoSites, error) { func newHugoSitesNew(cfg deps.DepsCfg, d *deps.Deps, sites []*Site) (*HugoSites, error) {

View file

@ -181,7 +181,7 @@ func (s *Site) logMissingLayout(name, layout, kind, outputFormat string) {
msg += ": " + errMsg msg += ": " + errMsg
log.Printf(msg, args...) log.Logf(msg, args...)
} }
// renderPaginator must be run after the owning Page has been rendered. // renderPaginator must be run after the owning Page has been rendered.

View file

@ -33,6 +33,7 @@ import (
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
"github.com/gohugoio/hugo/common/hexec" "github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/deps"
@ -47,7 +48,6 @@ import (
"github.com/gohugoio/hugo/resources/resource" "github.com/gohugoio/hugo/resources/resource"
qt "github.com/frankban/quicktest" qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/hugofs"
) )
@ -552,7 +552,7 @@ func (s *sitesBuilder) CreateSitesE() error {
if depsCfg.Configs.IsZero() { if depsCfg.Configs.IsZero() {
depsCfg.Configs = s.Configs depsCfg.Configs = s.Configs
} }
depsCfg.Logger = s.logger depsCfg.TestLogger = s.logger
sites, err := NewHugoSites(depsCfg) sites, err := NewHugoSites(depsCfg)

View file

@ -24,7 +24,6 @@ import (
"github.com/gohugoio/hugo/common/hreflect" "github.com/gohugoio/hugo/common/hreflect"
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/resources/page" "github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/go-i18n/v2/i18n" "github.com/gohugoio/go-i18n/v2/i18n"
@ -32,8 +31,6 @@ import (
type translateFunc func(ctx context.Context, translationID string, templateData any) string type translateFunc func(ctx context.Context, translationID string, templateData any) string
var i18nWarningLogger = helpers.NewDistinctErrorLogger()
// Translator handles i18n translations. // Translator handles i18n translations.
type Translator struct { type Translator struct {
translateFuncs map[string]translateFunc translateFuncs map[string]translateFunc
@ -123,7 +120,7 @@ func (t Translator) initFuncs(bndl *i18n.Bundle) {
} }
if t.cfg.LogI18nWarnings() { 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 { if enableMissingTranslationPlaceholders {

View file

@ -19,12 +19,13 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/bep/logg"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/config/testconfig" "github.com/gohugoio/hugo/config/testconfig"
"github.com/gohugoio/hugo/tpl/tplimpl" "github.com/gohugoio/hugo/tpl/tplimpl"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/resources/page" "github.com/gohugoio/hugo/resources/page"
"github.com/spf13/afero" "github.com/spf13/afero"
@ -34,7 +35,7 @@ import (
"github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config"
) )
var logger = loggers.NewErrorLogger() var logger = loggers.NewDefault()
type i18nTest struct { type i18nTest struct {
name string name string
@ -406,7 +407,7 @@ other = "{{ . }} miesiąca"
for _, variant := range test.variants { 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(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)
} }
}) })

View file

@ -44,7 +44,7 @@ func TestAsciidoctorDefaultArgs(t *testing.T) {
p, err := asciidocext.Provider.New( p, err := asciidocext.Provider.New(
converter.ProviderConfig{ converter.ProviderConfig{
Conf: conf, Conf: conf,
Logger: loggers.NewErrorLogger(), Logger: loggers.NewDefault(),
}, },
) )
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
@ -76,7 +76,7 @@ func TestAsciidoctorNonDefaultArgs(t *testing.T) {
p, err := asciidocext.Provider.New( p, err := asciidocext.Provider.New(
converter.ProviderConfig{ converter.ProviderConfig{
Conf: conf, Conf: conf,
Logger: loggers.NewErrorLogger(), Logger: loggers.NewDefault(),
}, },
) )
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
@ -106,7 +106,7 @@ func TestAsciidoctorDisallowedArgs(t *testing.T) {
p, err := asciidocext.Provider.New( p, err := asciidocext.Provider.New(
converter.ProviderConfig{ converter.ProviderConfig{
Conf: conf, Conf: conf,
Logger: loggers.NewErrorLogger(), Logger: loggers.NewDefault(),
}, },
) )
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
@ -130,7 +130,7 @@ func TestAsciidoctorArbitraryExtension(t *testing.T) {
p, err := asciidocext.Provider.New( p, err := asciidocext.Provider.New(
converter.ProviderConfig{ converter.ProviderConfig{
Conf: conf, Conf: conf,
Logger: loggers.NewErrorLogger(), Logger: loggers.NewDefault(),
}, },
) )
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
@ -164,7 +164,7 @@ func TestAsciidoctorDisallowedExtension(t *testing.T) {
p, err := asciidocext.Provider.New( p, err := asciidocext.Provider.New(
converter.ProviderConfig{ converter.ProviderConfig{
Conf: conf, Conf: conf,
Logger: loggers.NewErrorLogger(), Logger: loggers.NewDefault(),
}, },
) )
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
@ -195,7 +195,7 @@ trace = false
p, err := asciidocext.Provider.New( p, err := asciidocext.Provider.New(
converter.ProviderConfig{ converter.ProviderConfig{
Conf: conf, Conf: conf,
Logger: loggers.NewErrorLogger(), Logger: loggers.NewDefault(),
}, },
) )
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
@ -232,7 +232,7 @@ extensions = ["asciidoctor-html5s", "asciidoctor-diagram"]
p, err := asciidocext.Provider.New( p, err := asciidocext.Provider.New(
converter.ProviderConfig{ converter.ProviderConfig{
Conf: conf, Conf: conf,
Logger: loggers.NewErrorLogger(), Logger: loggers.NewDefault(),
}, },
) )
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
@ -272,7 +272,7 @@ my-attribute-name = "my value"
p, err := asciidocext.Provider.New( p, err := asciidocext.Provider.New(
converter.ProviderConfig{ converter.ProviderConfig{
Conf: conf, Conf: conf,
Logger: loggers.NewErrorLogger(), Logger: loggers.NewDefault(),
}, },
) )
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
@ -311,7 +311,7 @@ allow = ['asciidoctor']
p, err := asciidocext.Provider.New( p, err := asciidocext.Provider.New(
converter.ProviderConfig{ converter.ProviderConfig{
Logger: loggers.NewErrorLogger(), Logger: loggers.NewDefault(),
Conf: conf, Conf: conf,
Exec: hexec.New(securityConfig), Exec: hexec.New(securityConfig),
}, },

View file

@ -46,7 +46,7 @@ noclasses=false
func convert(c *qt.C, conf config.AllProvider, content string) converter.ResultRender { func convert(c *qt.C, conf config.AllProvider, content string) converter.ResultRender {
pconf := converter.ProviderConfig{ pconf := converter.ProviderConfig{
Logger: loggers.NewErrorLogger(), Logger: loggers.NewDefault(),
Conf: conf, Conf: conf,
} }
@ -442,7 +442,7 @@ LINE5
conf := testconfig.GetTestConfig(nil, cfg) conf := testconfig.GetTestConfig(nil, cfg)
pcfg := converter.ProviderConfig{ pcfg := converter.ProviderConfig{
Conf: conf, Conf: conf,
Logger: loggers.NewErrorLogger(), Logger: loggers.NewDefault(),
} }
p, err := goldmark.Provider.New( p, err := goldmark.Provider.New(
pcfg, pcfg,

View file

@ -17,12 +17,11 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config/testconfig" "github.com/gohugoio/hugo/config/testconfig"
"github.com/gohugoio/hugo/markup/converter/hooks" "github.com/gohugoio/hugo/markup/converter/hooks"
"github.com/gohugoio/hugo/markup/goldmark" "github.com/gohugoio/hugo/markup/goldmark"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/markup/converter" "github.com/gohugoio/hugo/markup/converter"
qt "github.com/frankban/quicktest" qt "github.com/frankban/quicktest"
@ -56,7 +55,7 @@ And then some.
p, err := goldmark.Provider.New( p, err := goldmark.Provider.New(
converter.ProviderConfig{ converter.ProviderConfig{
Conf: testconfig.GetTestConfig(nil, nil), Conf: testconfig.GetTestConfig(nil, nil),
Logger: loggers.NewErrorLogger(), Logger: loggers.NewDefault(),
}) })
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
conv, err := p.New(converter.DocumentContext{}) conv, err := p.New(converter.DocumentContext{})
@ -86,12 +85,12 @@ func TestEscapeToc(t *testing.T) {
safeP, _ := goldmark.Provider.New( safeP, _ := goldmark.Provider.New(
converter.ProviderConfig{ converter.ProviderConfig{
Conf: safeConf(), Conf: safeConf(),
Logger: loggers.NewErrorLogger(), Logger: loggers.NewDefault(),
}) })
unsafeP, _ := goldmark.Provider.New( unsafeP, _ := goldmark.Provider.New(
converter.ProviderConfig{ converter.ProviderConfig{
Conf: unsafeConf(), Conf: unsafeConf(),
Logger: loggers.NewErrorLogger(), Logger: loggers.NewDefault(),
}) })
safeConv, _ := safeP.New(converter.DocumentContext{}) safeConv, _ := safeP.New(converter.DocumentContext{})
unsafeConv, _ := unsafeP.New(converter.DocumentContext{}) unsafeConv, _ := unsafeP.New(converter.DocumentContext{})

View file

@ -16,6 +16,7 @@ package org
import ( import (
"bytes" "bytes"
"log"
"github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/identity"
@ -46,7 +47,7 @@ type orgConverter struct {
func (c *orgConverter) Convert(ctx converter.RenderContext) (converter.ResultRender, error) { func (c *orgConverter) Convert(ctx converter.RenderContext) (converter.ResultRender, error) {
logger := c.cfg.Logger logger := c.cfg.Logger
config := org.New() config := org.New()
config.Log = logger.Warn() config.Log = log.Default() // TODO(bep)
config.ReadFile = func(filename string) ([]byte, error) { config.ReadFile = func(filename string) ([]byte, error) {
return afero.ReadFile(c.cfg.ContentFs, filename) return afero.ReadFile(c.cfg.ContentFs, filename)
} }

View file

@ -29,7 +29,7 @@ import (
func TestConvert(t *testing.T) { func TestConvert(t *testing.T) {
c := qt.New(t) c := qt.New(t)
p, err := org.Provider.New(converter.ProviderConfig{ p, err := org.Provider.New(converter.ProviderConfig{
Logger: loggers.NewErrorLogger(), Logger: loggers.NewDefault(),
Conf: testconfig.GetTestConfig(afero.NewMemMapFs(), nil), Conf: testconfig.GetTestConfig(afero.NewMemMapFs(), nil),
}) })
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)

View file

@ -32,7 +32,7 @@ func TestConvert(t *testing.T) {
c := qt.New(t) c := qt.New(t)
sc := security.DefaultConfig sc := security.DefaultConfig
sc.Exec.Allow = security.NewWhitelist("pandoc") 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) c.Assert(err, qt.IsNil)
conv, err := p.New(converter.DocumentContext{}) conv, err := p.New(converter.DocumentContext{})
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)

View file

@ -35,7 +35,7 @@ func TestConvert(t *testing.T) {
p, err := Provider.New( p, err := Provider.New(
converter.ProviderConfig{ converter.ProviderConfig{
Logger: loggers.NewErrorLogger(), Logger: loggers.NewDefault(),
Exec: hexec.New(sc), Exec: hexec.New(sc),
}) })
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)

View file

@ -30,6 +30,7 @@ import (
"github.com/gohugoio/hugo/common/collections" "github.com/gohugoio/hugo/common/collections"
"github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/hexec" "github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
hglob "github.com/gohugoio/hugo/hugofs/glob" hglob "github.com/gohugoio/hugo/hugofs/glob"
@ -39,8 +40,6 @@ import (
"github.com/gohugoio/hugo/hugofs/files" "github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config"
"github.com/rogpeppe/go-internal/module" "github.com/rogpeppe/go-internal/module"
@ -98,7 +97,7 @@ func NewClient(cfg ClientConfig) *Client {
logger := cfg.Logger logger := cfg.Logger
if logger == nil { if logger == nil {
logger = loggers.NewWarningLogger() logger = loggers.NewDefault()
} }
var noVendor glob.Glob var noVendor glob.Glob

View file

@ -18,6 +18,7 @@ import (
"encoding/csv" "encoding/csv"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"regexp" "regexp"
"strings" "strings"
@ -28,7 +29,6 @@ import (
toml "github.com/pelletier/go-toml/v2" toml "github.com/pelletier/go-toml/v2"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cast" "github.com/spf13/cast"
jww "github.com/spf13/jwalterweatherman"
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
) )
@ -231,7 +231,7 @@ func parseORGDate(s string) string {
func (d Decoder) unmarshalORG(data []byte, v any) error { func (d Decoder) unmarshalORG(data []byte, v any) error {
config := org.New() config := org.New()
config.Log = jww.WARN config.Log = log.Default() // TODO(bep)
document := config.Parse(bytes.NewReader(data), "") document := config.Parse(bytes.NewReader(data), "")
if document.Error != nil { if document.Error != nil {
return document.Error return document.Error
@ -242,7 +242,7 @@ func (d Decoder) unmarshalORG(data []byte, v any) error {
if strings.HasSuffix(k, "[]") { if strings.HasSuffix(k, "[]") {
frontMatter[k[:len(k)-2]] = strings.Fields(v) frontMatter[k[:len(k)-2]] = strings.Fields(v)
} else if k == "tags" || k == "categories" || k == "aliases" { } 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) frontMatter[k] = strings.Fields(v)
} else if k == "date" || k == "lastmod" || k == "publishdate" || k == "expirydate" { } else if k == "date" || k == "lastmod" || k == "publishdate" || k == "expirydate" {
frontMatter[k] = parseORGDate(v) frontMatter[k] = parseORGDate(v)

View file

@ -18,9 +18,9 @@ import (
"time" "time"
"github.com/gohugoio/hugo/common/htime" "github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/resources/resource" "github.com/gohugoio/hugo/resources/resource"
@ -270,7 +270,7 @@ func toLowerSlice(in any) []string {
// If no logger is provided, one will be created. // If no logger is provided, one will be created.
func NewFrontmatterHandler(logger loggers.Logger, frontMatterConfig FrontmatterConfig) (FrontMatterHandler, error) { func NewFrontmatterHandler(logger loggers.Logger, frontMatterConfig FrontmatterConfig) (FrontMatterHandler, error) {
if logger == nil { if logger == nil {
logger = loggers.NewErrorLogger() logger = loggers.NewDefault()
} }
allDateKeys := make(map[string]bool) allDateKeys := make(map[string]bool)

View file

@ -30,7 +30,7 @@ func newTestPathSpecFor(cfg config.Provider) *helpers.PathSpec {
mfs := afero.NewMemMapFs() mfs := afero.NewMemMapFs()
conf := testconfig.GetTestConfig(mfs, cfg) conf := testconfig.GetTestConfig(mfs, cfg)
fs := hugofs.NewFrom(mfs, conf.BaseConfig()) 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 { if err != nil {
panic(err) panic(err)
} }

View file

@ -31,6 +31,7 @@ import (
"github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/hexec" "github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/identity"
@ -39,7 +40,6 @@ import (
"github.com/gohugoio/hugo/resources/postpub" "github.com/gohugoio/hugo/resources/postpub"
"github.com/gohugoio/hugo/cache/filecache" "github.com/gohugoio/hugo/cache/filecache"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/resources/images" "github.com/gohugoio/hugo/resources/images"
"github.com/gohugoio/hugo/resources/page" "github.com/gohugoio/hugo/resources/page"
@ -75,7 +75,7 @@ func NewSpec(
} }
if logger == nil { if logger == nil {
logger = loggers.NewErrorLogger() logger = loggers.NewDefault()
} }
permalinks, err := page.NewPermalinkExpander(s.URLize, conf.Permalinks) permalinks, err := page.NewPermalinkExpander(s.URLize, conf.Permalinks)

View file

@ -122,10 +122,10 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
} }
var configFile string var configFile string
logger := t.rs.Logger infol := t.rs.Logger.InfoCommand(binaryName)
infoW := loggers.LevelLoggerToWriter(infol)
var errBuf bytes.Buffer var errBuf bytes.Buffer
infoW := loggers.LoggerToWriterWithPrefix(logger.Info(), "babel")
if t.options.Config != "" { if t.options.Config != "" {
configFile = t.options.Config configFile = t.options.Config
@ -149,7 +149,7 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
var cmdArgs []any var cmdArgs []any
if configFile != "" { if configFile != "" {
logger.Infoln("babel: use config file", configFile) infol.Logf("use config file %q", configFile)
cmdArgs = []any{"--config-file", configFile} cmdArgs = []any{"--config-file", configFile}
} }

View file

@ -16,8 +16,7 @@ package babel_test
import ( import (
"testing" "testing"
jww "github.com/spf13/jwalterweatherman" "github.com/bep/logg"
"github.com/gohugoio/hugo/htesting" "github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/hugolib" "github.com/gohugoio/hugo/hugolib"
) )
@ -82,7 +81,7 @@ Transpiled3: {{ $transpiled.Permalink }}
TxtarString: files, TxtarString: files,
NeedsOsFS: true, NeedsOsFS: true,
NeedsNpmInstall: true, NeedsNpmInstall: true,
LogLevel: jww.LevelInfo, LogLevel: logg.LevelInfo,
}).Build() }).Build()
b.AssertLogContains("babel: Hugo Environment: production") b.AssertLogContains("babel: Hugo Environment: production")

View file

@ -16,11 +16,11 @@ package postcss_test
import ( import (
"fmt" "fmt"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"testing" "testing"
jww "github.com/spf13/jwalterweatherman" "github.com/bep/logg"
qt "github.com/frankban/quicktest" qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/htesting" "github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/hugofs"
@ -124,7 +124,7 @@ func TestTransformPostCSS(t *testing.T) {
T: c, T: c,
NeedsOsFS: true, NeedsOsFS: true,
NeedsNpmInstall: true, NeedsNpmInstall: true,
LogLevel: jww.LevelInfo, LogLevel: logg.LevelInfo,
WorkingDir: tempDir, WorkingDir: tempDir,
TxtarString: files, TxtarString: files,
}).Build() }).Build()
@ -148,6 +148,11 @@ func TestTransformPostCSSError(t *testing.T) {
t.Skip("Skip long running test when running locally") t.Skip("Skip long running test when running locally")
} }
if runtime.GOOS == "windows" {
//TODO(bep) This has started to fail on Windows with Go 1.19 on GitHub Actions for some mysterious reason.
t.Skip("Skip on Windows")
}
c := qt.New(t) c := qt.New(t)
s, err := hugolib.NewIntegrationTestBuilder( s, err := hugolib.NewIntegrationTestBuilder(
@ -176,7 +181,7 @@ func TestTransformPostCSSImportError(t *testing.T) {
T: c, T: c,
NeedsOsFS: true, NeedsOsFS: true,
NeedsNpmInstall: true, NeedsNpmInstall: true,
LogLevel: jww.LevelInfo, LogLevel: logg.LevelInfo,
TxtarString: strings.ReplaceAll(postCSSIntegrationTestFiles, `@import "components/all.css";`, `@import "components/doesnotexist.css";`), TxtarString: strings.ReplaceAll(postCSSIntegrationTestFiles, `@import "components/all.css";`, `@import "components/doesnotexist.css";`),
}).BuildE() }).BuildE()
@ -201,7 +206,7 @@ func TestTransformPostCSSImporSkipInlineImportsNotFound(t *testing.T) {
T: c, T: c,
NeedsOsFS: true, NeedsOsFS: true,
NeedsNpmInstall: true, NeedsNpmInstall: true,
LogLevel: jww.LevelInfo, LogLevel: logg.LevelInfo,
TxtarString: files, TxtarString: files,
}).Build() }).Build()
@ -233,7 +238,7 @@ func TestTransformPostCSSResourceCacheWithPathInBaseURL(t *testing.T) {
T: c, T: c,
NeedsOsFS: true, NeedsOsFS: true,
NeedsNpmInstall: true, NeedsNpmInstall: true,
LogLevel: jww.LevelInfo, LogLevel: logg.LevelInfo,
TxtarString: files, TxtarString: files,
WorkingDir: tempDir, WorkingDir: tempDir,
}).Build() }).Build()

View file

@ -27,13 +27,12 @@ import (
"github.com/gohugoio/hugo/common/collections" "github.com/gohugoio/hugo/common/collections"
"github.com/gohugoio/hugo/common/hexec" "github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/text" "github.com/gohugoio/hugo/common/text"
"github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/resources/internal" "github.com/gohugoio/hugo/resources/internal"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cast" "github.com/spf13/cast"
@ -151,10 +150,12 @@ func (t *postcssTransformation) Key() internal.ResourceTransformationKey {
func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationCtx) error { func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationCtx) error {
const binaryName = "postcss" const binaryName = "postcss"
infol := t.rs.Logger.InfoCommand(binaryName)
infoW := loggers.LevelLoggerToWriter(infol)
ex := t.rs.ExecHelper ex := t.rs.ExecHelper
var configFile string var configFile string
logger := t.rs.Logger
var options Options var options Options
if t.optionsm != nil { if t.optionsm != nil {
@ -185,7 +186,7 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC
var cmdArgs []any var cmdArgs []any
if configFile != "" { if configFile != "" {
logger.Infoln("postcss: use config file", configFile) infol.Logf("use config file %q", configFile)
cmdArgs = []any{"--config", configFile} cmdArgs = []any{"--config", configFile}
} }
@ -194,7 +195,6 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC
} }
var errBuf bytes.Buffer var errBuf bytes.Buffer
infoW := loggers.LoggerToWriterWithPrefix(logger.Info(), "postcss")
stderr := io.MultiWriter(infoW, &errBuf) stderr := io.MultiWriter(infoW, &errBuf)
cmdArgs = append(cmdArgs, hexec.WithStderr(stderr)) cmdArgs = append(cmdArgs, hexec.WithStderr(stderr))
@ -401,7 +401,6 @@ func (imp *importResolver) shouldImport(s string) bool {
} }
func (imp *importResolver) toFileError(output string) error { func (imp *importResolver) toFileError(output string) error {
output = strings.TrimSpace(loggers.RemoveANSIColours(output))
inErr := errors.New(output) inErr := errors.New(output)
match := cssSyntaxErrorRe.FindStringSubmatch(output) match := cssSyntaxErrorRe.FindStringSubmatch(output)

View file

@ -18,9 +18,9 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/htesting/hqt" "github.com/gohugoio/hugo/htesting/hqt"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/helpers"
"github.com/spf13/afero" "github.com/spf13/afero"
@ -95,7 +95,7 @@ LOCAL_STYLE
mainStyles, mainStyles,
"styles.css", "styles.css",
Options{}, Options{},
fs, loggers.NewErrorLogger(), fs, loggers.NewDefault(),
) )
r, err := imp.resolve() r, err := imp.resolve()
@ -144,7 +144,7 @@ LOCAL_STYLE
@import "e.css"; @import "e.css";
@import "missing.css";` @import "missing.css";`
logger := loggers.NewErrorLogger() logger := loggers.NewDefault()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
b.StopTimer() b.StopTimer()

View file

@ -58,6 +58,8 @@ func New(fs *filesystems.SourceFilesystem, rs *resources.Spec) (*Client, error)
transpiler *godartsass.Transpiler transpiler *godartsass.Transpiler
transpilerv1 *godartsassv1.Transpiler transpilerv1 *godartsassv1.Transpiler
err error err error
infol = rs.Logger.InfoCommand("Dart Sass")
warnl = rs.Logger.WarnCommand("Dart Sass")
) )
if hugo.IsDartSassV2() { if hugo.IsDartSassV2() {
@ -68,10 +70,10 @@ func New(fs *filesystems.SourceFilesystem, rs *resources.Spec) (*Client, error)
switch event.Type { switch event.Type {
case godartsass.LogEventTypeDebug: case godartsass.LogEventTypeDebug:
// Log as Info for now, we may adjust this if it gets too chatty. // 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: default:
// The rest are either deprecations or @warn statements. // 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 { switch event.Type {
case godartsassv1.LogEventTypeDebug: case godartsassv1.LogEventTypeDebug:
// Log as Info for now, we may adjust this if it gets too chatty. // 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: default:
// The rest are either deprecations or @warn statements. // The rest are either deprecations or @warn statements.
rs.Logger.Warnf("Dart Sass: %s", message) warnl.Logf(message)
} }
}, },
}) })

View file

@ -17,10 +17,10 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/bep/logg"
qt "github.com/frankban/quicktest" qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/hugolib" "github.com/gohugoio/hugo/hugolib"
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/dartsass" "github.com/gohugoio/hugo/resources/resource_transformers/tocss/dartsass"
jww "github.com/spf13/jwalterweatherman"
) )
func TestTransformIncludePaths(t *testing.T) { func TestTransformIncludePaths(t *testing.T) {
@ -288,11 +288,11 @@ T1: {{ $r.Content }}
T: t, T: t,
TxtarString: files, TxtarString: files,
NeedsOsFS: true, NeedsOsFS: true,
LogLevel: jww.LevelInfo, LogLevel: logg.LevelInfo,
}).Build() }).Build()
b.AssertLogMatches(`WARN.*Dart Sass: foo`) b.AssertLogMatches(`Dart Sass: foo`)
b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:1:0: bar`) b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:1:0: bar`)
} }
@ -513,20 +513,20 @@ T1: {{ $r.Content }}
T: t, T: t,
TxtarString: files, TxtarString: files,
NeedsOsFS: true, NeedsOsFS: true,
LogLevel: jww.LevelInfo, LogLevel: logg.LevelInfo,
}).Build() }).Build()
b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:3:0: color`) b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:3:0: color`)
b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:4:0: color`) b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:4:0: color`)
b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:5:0: color`) b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:5:0: color`)
b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:6:0: number`) b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:6:0: number`)
b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:7:0: number`) b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:7:0: number`)
b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:8:0: number`) b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:8:0: number`)
b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:9:0: string`) b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:9:0: string`)
b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:10:0: string`) b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:10:0: string`)
b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:11:0: string`) b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:11:0: string`)
b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:12:0: number`) b.AssertLogMatches(`Dart Sass: .*assets.*main.scss:12:0: number`)
b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:13:0: number`) b.AssertLogMatches(`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:14:0: number`)
} }

View file

@ -30,7 +30,6 @@ import (
"github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/langs" "github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/tpl/compare" "github.com/gohugoio/hugo/tpl/compare"
"github.com/spf13/cast" "github.com/spf13/cast"
@ -393,7 +392,7 @@ func (ns *Namespace) IsSet(c any, key any) (bool, error) {
return av.MapIndex(kv).IsValid(), nil return av.MapIndex(kv).IsValid(), nil
} }
default: default:
helpers.DistinctErrorLog.Printf("WARNING: calling IsSet with unsupported type %q (%T) will always return false.\n", av.Kind(), c) ns.deps.Log.Warnf("calling IsSet with unsupported type %q (%T) will always return false.\n", av.Kind(), c)
} }
return false, nil return false, nil

View file

@ -30,7 +30,6 @@ import (
"github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/common/constants" "github.com/gohugoio/hugo/common/constants"
"github.com/gohugoio/hugo/common/loggers"
"github.com/spf13/cast" "github.com/spf13/cast"
@ -92,7 +91,7 @@ func (ns *Namespace) GetCSV(sep string, args ...any) (d [][]string, err error) {
if security.IsAccessDenied(err) { if security.IsAccessDenied(err) {
return nil, 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 return nil, nil
} }
@ -128,7 +127,7 @@ func (ns *Namespace) GetJSON(args ...any) (any, error) {
if security.IsAccessDenied(err) { if security.IsAccessDenied(err) {
return nil, 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 return nil, nil
} }

View file

@ -22,6 +22,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/bep/logg"
"github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/maps"
qt "github.com/frankban/quicktest" qt "github.com/frankban/quicktest"
@ -108,13 +109,13 @@ func TestGetCSV(t *testing.T) {
got, err := ns.GetCSV(test.sep, test.url) got, err := ns.GetCSV(test.sep, test.url)
if _, ok := test.expect.(bool); ok { 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) c.Assert(got, qt.IsNil)
return return
} }
c.Assert(err, qt.IsNil, msg) 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.Not(qt.IsNil), msg)
c.Assert(got, qt.DeepEquals, test.expect, msg) c.Assert(got, qt.DeepEquals, test.expect, msg)
}) })
@ -200,11 +201,11 @@ func TestGetJSON(t *testing.T) {
got, _ := ns.GetJSON(test.url) got, _ := ns.GetJSON(test.url)
if _, ok := test.expect.(bool); ok { 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 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.Not(qt.IsNil), msg)
c.Assert(got, qt.DeepEquals, test.expect) 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) err := fn("http://example.org/api", "?foo", test.headers)
c.Assert(err, qt.IsNil) 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()) test.assert(c, headers.String())
} }

View file

@ -15,6 +15,9 @@ package data
import ( import (
"bytes" "bytes"
"github.com/gohugoio/hugo/common/loggers"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
@ -29,7 +32,6 @@ import (
qt "github.com/frankban/quicktest" qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/cache/filecache" "github.com/gohugoio/hugo/cache/filecache"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/hugofs"
@ -182,7 +184,7 @@ func TestScpGetRemoteParallel(t *testing.T) {
func newDeps(cfg config.Provider) *deps.Deps { func newDeps(cfg config.Provider) *deps.Deps {
conf := testconfig.GetTestConfig(nil, cfg) conf := testconfig.GetTestConfig(nil, cfg)
logger := loggers.NewIgnorableLogger(loggers.NewErrorLogger(), nil) logger := loggers.NewDefault()
fs := hugofs.NewFrom(afero.NewMemMapFs(), conf.BaseConfig()) fs := hugofs.NewFrom(afero.NewMemMapFs(), conf.BaseConfig())
d := &deps.Deps{ d := &deps.Deps{

View file

@ -16,27 +16,22 @@ package fmt
import ( import (
_fmt "fmt" _fmt "fmt"
"sort"
"github.com/bep/logg"
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/helpers" "github.com/spf13/cast"
) )
// New returns a new instance of the fmt-namespaced template functions. // New returns a new instance of the fmt-namespaced template functions.
func New(d *deps.Deps) *Namespace { 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{ ns := &Namespace{
distinctLogger: ignorableLogger.Apply(distinctLogger), logger: d.Log,
} }
d.BuildStartListeners.Add(func() { d.BuildStartListeners.Add(func() {
ns.distinctLogger.Reset() ns.logger.Reset()
}) })
return ns return ns
@ -44,7 +39,7 @@ func New(d *deps.Deps) *Namespace {
// Namespace provides template functions for the "fmt" namespace. // Namespace provides template functions for the "fmt" namespace.
type Namespace struct { type Namespace struct {
distinctLogger loggers.IgnorableLogger logger loggers.Logger
} }
// Print returns a string representation of args. // Print returns a string representation of args.
@ -65,7 +60,7 @@ func (ns *Namespace) Println(args ...any) string {
// Errorf formats args according to a format specifier and logs an ERROR. // Errorf formats args according to a format specifier and logs an ERROR.
// It returns an empty string. // It returns an empty string.
func (ns *Namespace) Errorf(format string, args ...any) string { func (ns *Namespace) Errorf(format string, args ...any) string {
ns.distinctLogger.Errorf(format, args...) ns.logger.Errorf(format, args...)
return "" return ""
} }
@ -73,13 +68,41 @@ func (ns *Namespace) Errorf(format string, args ...any) string {
// an information text that the error with the given id can be suppressed in config. // an information text that the error with the given id can be suppressed in config.
// It returns an empty string. // It returns an empty string.
func (ns *Namespace) Erroridf(id, format string, args ...any) string { func (ns *Namespace) Erroridf(id, format string, args ...any) string {
ns.distinctLogger.Errorsf(id, format, args...) ns.logger.Errorsf(id, format, args...)
return "" return ""
} }
// Warnf formats args according to a format specifier and logs a WARNING. // Warnf formats args according to a format specifier and logs a WARNING.
// It returns an empty string. // It returns an empty string.
func (ns *Namespace) Warnf(format string, args ...any) string { func (ns *Namespace) Warnf(format string, args ...any) string {
ns.distinctLogger.Warnf(format, args...) ns.logger.Warnf(format, args...)
return ""
}
// Warnmf is epxermimental and subject to change at any time.
func (ns *Namespace) Warnmf(m any, format string, args ...any) string {
return ns.logmf(ns.logger.Warn(), m, format, args...)
}
// Errormf is epxermimental and subject to change at any time.
func (ns *Namespace) Errormf(m any, format string, args ...any) string {
return ns.logmf(ns.logger.Error(), m, format, args...)
}
func (ns *Namespace) logmf(l logg.LevelLogger, m any, format string, args ...any) string {
mm := cast.ToStringMap(m)
fields := make(logg.Fields, len(mm))
i := 0
for k, v := range mm {
fields[i] = logg.Field{Name: k, Value: v}
i++
}
// Sort the fields to make the output deterministic.
sort.Slice(fields, func(i, j int) bool {
return fields[i].Name < fields[j].Name
})
l.WithFields(fields).Logf(format, args...)
return "" return ""
} }

View file

@ -20,7 +20,8 @@ import (
"net/url" "net/url"
"strings" "strings"
"github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/transform" "github.com/gohugoio/hugo/transform"
) )
@ -90,7 +91,7 @@ func New(baseURL url.URL) transform.Transformer {
c = append(c[:i], append(script, c[i:]...)...) c = append(c[:i], append(script, c[i:]...)...)
if _, err := ft.To().Write(c); err != nil { if _, err := ft.To().Write(c); err != nil {
helpers.DistinctWarnLog.Println("Failed to inject LiveReload script:", err) loggers.Log().Warnf("Failed to inject LiveReload script:", err)
} }
return nil return nil
} }

View file

@ -19,7 +19,7 @@ import (
"regexp" "regexp"
"github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/transform" "github.com/gohugoio/hugo/transform"
) )
@ -33,7 +33,7 @@ func HugoGenerator(ft transform.FromTo) error {
b := ft.From().Bytes() b := ft.From().Bytes()
if metaTagsCheck.Match(b) { if metaTagsCheck.Match(b) {
if _, err := ft.To().Write(b); err != nil { if _, err := ft.To().Write(b); err != nil {
helpers.DistinctWarnLog.Println("Failed to inject Hugo generator tag:", err) loggers.Log().Warnf("Failed to inject Hugo generator tag: %s", err)
} }
return nil return nil
} }
@ -49,7 +49,7 @@ func HugoGenerator(ft transform.FromTo) error {
} }
if _, err := ft.To().Write(newcontent); err != nil { if _, err := ft.To().Write(newcontent); err != nil {
helpers.DistinctWarnLog.Println("Failed to inject Hugo generator tag:", err) loggers.Log().Warnf("Failed to inject Hugo generator tag: %s", err)
} }
return nil return nil