diff --git a/.gitignore b/.gitignore index f1c181e..922a0db 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out +.idea/ \ No newline at end of file diff --git a/README.md b/README.md index 686fcb5..4cebc86 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,34 @@ # lg -zap日志项目级别的封装 + +Project logger packaging + +## Getting Started + +Suitable for project log in the container + +``` +package main + +import ( + "github.com/qclaogui/lg" +) + +func main() { + //lg.TimeFormat = time.RFC3339Nano + + // lvl - global log level: Debug(-1), Info(0), Warn(1), Error(2), DPanic(3), Panic(4), Fatal(5) + _ = lg.Init(-1, "demo-project") + + lg.APPLog.Info("Happy Goding!") +} + +``` + +## Versioning + +Using [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/qclaogui/lg/tags). + + +## License + +This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..06add96 --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module github.com/qclaogui/lg + +go 1.12 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pkg/errors v0.8.1 // indirect + github.com/stretchr/testify v1.3.0 // indirect + go.uber.org/atomic v1.4.0 // indirect + go.uber.org/multierr v1.1.0 // indirect + go.uber.org/zap v1.10.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..9804ce4 --- /dev/null +++ b/go.sum @@ -0,0 +1,17 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..b3d19d8 --- /dev/null +++ b/logger.go @@ -0,0 +1,96 @@ +package lg + +import ( + "os" + "sync" + "time" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +var ( + // APPLog is global logger + APPLog *zap.Logger + + // TimeFormat is custom Time format + // example: "2006-01-02T15:04:05.999999999Z07:00" + // 推荐不要设置, 使用默认时间戳 + TimeFormat string + + // onceInit guarantee initialize logger only once + onceInit sync.Once +) + +type commonInfo struct { + Project string `json:"project"` + Hostname string `json:"hostname"` +} + +func (i *commonInfo) MarshalLogObject(enc zapcore.ObjectEncoder) error { + enc.AddString("project", i.Project) + enc.AddString("hostname", i.Hostname) + return nil +} + +// customTimeEncoder encode Time to our custom format +// This example how we can customize zap default functionality +func customTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(t.Format(TimeFormat)) +} + +// Init initializes log by input parameters +// lvl - global log level: Debug(-1), Info(0), Warn(1), Error(2), DPanic(3), Panic(4), Fatal(5) +// timeFormat - custom time format for logger of empty string to use default +func Init(lvl int, project string) (err error) { + onceInit.Do(func() { + // First, define our level-handling logic. + globalLevel := zapcore.Level(lvl) + // High-priority output should also go to standard error, and low-priority + // output should also go to standard out. + // It is usefull for Kubernetes deployment. + // Kubernetes interprets os.Stdout log items as INFO and os.Stderr log items + // as ERROR by default. + highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { + return lvl >= zapcore.ErrorLevel + }) + lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { + return lvl >= globalLevel && lvl < zapcore.ErrorLevel + }) + + cfg := zap.NewProductionEncoderConfig() + if len(TimeFormat) > 0 { + // 默认的TimeKey为(ts) float64类型. 自定义会将TimeKey修改为string,防止es中出现问题,所以换个新的key叫tsp + cfg.TimeKey = "tsp" + cfg.EncodeTime = customTimeEncoder + } + // Optimize the Kafka output for machine consumption and the console output + // for human operators. + consoleEncoder := zapcore.NewJSONEncoder(cfg) + // Join the outputs, encoders, and level-handling functions into + // zapcore.Cores, then tee the four cores together. + core := zapcore.NewTee( + zapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), lowPriority), + zapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stderr), highPriority), + ) + + // ErrorLevel 堆栈跟踪 + stackTrace := zap.AddStacktrace(zap.ErrorLevel) + // 设置初始化字段 + fields := zap.Fields(zap.Object("info", &commonInfo{project, getHostName()})) + + // From a zapcore.Core, it's easy to construct a Logger. + APPLog = zap.New(core, fields, stackTrace) + zap.RedirectStdLog(APPLog) + }) + + return err +} + +// getHostName 获取当前主机的Hostname +func getHostName() string { + if host, err := os.Hostname(); err == nil { + return host + } + return "unknown" +}