Repository: gohugoio/hugo
Pull request #11659 (2023-11-01 14:36:44)

fix/deprecated 11645
- Avoid double printing INFO deprecation messages
- Add a field prefix to the deprecated log statements
- Remove some old and unused deprecation code



Patch:
diff --git a/commands/commandeer.go b/commands/commandeer.go
index c3e2019b5..5d414b04a 100644
--- a/commands/commandeer.go
+++ b/commands/commandeer.go
@@ -441,11 +441,11 @@ func (r *rootCommand) createLogger(running bool) (loggers.Logger, error) {
 	}
 
 	optsLogger := loggers.Options{
-		Distinct:    true,
-		Level:       level,
-		Stdout:      r.Out,
-		Stderr:      r.Out,
-		StoreErrors: running,
+		DistinctLevel: logg.LevelWarn,
+		Level:         level,
+		Stdout:        r.Out,
+		Stderr:        r.Out,
+		StoreErrors:   running,
 	}
 
 	return loggers.New(optsLogger), nil
diff --git a/common/hugo/hugo.go b/common/hugo/hugo.go
index f6eb72d8f..dedbc8c98 100644
--- a/common/hugo/hugo.go
+++ b/common/hugo/hugo.go
@@ -369,7 +369,7 @@ func DeprecateLevel(item, alternative, version string, level logg.Level) {
 		msg = fmt.Sprintf("%s was deprecated in Hugo %s and will be removed in a future release. %s", item, version, alternative)
 	}
 
-	loggers.Log().Logger().WithLevel(level).Logf(msg)
+	loggers.Log().Logger().WithLevel(level).WithField(loggers.FieldNameCmd, "deprecated").Logf(msg)
 }
 
 // We ususally do about one minor version a month.
diff --git a/common/loggers/logger.go b/common/loggers/logger.go
index a50502897..bc64ae0e5 100644
--- a/common/loggers/logger.go
+++ b/common/loggers/logger.go
@@ -40,7 +40,7 @@ type Options struct {
 	Level              logg.Level
 	Stdout             io.Writer
 	Stderr             io.Writer
-	Distinct           bool
+	DistinctLevel      logg.Level
 	StoreErrors        bool
 	HandlerPost        func(e *logg.Entry) error
 	SuppressStatements map[string]bool
@@ -92,8 +92,8 @@ func New(opts Options) Logger {
 	logHandler = multi.New(handlers...)
 
 	var logOnce *logOnceHandler
-	if opts.Distinct {
-		logOnce = newLogOnceHandler(logg.LevelWarn)
+	if opts.DistinctLevel != 0 {
+		logOnce = newLogOnceHandler(opts.DistinctLevel)
 		logHandler = newStopHandler(logOnce, logHandler)
 	}
 
@@ -137,10 +137,10 @@ func New(opts Options) Logger {
 // 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,
+		DistinctLevel: logg.LevelWarn,
+		Level:         logg.LevelWarn,
+		Stdout:        os.Stdout,
+		Stderr:        os.Stdout,
 	}
 	return New(opts)
 }
diff --git a/common/loggers/loggerglobal.go b/common/loggers/loggerglobal.go
index 8b8e8cfc2..6fd474a69 100644
--- a/common/loggers/loggerglobal.go
+++ b/common/loggers/loggerglobal.go
@@ -31,9 +31,9 @@ func InitGlobalLogger(level logg.Level, panicOnWarnings bool) {
 
 	log = New(
 		Options{
-			Level:       level,
-			Distinct:    true,
-			HandlerPost: logHookLast,
+			Level:         level,
+			DistinctLevel: logg.LevelInfo,
+			HandlerPost:   logHookLast,
 		},
 	)
 }
diff --git a/hugolib/site_new.go b/hugolib/site_new.go
index ac59e01c7..da9d19f21 100644
--- a/hugolib/site_new.go
+++ b/hugolib/site_new.go
@@ -117,7 +117,7 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
 
 		logOpts := loggers.Options{
 			Level:              cfg.LogLevel,
-			Distinct:           true, // This will drop duplicate log warning and errors.
+			DistinctLevel:      logg.LevelWarn, // This will drop duplicate log warning and errors.
 			HandlerPost:        logHookLast,
 			Stdout:             cfg.LogOut,
 			Stderr:             cfg.LogOut,
diff --git a/resources/page/page.go b/resources/page/page.go
index 54fc71f33..b5af489f1 100644
--- a/resources/page/page.go
+++ b/resources/page/page.go
@@ -339,8 +339,6 @@ type PageWithoutContent interface {
 	// This is currently only triggered with the Related content feature
 	// and the "fragments" type of index.
 	HeadingsFiltered(context.Context) tableofcontents.Headings
-
-	DeprecatedWarningPageMethods
 }
 
 // Positioner provides next/prev navigation.
@@ -417,7 +415,6 @@ type TableOfContentsProvider interface {
 
 // TranslationsProvider provides access to any translations.
 type TranslationsProvider interface {
-
 	// IsTranslated returns whether this content file is translated to
 	// other language(s).
 	IsTranslated() bool
@@ -431,7 +428,6 @@ type TranslationsProvider interface {
 
 // TreeProvider provides section tree navigation.
 type TreeProvider interface {
-
 	// IsAncestor returns whether the current page is an ancestor of other.
 	// Note that this method is not relevant for taxonomy lists and taxonomy terms pages.
 	IsAncestor(other any) (bool, error)
@@ -469,15 +465,6 @@ type TreeProvider interface {
 	Page() Page
 }
 
-// DeprecatedWarningPageMethods lists deprecated Page methods that will trigger
-// a WARNING if invoked.
-// This was added in Hugo 0.55.
-type DeprecatedWarningPageMethods any // This was emptied in Hugo 0.93.0.
-
-// Move here to trigger ERROR instead of WARNING.
-// TODO(bep) create wrappers and put into the Page once it has some methods.
-type DeprecatedErrorPageMethods any
-
 // PageWithContext is a Page with a context.Context.
 type PageWithContext struct {
 	Page
diff --git a/resources/page/page_generate/generate_page_wrappers.go b/resources/page/page_generate/generate_page_wrappers.go
index abbf02bf1..2449cf28d 100644
--- a/resources/page/page_generate/generate_page_wrappers.go
+++ b/resources/page/page_generate/generate_page_wrappers.go
@@ -15,13 +15,12 @@ package page_generate
 
 import (
 	"bytes"
+	"errors"
 	"fmt"
 	"os"
 	"path/filepath"
 	"reflect"
 
-	"errors"
-
 	"github.com/gohugoio/hugo/common/maps"
 
 	"github.com/gohugoio/hugo/codegen"
@@ -47,8 +46,7 @@ const header = `// Copyright 2019 The Hugo Authors. All rights reserved.
 `
 
 var (
-	pageInterfaceDeprecated = reflect.TypeOf((*page.DeprecatedWarningPageMethods)(nil)).Elem()
-	pageInterface           = reflect.TypeOf((*page.Page)(nil)).Elem()
+	pageInterface = reflect.TypeOf((*page.Page)(nil)).Elem()
 
 	packageDir = filepath.FromSlash("resources/page")
 )
@@ -58,10 +56,6 @@ func Generate(c *codegen.Inspector) error {
 		return fmt.Errorf("failed to generate JSON marshaler: %w", err)
 	}
 
-	if err := generateDeprecatedWrappers(c); err != nil {
-		return fmt.Errorf("failed to generate deprecate wrappers: %w", err)
-	}
-
 	if err := generateFileIsZeroWrappers(c); err != nil {
 		return fmt.Errorf("failed to generate file wrappers: %w", err)
 	}
@@ -81,10 +75,6 @@ func generateMarshalJSON(c *codegen.Inspector) error {
 
 	// Exclude these methods
 	excludes := []reflect.Type{
-		// We need to evaluate the deprecated vs JSON in the future,
-		// but leave them out for now.
-		pageInterfaceDeprecated,
-
 		// Leave this out for now. We need to revisit the author issue.
 		reflect.TypeOf((*page.AuthorProvider)(nil)).Elem(),
 
@@ -133,71 +123,6 @@ package page
 	return nil
 }
 
-func generateDeprecatedWrappers(c *codegen.Inspector) error {
-	filename := filepath.Join(c.ProjectRootDir, packageDir, "page_wrappers.autogen.go")
-	f, err := os.Create(filename)
-	if err != nil {
-		return err
-	}
-	defer f.Close()
-
-	// Generate a wrapper for deprecated page methods
-
-	reasons := map[string]string{
-		"IsDraft":        "Use .Draft.",
-		"Hugo":           "Use the global hugo function.",
-		"LanguagePrefix": "Use .Site.LanguagePrefix.",
-		"GetParam":       "Use .Param or .Params.myParam.",
-		"RSSLink": `Use the Output Format's link, e.g. something like:
-    {{ with .OutputFormats.Get "RSS" }}{{ .RelPermalink }}{{ end }}`,
-		"URL": "Use .Permalink or .RelPermalink. If what you want is the front matter URL value, use .Params.url",
-	}
-
-	deprecated := func(name string, tp reflect.Type) string {
-		alternative, found := reasons[name]
-		if !found {
-			panic(fmt.Sprintf("no deprecated reason found for %q", name))
-		}
-
-		return fmt.Sprintf("helpers.Deprecated(%q, %q, true)", "Page."+name, alternative)
-	}
-
-	var buff bytes.Buffer
-
-	methods := c.MethodsFromTypes([]reflect.Type{pageInterfaceDeprecated}, nil)
-
-	for _, m := range methods {
-		fmt.Fprint(&buff, m.Declaration("*pageDeprecated"))
-		fmt.Fprintln(&buff, " {")
-		fmt.Fprintf(&buff, "\t%s\n", deprecated(m.Name, m.Owner))
-		fmt.Fprintf(&buff, "\t%s\n}\n", m.Delegate("p", "p"))
-
-	}
-
-	pkgImports := methods.Imports()
-	// pkgImports := append(methods.Imports(), "github.com/gohugoio/hugo/helpers")
-
-	fmt.Fprintf(f, `%s
-
-package page
-
-%s
-// NewDeprecatedWarningPage adds deprecation warnings to the given implementation.
-func NewDeprecatedWarningPage(p DeprecatedWarningPageMethods) DeprecatedWarningPageMethods {
-	return &pageDeprecated{p: p}
-}
-
-type pageDeprecated struct {
-	p DeprecatedWarningPageMethods
-}
-
-%s
-
-`, header, importsString(pkgImports), buff.String())
-
-	return nil
-}
-
 func generateFileIsZeroWrappers(c *codegen.Inspector) error {
 	filename := filepath.Join(c.ProjectRootDir, packageDir, "zero_file.autogen.go")
 	f, err := os.Create(filename)
diff --git a/resources/page/page_wrappers.autogen.go b/resources/page/page_wrappers.autogen.go
index 55dff47d5..4b7c034a1 100644
--- a/resources/page/page_wrappers.autogen.go
+++ b/resources/page/page_wrappers.autogen.go
@@ -14,12 +14,3 @@
 // This file is autogenerated.
 
 package page
-
-// NewDeprecatedWarningPage adds deprecation warnings to the given implementation.
-func NewDeprecatedWarningPage(p DeprecatedWarningPageMethods) DeprecatedWarningPageMethods {
-	return &pageDeprecated{p: p}
-}
-
-type pageDeprecated struct {
-	p DeprecatedWarningPageMethods
-}


Test patch:
diff --git a/common/loggers/logger_test.go b/common/loggers/logger_test.go
index 1b42eff53..6f589aafe 100644
--- a/common/loggers/logger_test.go
+++ b/common/loggers/logger_test.go
@@ -29,10 +29,10 @@ func TestLogDistinct(t *testing.T) {
 	c := qt.New(t)
 
 	opts := loggers.Options{
-		Distinct:    true,
-		StoreErrors: true,
-		Stdout:      io.Discard,
-		Stderr:      io.Discard,
+		DistinctLevel: logg.LevelWarn,
+		StoreErrors:   true,
+		Stdout:        io.Discard,
+		Stderr:        io.Discard,
 	}
 
 	l := loggers.New(opts)
@@ -85,7 +85,6 @@ func TestOptionStoreErrors(t *testing.T) {
 
 	c.Assert(sb.String(), qt.Contains, "error 1")
 	c.Assert(sb.String(), qt.Contains, "ERROR")
-
 }
 
 func TestLogCount(t *testing.T) {
@@ -124,17 +123,16 @@ func TestSuppressStatements(t *testing.T) {
 	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,
+		StoreErrors:   true,
+		DistinctLevel: logg.LevelWarn,
+		Stdout:        io.Discard,
+		Stderr:        io.Discard,
 	}
 
 	l := loggers.New(opts)
diff --git a/hugolib/integrationtest_builder.go b/hugolib/integrationtest_builder.go
index 54085070c..14509657a 100644
--- a/hugolib/integrationtest_builder.go
+++ b/hugolib/integrationtest_builder.go
@@ -394,10 +394,10 @@ func (s *IntegrationTestBuilder) initBuilder() error {
 
 		logger := loggers.New(
 			loggers.Options{
-				Stdout:   w,
-				Stderr:   w,
-				Level:    s.Cfg.LogLevel,
-				Distinct: true,
+				Stdout:        w,
+				Stderr:        w,
+				Level:         s.Cfg.LogLevel,
+				DistinctLevel: logg.LevelWarn,
 			},
 		)
 
diff --git a/testscripts/commands/deprecate.txt b/testscripts/commands/deprecate.txt
index b15d7ec84..3be4976d5 100644
--- a/testscripts/commands/deprecate.txt
+++ b/testscripts/commands/deprecate.txt
@@ -1,13 +1,13 @@
 
+# Test deprecation logging.
+hugo -e info --logLevel info
+stdout 'INFO  deprecated: item was deprecated in Hugo'
 
-hugo -e info  --logLevel info
-stdout 'INFO  item was deprecated in Hugo'
+hugo -e warn --logLevel warn
+stdout 'WARN  deprecated: item was deprecated in Hugo'
 
-hugo -e warn  --logLevel warn
-stdout 'WARN  item was deprecated in Hugo'
-
-! hugo -e error  --logLevel warn
-stdout 'ERROR item was deprecated in Hugo'
+! hugo -e error --logLevel warn
+stdout 'ERROR deprecated: item was deprecated in Hugo'
 
 -- hugo.toml --
 baseURL = "https://example.com/"