// MIT License // // Copyright (c) 2024 vinchent // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package log import ( "os" "sync" "git.vinchent.xyz/vinchent/howmuch/internal/pkg/core" "git.vinchent.xyz/vinchent/howmuch/internal/pkg/middleware" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) type Logger interface { FatalLog(msg string, keyValues ...interface{}) PanicLog(msg string, keyValues ...interface{}) ErrorLog(msg string, keyValues ...interface{}) WarnLog(msg string, keyValues ...interface{}) InfoLog(msg string, keyValues ...interface{}) DebugLog(msg string, keyValues ...interface{}) Sync() } // zapLogger is an implementation of Logger interface type zapLogger struct { z *zap.Logger } var ( mu sync.Mutex // default global logger std = NewLogger(NewOptions()) ) // Init initializes global logger with options func Init(opts *Options) { mu.Lock() defer mu.Unlock() std = NewLogger(opts) } func NewLogger(opts *Options) *zapLogger { if opts == nil { opts = NewOptions() } var zapLevel zapcore.Level // If unknown level, use info if err := zapLevel.UnmarshalText([]byte(opts.Level)); err != nil { zapLevel = zap.InfoLevel } encoderCfg := zap.NewProductionEncoderConfig() encoderCfg.TimeKey = "timestamp" encoderCfg.MessageKey = "message" encoderCfg.EncodeTime = zapcore.ISO8601TimeEncoder config := zap.Config{ Level: zap.NewAtomicLevelAt(zapLevel), Development: opts.Development, DisableCaller: opts.DisableCaller, DisableStacktrace: opts.DisableStacktrace, Sampling: nil, Encoding: opts.Format, EncoderConfig: encoderCfg, OutputPaths: opts.OutputPaths, ErrorOutputPaths: []string{ "stderr", }, InitialFields: map[string]interface{}{ "pid": os.Getpid(), }, } z := zap.Must(config.Build(zap.AddStacktrace(zapcore.PanicLevel), zap.AddCallerSkip(1))) zap.RedirectStdLog(z) return &zapLogger{z: z} } // CtxLog writes context's information into the log func CtxLog(ctx core.Context) *zapLogger { return std.CtxLog(ctx) } func (z *zapLogger) CtxLog(ctx core.Context) *zapLogger { zz := z.clone() if rid := ctx.GetHeader(middleware.XRequestID); rid != "" { zz.z = zz.z.With(zap.Any(middleware.XRequestID, rid)) } return zz } func (z *zapLogger) clone() *zapLogger { zz := *z return &zz } func (z *zapLogger) FatalLog(msg string, keyValues ...interface{}) { z.z.Sugar().Fatalw(msg, keyValues...) } func (z *zapLogger) PanicLog(msg string, keyValues ...interface{}) { z.z.Sugar().Panicw(msg, keyValues...) } func (z *zapLogger) ErrorLog(msg string, keyValues ...interface{}) { z.z.Sugar().Errorw(msg, keyValues...) } func (z *zapLogger) WarnLog(msg string, keyValues ...interface{}) { z.z.Sugar().Warnw(msg, keyValues...) } func (z *zapLogger) InfoLog(msg string, keyValues ...interface{}) { z.z.Sugar().Infow(msg, keyValues...) } func (z *zapLogger) DebugLog(msg string, keyValues ...interface{}) { z.z.Sugar().Debugw(msg, keyValues...) } func (z *zapLogger) Sync() { _ = z.z.Sync() } func FatalLog(msg string, keyValues ...interface{}) { std.z.Sugar().Fatalw(msg, keyValues...) } func PanicLog(msg string, keyValues ...interface{}) { std.z.Sugar().Panicw(msg, keyValues...) } func ErrorLog(msg string, keyValues ...interface{}) { std.z.Sugar().Errorw(msg, keyValues...) } func WarnLog(msg string, keyValues ...interface{}) { std.z.Sugar().Warnw(msg, keyValues...) } func InfoLog(msg string, keyValues ...interface{}) { std.z.Sugar().Infow(msg, keyValues...) } func DebugLog(msg string, keyValues ...interface{}) { std.z.Sugar().Debugw(msg, keyValues...) } func Sync() { _ = std.z.Sync() }