aboutsummaryrefslogtreecommitdiffhomepage
path: root/common/loggers/handlersmisc.go
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <[email protected]>2023-06-16 08:17:42 +0200
committerBjørn Erik Pedersen <[email protected]>2023-06-18 13:03:04 +0200
commit7c9fada778e91976d4ba1cbe942235a9bbeaf5cb (patch)
treea717f6e0a5915777ae6859564acd13385213bbab /common/loggers/handlersmisc.go
parent0e7944658660b5658b7640dce3cb346d7198d8c9 (diff)
downloadhugo-7c9fada778e91976d4ba1cbe942235a9bbeaf5cb.tar.gz
hugo-7c9fada778e91976d4ba1cbe942235a9bbeaf5cb.zip
Replace the old log setup, with structured logging etc.
Fixes #11124
Diffstat (limited to 'common/loggers/handlersmisc.go')
-rw-r--r--common/loggers/handlersmisc.go158
1 files changed, 158 insertions, 0 deletions
diff --git a/common/loggers/handlersmisc.go b/common/loggers/handlersmisc.go
new file mode 100644
index 000000000..5c9d6c091
--- /dev/null
+++ b/common/loggers/handlersmisc.go
@@ -0,0 +1,158 @@
+// Copyright 2023 The Hugo Authors. All rights reserved.
+// Some functions in this file (see comments) is based on the Go source code,
+// copyright The Go Authors and governed by a BSD-style license.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package loggers
+
+import (
+ "fmt"
+ "strings"
+ "sync"
+
+ "github.com/bep/logg"
+ "github.com/gohugoio/hugo/identity"
+)
+
+// PanicOnWarningHook panics on warnings.
+var PanicOnWarningHook = func(e *logg.Entry) error {
+ if e.Level != logg.LevelWarn {
+ return nil
+ }
+ panic(e.Message)
+}
+
+func newLogLevelCounter() *logLevelCounter {
+ return &logLevelCounter{
+ counters: make(map[logg.Level]int),
+ }
+}
+
+func newLogOnceHandler(threshold logg.Level) *logOnceHandler {
+ return &logOnceHandler{
+ threshold: threshold,
+ seen: make(map[uint64]bool),
+ }
+}
+
+func newStopHandler(h ...logg.Handler) *stopHandler {
+ return &stopHandler{
+ handlers: h,
+ }
+}
+
+func newSuppressStatementsHandler(statements map[string]bool) *suppressStatementsHandler {
+ return &suppressStatementsHandler{
+ statements: statements,
+ }
+}
+
+type logLevelCounter struct {
+ mu sync.RWMutex
+ counters map[logg.Level]int
+}
+
+func (h *logLevelCounter) HandleLog(e *logg.Entry) error {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ h.counters[e.Level]++
+ return nil
+}
+
+var stopError = fmt.Errorf("stop")
+
+type logOnceHandler struct {
+ threshold logg.Level
+ mu sync.Mutex
+ seen map[uint64]bool
+}
+
+func (h *logOnceHandler) HandleLog(e *logg.Entry) error {
+ if e.Level < h.threshold {
+ // We typically only want to enable this for warnings and above.
+ // The common use case is that many go routines may log the same error.
+ return nil
+ }
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ hash := identity.HashUint64(e.Level, e.Message, e.Fields)
+ if h.seen[hash] {
+ return stopError
+ }
+ h.seen[hash] = true
+ return nil
+}
+
+func (h *logOnceHandler) reset() {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ h.seen = make(map[uint64]bool)
+}
+
+type stopHandler struct {
+ handlers []logg.Handler
+}
+
+// HandleLog implements logg.Handler.
+func (h *stopHandler) HandleLog(e *logg.Entry) error {
+ for _, handler := range h.handlers {
+ if err := handler.HandleLog(e); err != nil {
+ if err == stopError {
+ return nil
+ }
+ return err
+ }
+ }
+ return nil
+}
+
+type suppressStatementsHandler struct {
+ statements map[string]bool
+}
+
+func (h *suppressStatementsHandler) HandleLog(e *logg.Entry) error {
+ for _, field := range e.Fields {
+ if field.Name == FieldNameStatementID {
+ if h.statements[field.Value.(string)] {
+ return stopError
+ }
+ }
+ }
+ return nil
+}
+
+// replacer creates a new log handler that does string replacement in log messages.
+func replacer(repl *strings.Replacer) logg.Handler {
+ return logg.HandlerFunc(func(e *logg.Entry) error {
+ e.Message = repl.Replace(e.Message)
+ for i, field := range e.Fields {
+ if s, ok := field.Value.(string); ok {
+ e.Fields[i].Value = repl.Replace(s)
+ }
+ }
+ return nil
+ })
+}
+
+// whiteSpaceTrimmer creates a new log handler that trims whitespace from log messages and string fields.
+func whiteSpaceTrimmer() logg.Handler {
+ return logg.HandlerFunc(func(e *logg.Entry) error {
+ e.Message = strings.TrimSpace(e.Message)
+ for i, field := range e.Fields {
+ if s, ok := field.Value.(string); ok {
+ e.Fields[i].Value = strings.TrimSpace(s)
+ }
+ }
+ return nil
+ })
+}