network/log/zap.go

228 lines
5.1 KiB
Go
Raw Normal View History

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
func (z zapLogger) Warnf(template string, args ...interface{}) {
z.Instance().Warnf(template, args...)
}
// Errorf logs a message at ErrorLevel
func (z zapLogger) Errorf(template string, args ...interface{}) {
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
}
rotatedLogger.Write(msg.Bytes())
}
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
}
func errorRotatedLogger(file string, maxSize, maxBacukups, maxAge int) *lumberjack.Logger {
return &lumberjack.Logger{
Filename: file,
MaxSize: maxSize,
MaxBackups: maxBacukups,
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) {
os.Stderr.WriteString(time.Now().Format(timeFormat))
os.Stderr.Write([]byte("\t"))
return os.Stderr.Write(bytes)
}