mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-20 21:31:32 +02:00
303
common/loggers/logger.go
Normal file
303
common/loggers/logger.go
Normal 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
|
||||
}
|
Reference in New Issue
Block a user