log/log.go

249 lines
5.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package log
import (
"fmt"
"log"
"os"
"path"
"strings"
"time"
"github.com/ehlxr/logger/encoder"
"github.com/ehlxr/logger/bufferpool"
"github.com/ehlxr/logger/crash"
"github.com/pkg/errors"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
var logger *zap.SugaredLogger
const (
DebugLevel = zapcore.DebugLevel
InfoLevel = zapcore.InfoLevel
WarnLevel = zapcore.WarnLevel
ErrorLevel = zapcore.ErrorLevel
DPanicLevel = zapcore.DPanicLevel
PanicLevel = zapcore.PanicLevel
FatalLevel = zapcore.FatalLevel
)
type logConfig struct {
Level zapcore.Level
FilePath string
EnableColors bool
CrashLogPath string
ErrorLogPath string
EnableLineNumber bool
// enable the truncation of the level text to 4 characters.
EnableLevelTruncation bool
EnableErrorStacktrace bool
TimestampFormat string
EnableCapitalLevel bool
atomicLevel zap.AtomicLevel
Name string
}
func init() {
lc := &logConfig{
Level: InfoLevel,
FilePath: "",
EnableColors: false,
CrashLogPath: "",
ErrorLogPath: "",
EnableLineNumber: false,
EnableLevelTruncation: false,
EnableErrorStacktrace: false,
TimestampFormat: "",
EnableCapitalLevel: false,
Name: "",
}
logger = lc.newLogger().Sugar()
}
func (lc *logConfig) Init() {
logger = lc.newLogger().Sugar()
}
func NewLogConfig() *logConfig {
return &logConfig{
Level: DebugLevel,
FilePath: "./log/",
EnableColors: true,
CrashLogPath: "./log/crash.log",
ErrorLogPath: "./log/error_",
EnableLineNumber: true,
EnableLevelTruncation: true,
EnableErrorStacktrace: true,
TimestampFormat: "2006-01-02 15:04:05.000",
EnableCapitalLevel: true,
}
}
func (lc *logConfig) newLogger() *zap.Logger {
if lc.CrashLogPath != "" {
writeCrashLog(lc.CrashLogPath)
}
lc.atomicLevel = zap.NewAtomicLevelAt(lc.Level)
lc.initColor()
cores := []zapcore.Core{
zapcore.NewCore(
encoder.NewTextEncoder(lc.encoderConfig()),
zapcore.Lock(os.Stdout),
lc.atomicLevel,
)}
if lc.FilePath != "" {
cores = append(cores, lc.fileCore())
}
if lc.ErrorLogPath != "" {
cores = append(cores, lc.errorFileCore())
}
core := zapcore.NewTee(cores...)
var options []zap.Option
if lc.EnableLineNumber {
options = append(options, zap.AddCaller(), zap.AddCallerSkip(1))
}
if lc.EnableErrorStacktrace {
options = append(options, zap.AddStacktrace(zapcore.ErrorLevel))
}
zapLog := zap.New(core, options...)
if lc.Name != "" {
zapLog = zapLog.Named(fmt.Sprintf("[%s]", lc.Name))
}
return zapLog
}
func (lc *logConfig) encoderConfig() zapcore.EncoderConfig {
el := lc.encodeLevel
if lc.EnableColors {
el = lc.encodeColorLevel
}
return zapcore.EncoderConfig{
TimeKey: "T",
LevelKey: "L",
NameKey: "N",
CallerKey: "C",
MessageKey: "M",
StacktraceKey: "S",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: el,
EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
tf := time.RFC3339
if lc.TimestampFormat != "" {
tf = lc.TimestampFormat
}
enc.AppendString(t.Format(fmt.Sprintf("[%s]", tf)))
},
EncodeDuration: zapcore.StringDurationEncoder,
EncodeCaller: func(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(trimCallerFilePath(caller))
},
}
}
func (lc *logConfig) fileCore() zapcore.Core {
return zapcore.NewCore(
encoder.NewTextEncoder(lc.encoderConfig()),
// zapcore.NewMultiWriteSyncer(
// zapcore.Lock(os.Stdout),
// lc.fileWriteSyncer(),
// ),
fileWriteSyncer(lc.FilePath),
lc.atomicLevel,
)
}
func (lc *logConfig) errorFileCore() zapcore.Core {
return zapcore.NewCore(
encoder.NewTextEncoder(lc.encoderConfig()),
fileWriteSyncer(lc.ErrorLogPath),
zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl >= zapcore.ErrorLevel
}),
)
}
func (lc *logConfig) encodeLevel(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
levelString := l.CapitalString()
if lc.EnableLevelTruncation {
levelString = levelString[:4]
}
enc.AppendString(fmt.Sprintf("[%s]", levelString))
}
func (lc *logConfig) encodeColorLevel(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
s, ok := _levelToColorStrings[l]
if !ok {
s = _unknownLevelColor.Add(l.CapitalString())
}
enc.AppendString(fmt.Sprintf("[%s]", s))
}
func trimCallerFilePath(ec zapcore.EntryCaller) string {
if !ec.Defined {
return "undefined"
}
// Find the last separator.
idx := strings.LastIndexByte(ec.File, '/')
if idx == -1 {
return ec.FullPath()
}
buf := bufferpool.Get()
buf.AppendString(ec.File[idx+1:])
buf.AppendByte(':')
buf.AppendInt(int64(ec.Line))
caller := buf.String()
buf.Free()
return fmt.Sprintf(" %s", caller)
}
func fileWriteSyncer(name string) zapcore.WriteSyncer {
fileName := fmt.Sprintf("%s%s.log",
name,
time.Now().Format("2006-01-02"))
return zapcore.AddSync(&lumberjack.Logger{
Filename: fileName,
MaxSize: 500, // 单个日志文件大小MB
MaxBackups: 10,
MaxAge: 30, // 保留多少天的日志
LocalTime: true,
Compress: true,
})
}
func writeCrashLog(file string) {
err := os.MkdirAll(path.Dir(file), os.ModePerm)
if err != nil {
log.Fatalf("make crash log dir error. %v", errors.WithStack(err))
}
crash.CrashLog(file)
}
func Fields(args ...interface{}) {
logger = logger.With(args...)
}