internal/logs/logs.go (140 lines of code) (raw):

// Copyright 2020, Google Inc. // // 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 logs import ( "log" "os" "go.uber.org/zap" "go.uber.org/zap/zapcore" "go.uber.org/zap/zaptest/observer" ) const ( MessageZapKey string = "message" SeverityZapKey string = "severity" TimeZapKey string = "time" ) type StructuredLogger interface { // Infof, Warnf, Errorf use fmt.Sprintf to log a templated message. Infof(format string, v ...any) Warnf(format string, v ...any) Errorf(format string, v ...any) // Infow, Warnw, Errorw log a string message and optionally // can add key, value pairs of metadata to the log depending // on the underlying implementation of the interface. Infow(msg string, keysAndValues ...any) Warnw(msg string, keysAndValues ...any) Errorw(msg string, keysAndValues ...any) Println(v ...any) } type ZapStructuredLogger struct { logger *zap.SugaredLogger } func severityEncoder(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { var severity string switch level { case zapcore.ErrorLevel: severity = "ERROR" case zapcore.WarnLevel: severity = "WARNING" case zapcore.InfoLevel: severity = "INFO" case zapcore.DebugLevel: severity = "DEBUG" default: severity = "DEFAULT" } enc.AppendString(severity) } type stringMap map[string]string func (sm stringMap) MarshalLogObject(enc zapcore.ObjectEncoder) error { for k, v := range sm { enc.AddString(k, v) } return nil } func New(file string) *ZapStructuredLogger { cfg := zap.NewProductionConfig() cfg.DisableCaller = true cfg.DisableStacktrace = true cfg.EncoderConfig.MessageKey = MessageZapKey cfg.EncoderConfig.LevelKey = SeverityZapKey cfg.EncoderConfig.TimeKey = TimeZapKey cfg.EncoderConfig.EncodeTime = zapcore.RFC3339TimeEncoder cfg.EncoderConfig.EncodeLevel = severityEncoder cfg.OutputPaths = []string{ file, } logger, err := cfg.Build(zap.AddCallerSkip(1)) if err != nil { return Default() } return &ZapStructuredLogger{ logger: logger.Sugar(), } } func DiscardLogger() (*ZapStructuredLogger, *observer.ObservedLogs) { observedZapCore, observedLogs := observer.New(zap.InfoLevel) observedLogger := zap.New(observedZapCore) fileLogger := &ZapStructuredLogger{ logger: observedLogger.Sugar(), } return fileLogger, observedLogs } func Default() *ZapStructuredLogger { logger, err := zap.NewProduction() if err != nil { logger, _ := DiscardLogger() return logger } return &ZapStructuredLogger{ logger: logger.Sugar(), } } func (f ZapStructuredLogger) Infof(format string, v ...any) { f.logger.Infof(format, v...) } func (f ZapStructuredLogger) Warnf(format string, v ...any) { f.logger.Warnf(format, v...) } func (f ZapStructuredLogger) Errorf(format string, v ...any) { f.logger.Errorf(format, v...) } // A zap logger has an implementation of Infow, Warnw, Errorw that will // add a key, value pairs to the output json structured log. // Link: https://pkg.go.dev/go.uber.org/zap#SugaredLogger.Infow func (f ZapStructuredLogger) Infow(msg string, keysAndValues ...any) { f.logger.Infow(msg, keysAndValues...) } func (f ZapStructuredLogger) Warnw(msg string, keysAndValues ...any) { f.logger.Warnw(msg, keysAndValues...) } func (f ZapStructuredLogger) Errorw(msg string, keysAndValues ...any) { f.logger.Errorw(msg, keysAndValues...) } func (f ZapStructuredLogger) Println(v ...any) { f.logger.Infoln(v...) } type SimpleLogger struct { l *log.Logger } func (sl SimpleLogger) Fatalf(format string, v ...any) { sl.l.Fatalf(format, v...) } func (sl SimpleLogger) Printf(format string, v ...any) { sl.l.Printf(format, v...) } func (sl SimpleLogger) Infof(format string, v ...any) { sl.l.Printf(format, v...) } func (sl SimpleLogger) Warnf(format string, v ...any) { sl.l.Printf(format, v...) } func (sl SimpleLogger) Errorf(format string, v ...any) { sl.l.Printf(format, v...) } // A SimpleLogger doesn't have the capability of adding metadata // to the resulting log. func (sl SimpleLogger) Infow(msg string, keysAndValues ...any) { sl.l.Println(msg) } func (sl SimpleLogger) Warnw(msg string, keysAndValues ...any) { sl.l.Println(msg) } func (sl SimpleLogger) Errorw(msg string, keysAndValues ...any) { sl.l.Println(msg) } func (sl SimpleLogger) Println(v ...any) { sl.l.Println(v...) } func NewSimpleLogger() SimpleLogger { return SimpleLogger{log.New(os.Stdout, log.Prefix(), log.Flags())} }