github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/common/loggers/logger.go (about)

     1  // Copyright 2023 The Hugo Authors. All rights reserved.
     2  // Some functions in this file (see comments) is based on the Go source code,
     3  // copyright The Go Authors and  governed by a BSD-style license.
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  // http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package loggers
    17  
    18  import (
    19  	"fmt"
    20  	"io"
    21  	"os"
    22  	"strings"
    23  	"time"
    24  
    25  	"github.com/bep/logg"
    26  	"github.com/bep/logg/handlers/multi"
    27  	"github.com/gohugoio/hugo/common/terminal"
    28  )
    29  
    30  var (
    31  	reservedFieldNamePrefix = "__h_field_"
    32  	// FieldNameCmd is the name of the field that holds the command name.
    33  	FieldNameCmd = reservedFieldNamePrefix + "_cmd"
    34  	// Used to suppress statements.
    35  	FieldNameStatementID = reservedFieldNamePrefix + "__h_field_statement_id"
    36  )
    37  
    38  // Options defines options for the logger.
    39  type Options struct {
    40  	Level              logg.Level
    41  	Stdout             io.Writer
    42  	Stderr             io.Writer
    43  	Distinct           bool
    44  	StoreErrors        bool
    45  	HandlerPost        func(e *logg.Entry) error
    46  	SuppressStatements map[string]bool
    47  }
    48  
    49  // New creates a new logger with the given options.
    50  func New(opts Options) Logger {
    51  	if opts.Stdout == nil {
    52  		opts.Stdout = os.Stdout
    53  	}
    54  	if opts.Stderr == nil {
    55  		opts.Stderr = os.Stdout
    56  	}
    57  	if opts.Level == 0 {
    58  		opts.Level = logg.LevelWarn
    59  	}
    60  
    61  	var logHandler logg.Handler
    62  	if terminal.PrintANSIColors(os.Stdout) {
    63  		logHandler = newDefaultHandler(opts.Stdout, opts.Stderr)
    64  	} else {
    65  		logHandler = newNoColoursHandler(opts.Stdout, opts.Stderr, false, nil)
    66  	}
    67  
    68  	errorsw := &strings.Builder{}
    69  	logCounters := newLogLevelCounter()
    70  	handlers := []logg.Handler{
    71  		whiteSpaceTrimmer(),
    72  		logHandler,
    73  		logCounters,
    74  	}
    75  
    76  	if opts.HandlerPost != nil {
    77  		var hookHandler logg.HandlerFunc = func(e *logg.Entry) error {
    78  			opts.HandlerPost(e)
    79  			return nil
    80  		}
    81  		handlers = append(handlers, hookHandler)
    82  	}
    83  
    84  	if opts.StoreErrors {
    85  		h := newNoColoursHandler(io.Discard, errorsw, true, func(e *logg.Entry) bool {
    86  			return e.Level >= logg.LevelError
    87  		})
    88  
    89  		handlers = append(handlers, h)
    90  	}
    91  
    92  	logHandler = multi.New(handlers...)
    93  
    94  	var logOnce *logOnceHandler
    95  	if opts.Distinct {
    96  		logOnce = newLogOnceHandler(logg.LevelWarn)
    97  		logHandler = newStopHandler(logOnce, logHandler)
    98  	}
    99  
   100  	if opts.SuppressStatements != nil && len(opts.SuppressStatements) > 0 {
   101  		logHandler = newStopHandler(newSuppressStatementsHandler(opts.SuppressStatements), logHandler)
   102  	}
   103  
   104  	logger := logg.New(
   105  		logg.Options{
   106  			Level:   opts.Level,
   107  			Handler: logHandler,
   108  		},
   109  	)
   110  
   111  	l := logger.WithLevel(opts.Level)
   112  
   113  	reset := func() {
   114  		logCounters.mu.Lock()
   115  		defer logCounters.mu.Unlock()
   116  		logCounters.counters = make(map[logg.Level]int)
   117  		errorsw.Reset()
   118  		if logOnce != nil {
   119  			logOnce.reset()
   120  		}
   121  	}
   122  
   123  	return &logAdapter{
   124  		logCounters: logCounters,
   125  		errors:      errorsw,
   126  		reset:       reset,
   127  		out:         opts.Stdout,
   128  		level:       opts.Level,
   129  		logger:      logger,
   130  		debugl:      l.WithLevel(logg.LevelDebug),
   131  		infol:       l.WithLevel(logg.LevelInfo),
   132  		warnl:       l.WithLevel(logg.LevelWarn),
   133  		errorl:      l.WithLevel(logg.LevelError),
   134  	}
   135  }
   136  
   137  // NewDefault creates a new logger with the default options.
   138  func NewDefault() Logger {
   139  	opts := Options{
   140  		Distinct: true,
   141  		Level:    logg.LevelWarn,
   142  		Stdout:   os.Stdout,
   143  		Stderr:   os.Stdout,
   144  	}
   145  	return New(opts)
   146  }
   147  
   148  func LevelLoggerToWriter(l logg.LevelLogger) io.Writer {
   149  	return logWriter{l: l}
   150  }
   151  
   152  type Logger interface {
   153  	Debugf(format string, v ...any)
   154  	Debugln(v ...any)
   155  	Error() logg.LevelLogger
   156  	Errorf(format string, v ...any)
   157  	Errorln(v ...any)
   158  	Errors() string
   159  	Errorsf(id, format string, v ...any)
   160  	Info() logg.LevelLogger
   161  	InfoCommand(command string) logg.LevelLogger
   162  	Infof(format string, v ...any)
   163  	Infoln(v ...any)
   164  	Level() logg.Level
   165  	LoggCount(logg.Level) int
   166  	Logger() logg.Logger
   167  	Out() io.Writer
   168  	Printf(format string, v ...any)
   169  	Println(v ...any)
   170  	PrintTimerIfDelayed(start time.Time, name string)
   171  	Reset()
   172  	Warn() logg.LevelLogger
   173  	WarnCommand(command string) logg.LevelLogger
   174  	Warnf(format string, v ...any)
   175  	Warnln(v ...any)
   176  	Deprecatef(fail bool, format string, v ...any)
   177  }
   178  
   179  type logAdapter struct {
   180  	logCounters *logLevelCounter
   181  	errors      *strings.Builder
   182  	reset       func()
   183  	out         io.Writer
   184  	level       logg.Level
   185  	logger      logg.Logger
   186  	debugl      logg.LevelLogger
   187  	infol       logg.LevelLogger
   188  	warnl       logg.LevelLogger
   189  	errorl      logg.LevelLogger
   190  }
   191  
   192  func (l *logAdapter) Debugf(format string, v ...any) {
   193  	l.debugl.Logf(format, v...)
   194  }
   195  
   196  func (l *logAdapter) Debugln(v ...any) {
   197  	l.debugl.Logf(l.sprint(v...))
   198  }
   199  
   200  func (l *logAdapter) Info() logg.LevelLogger {
   201  	return l.infol
   202  }
   203  
   204  func (l *logAdapter) InfoCommand(command string) logg.LevelLogger {
   205  	return l.infol.WithField(FieldNameCmd, command)
   206  }
   207  
   208  func (l *logAdapter) Infof(format string, v ...any) {
   209  	l.infol.Logf(format, v...)
   210  }
   211  
   212  func (l *logAdapter) Infoln(v ...any) {
   213  	l.infol.Logf(l.sprint(v...))
   214  }
   215  
   216  func (l *logAdapter) Level() logg.Level {
   217  	return l.level
   218  }
   219  
   220  func (l *logAdapter) LoggCount(level logg.Level) int {
   221  	l.logCounters.mu.RLock()
   222  	defer l.logCounters.mu.RUnlock()
   223  	return l.logCounters.counters[level]
   224  }
   225  
   226  func (l *logAdapter) Logger() logg.Logger {
   227  	return l.logger
   228  }
   229  
   230  func (l *logAdapter) Out() io.Writer {
   231  	return l.out
   232  }
   233  
   234  // PrintTimerIfDelayed prints a time statement to the FEEDBACK logger
   235  // if considerable time is spent.
   236  func (l *logAdapter) PrintTimerIfDelayed(start time.Time, name string) {
   237  	elapsed := time.Since(start)
   238  	milli := int(1000 * elapsed.Seconds())
   239  	if milli < 500 {
   240  		return
   241  	}
   242  	l.Printf("%s in %v ms", name, milli)
   243  }
   244  
   245  func (l *logAdapter) Printf(format string, v ...any) {
   246  	// Add trailing newline if not present.
   247  	if !strings.HasSuffix(format, "\n") {
   248  		format += "\n"
   249  	}
   250  	fmt.Fprintf(l.out, format, v...)
   251  }
   252  
   253  func (l *logAdapter) Println(v ...any) {
   254  	fmt.Fprintln(l.out, v...)
   255  }
   256  
   257  func (l *logAdapter) Reset() {
   258  	l.reset()
   259  }
   260  
   261  func (l *logAdapter) Warn() logg.LevelLogger {
   262  	return l.warnl
   263  }
   264  
   265  func (l *logAdapter) Warnf(format string, v ...any) {
   266  	l.warnl.Logf(format, v...)
   267  }
   268  
   269  func (l *logAdapter) WarnCommand(command string) logg.LevelLogger {
   270  	return l.warnl.WithField(FieldNameCmd, command)
   271  }
   272  
   273  func (l *logAdapter) Warnln(v ...any) {
   274  	l.warnl.Logf(l.sprint(v...))
   275  }
   276  
   277  func (l *logAdapter) Error() logg.LevelLogger {
   278  	return l.errorl
   279  }
   280  
   281  func (l *logAdapter) Errorf(format string, v ...any) {
   282  	l.errorl.Logf(format, v...)
   283  }
   284  
   285  func (l *logAdapter) Errorln(v ...any) {
   286  	l.errorl.Logf(l.sprint(v...))
   287  }
   288  
   289  func (l *logAdapter) Errors() string {
   290  	return l.errors.String()
   291  }
   292  
   293  func (l *logAdapter) Errorsf(id, format string, v ...any) {
   294  	l.errorl.WithField(FieldNameStatementID, id).Logf(format, v...)
   295  }
   296  
   297  func (l *logAdapter) sprint(v ...any) string {
   298  	return strings.TrimRight(fmt.Sprintln(v...), "\n")
   299  }
   300  
   301  func (l *logAdapter) Deprecatef(fail bool, format string, v ...any) {
   302  	format = "DEPRECATED: " + format
   303  	if fail {
   304  		l.errorl.Logf(format, v...)
   305  	} else {
   306  		l.warnl.Logf(format, v...)
   307  	}
   308  }
   309  
   310  type logWriter struct {
   311  	l logg.LevelLogger
   312  }
   313  
   314  func (w logWriter) Write(p []byte) (n int, err error) {
   315  	w.l.Log(logg.String(string(p)))
   316  	return len(p), nil
   317  }