2022-10-11 17:36:09 +08:00
|
|
|
package log
|
|
|
|
|
|
|
|
import (
|
|
|
|
stdlog "log"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"go.uber.org/zap/zapcore"
|
|
|
|
"gopkg.in/natefinch/lumberjack.v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
timeFormat = "2006-01-02 15:04:05.000"
|
|
|
|
)
|
|
|
|
|
|
|
|
// zapLogger is the logger implementation in go.uber.org/zap
|
|
|
|
type zapLogger struct {
|
|
|
|
level zapcore.Level
|
|
|
|
debug bool
|
|
|
|
encoding string
|
|
|
|
opts []zap.Option
|
|
|
|
logger *zap.Logger
|
|
|
|
instance *zap.SugaredLogger
|
|
|
|
output string
|
|
|
|
errorOutput string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Default the default logger instance
|
|
|
|
func Default(debug ...bool) Logger {
|
|
|
|
z := New()
|
|
|
|
z.SetLevel(logLevel())
|
|
|
|
if isJSONFormat() {
|
|
|
|
z.SetEncoding("json")
|
|
|
|
}
|
|
|
|
// env debug
|
|
|
|
if isEnableDebug() {
|
|
|
|
z.SetLevel(DebugLevel)
|
|
|
|
}
|
|
|
|
if len(debug) > 0 {
|
|
|
|
if debug[0] {
|
|
|
|
z.SetLevel(DebugLevel)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
z.Output(output())
|
|
|
|
z.ErrorOutput(errorOutput())
|
|
|
|
|
|
|
|
return z
|
|
|
|
}
|
|
|
|
|
|
|
|
// New create new logger instance
|
|
|
|
func New(opts ...zap.Option) Logger {
|
|
|
|
// std logger
|
|
|
|
stdlog.Default().SetFlags(0)
|
|
|
|
stdlog.Default().SetOutput(new(logWriter))
|
|
|
|
|
|
|
|
z := zapLogger{
|
|
|
|
level: zap.ErrorLevel,
|
|
|
|
debug: false,
|
|
|
|
encoding: "console",
|
|
|
|
opts: opts,
|
|
|
|
}
|
|
|
|
|
|
|
|
return &z
|
|
|
|
}
|
|
|
|
|
|
|
|
func openSinks(cfg zap.Config) (zapcore.WriteSyncer, zapcore.WriteSyncer, error) {
|
|
|
|
sink, closeOut, err := zap.Open(cfg.OutputPaths...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
errSink, _, err := zap.Open(cfg.ErrorOutputPaths...)
|
|
|
|
if err != nil {
|
|
|
|
closeOut()
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
return sink, errSink, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetEncoding set logger message coding
|
|
|
|
func (z *zapLogger) SetEncoding(enc string) {
|
|
|
|
z.encoding = enc
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetLevel set logger level
|
|
|
|
func (z *zapLogger) SetLevel(lvl Level) {
|
|
|
|
isDebug := lvl == DebugLevel
|
|
|
|
level := zap.ErrorLevel
|
|
|
|
switch lvl {
|
|
|
|
case DebugLevel:
|
|
|
|
level = zap.DebugLevel
|
|
|
|
case InfoLevel:
|
|
|
|
level = zap.InfoLevel
|
|
|
|
case WarnLevel:
|
|
|
|
level = zap.WarnLevel
|
|
|
|
case ErrorLevel:
|
|
|
|
level = zap.ErrorLevel
|
|
|
|
}
|
|
|
|
z.level = level
|
|
|
|
z.debug = isDebug
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output file path to write log message
|
|
|
|
func (z *zapLogger) Output(file string) {
|
|
|
|
if file != "" {
|
|
|
|
z.output = file
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ErrorOutput file path to write log message
|
|
|
|
func (z *zapLogger) ErrorOutput(file string) {
|
|
|
|
if file != "" {
|
|
|
|
z.errorOutput = file
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Printf logs a message wihout level
|
|
|
|
func (z *zapLogger) Printf(format string, v ...interface{}) {
|
|
|
|
stdlog.Printf(format, v...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Debugf logs a message at DebugLevel
|
|
|
|
func (z *zapLogger) Debugf(template string, args ...interface{}) {
|
|
|
|
z.Instance().Debugf(template, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Infof logs a message at InfoLevel
|
|
|
|
func (z *zapLogger) Infof(template string, args ...interface{}) {
|
|
|
|
z.Instance().Infof(template, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Warnf logs a message at WarnLevel
|
2023-03-10 23:49:52 +08:00
|
|
|
func (z *zapLogger) Warnf(template string, args ...interface{}) {
|
2022-10-11 17:36:09 +08:00
|
|
|
z.Instance().Warnf(template, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Errorf logs a message at ErrorLevel
|
2023-03-10 23:49:52 +08:00
|
|
|
func (z *zapLogger) Errorf(template string, args ...interface{}) {
|
2022-10-11 17:36:09 +08:00
|
|
|
z.Instance().Errorf(template, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (z *zapLogger) Instance() *zap.SugaredLogger {
|
|
|
|
if z.instance == nil {
|
|
|
|
// zap
|
|
|
|
encoderConfig := zapcore.EncoderConfig{
|
|
|
|
TimeKey: "ts",
|
|
|
|
LevelKey: "level",
|
|
|
|
NameKey: "logger",
|
|
|
|
CallerKey: "caller",
|
|
|
|
FunctionKey: zapcore.OmitKey,
|
|
|
|
MessageKey: "msg",
|
|
|
|
StacktraceKey: "stacktrace",
|
|
|
|
LineEnding: zapcore.DefaultLineEnding,
|
|
|
|
EncodeLevel: zapcore.CapitalColorLevelEncoder,
|
|
|
|
EncodeTime: timeEncoder,
|
|
|
|
EncodeDuration: zapcore.SecondsDurationEncoder,
|
|
|
|
EncodeCaller: zapcore.ShortCallerEncoder,
|
|
|
|
}
|
|
|
|
cfg := zap.Config{
|
|
|
|
Level: zap.NewAtomicLevelAt(zap.ErrorLevel),
|
|
|
|
Development: z.debug,
|
|
|
|
DisableCaller: true,
|
|
|
|
DisableStacktrace: true,
|
|
|
|
Encoding: z.encoding,
|
|
|
|
EncoderConfig: encoderConfig,
|
|
|
|
OutputPaths: []string{"stderr"},
|
|
|
|
ErrorOutputPaths: []string{"stderr"},
|
|
|
|
}
|
|
|
|
cfg.Level.SetLevel(z.level)
|
|
|
|
if z.debug {
|
|
|
|
// set the minimal level to debug
|
|
|
|
cfg.Level.SetLevel(zap.DebugLevel)
|
|
|
|
}
|
|
|
|
// output
|
|
|
|
if z.output != "" {
|
|
|
|
cfg.OutputPaths = append(cfg.OutputPaths, z.output)
|
|
|
|
}
|
|
|
|
encoder := zapcore.NewConsoleEncoder(encoderConfig)
|
|
|
|
sink, _, err := openSinks(cfg)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
core := zapcore.NewCore(encoder, sink, cfg.Level)
|
|
|
|
// error output
|
|
|
|
if z.errorOutput != "" {
|
|
|
|
rotatedLogger := errorRotatedLogger(z.errorOutput, 10, 30, 7)
|
|
|
|
errorOutputOption := zap.Hooks(func(entry zapcore.Entry) error {
|
|
|
|
if entry.Level == zap.ErrorLevel {
|
|
|
|
msg, err := encoder.EncodeEntry(entry, nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-03-10 23:49:52 +08:00
|
|
|
_, _ = rotatedLogger.Write(msg.Bytes())
|
2022-10-11 17:36:09 +08:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
z.opts = append(z.opts, errorOutputOption)
|
|
|
|
}
|
|
|
|
l := zap.New(core, z.opts...)
|
|
|
|
|
|
|
|
z.logger = l
|
|
|
|
z.instance = z.logger.Sugar()
|
|
|
|
}
|
|
|
|
return z.instance
|
|
|
|
}
|
|
|
|
|
2023-03-10 23:49:52 +08:00
|
|
|
func errorRotatedLogger(file string, maxSize, maxBackups, maxAge int) *lumberjack.Logger {
|
2022-10-11 17:36:09 +08:00
|
|
|
return &lumberjack.Logger{
|
|
|
|
Filename: file,
|
|
|
|
MaxSize: maxSize,
|
2023-03-10 23:49:52 +08:00
|
|
|
MaxBackups: maxBackups,
|
2022-10-11 17:36:09 +08:00
|
|
|
MaxAge: maxAge,
|
|
|
|
Compress: false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
|
|
|
|
enc.AppendString(t.Format(timeFormat))
|
|
|
|
}
|
|
|
|
|
|
|
|
type logWriter struct{}
|
|
|
|
|
|
|
|
func (l logWriter) Write(bytes []byte) (int, error) {
|
2023-03-10 23:49:52 +08:00
|
|
|
_, _ = os.Stderr.WriteString(time.Now().Format(timeFormat))
|
|
|
|
_, _ = os.Stderr.Write([]byte("\t"))
|
2022-10-11 17:36:09 +08:00
|
|
|
return os.Stderr.Write(bytes)
|
|
|
|
}
|