Browse Source

1.0.0

master 1.0.0
王海涛 2 years ago
commit
1e5870c065
  1. 1
      .gitignore
  2. 121
      README.md
  3. 33
      context.go
  4. 27
      cronlog/log.go
  5. 165
      distribution/logger.go
  6. 18
      encoder.go
  7. 59
      example/context/main.go
  8. 70
      example/example.go
  9. 11
      example/simple/simple.go
  10. 16
      example/vlevel/v_level.go
  11. 47
      gin/logMiddleware.go
  12. 38
      go.mod
  13. 108
      go.sum
  14. 61
      klog/logger.go
  15. 553
      log.go
  16. 42
      log_test.go
  17. 59
      logrus/hook.go
  18. 17
      logrus/logger.go
  19. 143
      options.go
  20. 23
      options_test.go
  21. 93
      types.go

1
.gitignore

@ -0,0 +1 @@
.idea

121
README.md

@ -0,0 +1,121 @@
# pkg/util/log
本包基于`github.com/tkestack/tke/pkg/util/log`开发
`pkg/util/log` 是一个生产可用的日志包,基于 `zap` 包封装。除了实现 `Go` 日志包的基本功能外,还实现了很多高级功能,`pkg/util/log`具有如下特性:
- 支持日志级别:`Debug`、`Info`、`Warn`、`Error`、`Panic`、`Fatal`。
- 支持自定义配置。
- 支持文件名和行号。
- 支持输出掉标准输出和文件,可以同时输出到多个地方。
- 支持 `JSON``Text` 两种日志格式。
- 支持颜色输出。
- 高性能。
- 支持结构化日志记录。
- **兼容标准库 `log` 包**
- **支持Context(业务定制)**
## 使用方法
### 一个简单的示例
创建一个 `example2.go` 文件,内容如下:
```go
package main
func main() {
defer log.Flush()
// Debug、Info(with field)、Warnf、Errorw使用
log.Debug("This is a debug message")
log.Info("This is a info message", log.Int32("int_key", 10))
log.Warnf("This is a formatted %s message", "warn")
}
```
执行代码:
```bash
$ go run example2.go
2020-12-05 07:56:37.154 info example/example2.go:12 This is a info message {"int_key": 10}
2020-12-05 07:56:37.154 warn example/example2.go:13 This is a formatted warn message
```
上述代码使用 `pkg/util/log` 包默认的全局 `logger`,分别在 `Debug` 、`Info` 和 `Warn` 级别打印了一条日志。
### 初始化日志包
可以使用 `Init` 来初始化一个日志包,如下:
```go
// logger配置
opts := &log.Options{
Level: "debug",
Format: "console",
EnableColor: true,
EnableCaller: true,
OutputPaths: []string{"test.log", "stdout"},
ErrorOutputPaths: []string{"error.log"},
}
// 初始化全局logger
log.Init(opts)
```
Format 支持 `console``json` 2 种格式:
- console:输出为 text 格式。例如:`2020-12-05 08:12:02.324 DEBUG example/example.go:43 This is a debug message`
- json:输出为 json 格式,例如:`{"level":"debug","time":"2020-12-05 08:12:54.113","caller":"example/example.go:43","msg":"This is a debug message"}`
OutputPaths,可以设置日志输出:
- stdout:输出到标准输出。
- stderr:输出到标准错误输出。
- /var/log/test.log:输出到文件。
支持同时输出到多个输出。
EnableColor 为 `true` 开启颜色输出,为 `false` 关闭颜色输出。
### 结构化日志输出
`pkg/util/log` 也支持结构化日志打印,例如:
```go
log.Info("This is a info message", log.Int32("int_key", 10))
log.Infow("Message printed with Errorw", "X-Request-ID", "fbf54504-64da-4088-9b86-67824a7fb508")
```
对应的输出结果为:
```
2020-12-05 08:16:18.749 INFO example/example.go:44 This is a info message {"int_key": 10}
2020-12-05 08:16:18.749 ERROR example/example.go:46 Message printed with Errorw {"X-Request-ID": "fbf54504-64da-4088-9b86-67824a7fb508"}
```
log.Info 这类函数需要指定具体的类型,以最大化的 提高日志的性能。log.Infow 这类函数,不用指定具体的类型,底层使用了反射,性能会差些。建议用在低频调用的函数中。
## 支持V level
创建 `v_level.go`,内容如下:
```go
package main
func main() {
defer log.Flush()
log.V(0).Info("This is a V level message")
log.V(0).Infow("This is a V level message with fields", "X-Request-ID", "7a7b9f24-4cae-4b2a-9464-69088b45b904")
}
```
执行如上代码:
```bash
$ go run v_level.go
2020-12-05 08:20:37.763 info example/v_level.go:10 This is a V level message
2020-12-05 08:20:37.763 info example/v_level.go:11 This is a V level message with fields {"X-Request-ID": "7a7b9f24-4cae-4b2a-9464-69088b45b904"}
```
## 完整的示例
一个完整的示例请参考[example.go](./example/example.go)。

33
context.go

@ -0,0 +1,33 @@
package log
import (
"context"
)
type key int
const (
logContextKey key = iota
)
// WithContext 把命令行输出logger保存到context中.可以使用FromContext(ctx)获取绑定的logger
func WithContext(ctx context.Context) context.Context {
return std.WithContext(ctx)
}
// WithContext 把指定的logger保存到context中.可以使用FromContext(ctx)获取绑定的logger
func (l *zapLogger) WithContext(ctx context.Context) context.Context {
return context.WithValue(ctx, logContextKey, l)
}
// FromContext 从ctx中获取logger
func FromContext(ctx context.Context) Logger {
if ctx != nil {
logger := ctx.Value(logContextKey)
if logger != nil {
return logger.(Logger)
}
}
return WithName("Unknown-Context")
}

27
cronlog/log.go

@ -0,0 +1,27 @@
package cronlog
import (
"fmt"
"go.uber.org/zap"
)
type logger struct {
zapLogger *zap.SugaredLogger
}
// NewLogger 创建一个兼容 `github.com/robfig/cron.Logger` 的logger
func NewLogger(zapLogger *zap.SugaredLogger) logger {
return logger{zapLogger: zapLogger}
}
func (l logger) Info(msg string, args ...interface{}) {
l.zapLogger.Infow(msg, args...)
}
func (l logger) Error(err error, msg string, args ...interface{}) {
l.zapLogger.Errorw(fmt.Sprintf(msg, args...), "error", err.Error())
}
func (l logger) Flush() {
_ = l.zapLogger.Sync()
}

165
distribution/logger.go

@ -0,0 +1,165 @@
// Package distribution 实现了兼容 logrus/std log/prometheus 的logger
package distribution
import (
logruslogger "cynking.com/pkg/log/logrus"
"fmt"
"github.com/sirupsen/logrus"
"go.uber.org/zap"
)
// Logger is a logger which compatible to logrus/std log/prometheus.
// it implements Print() Println() Printf() Dbug() Debugln() Debugf() Info() Infoln() Infof() Warn() Warnln() Warnf()
// Error() Errorln() Errorf() Fatal() Fataln() Fatalf() Panic() Panicln() Panicf() With() WithField() WithFields().
type Logger struct {
logger *zap.Logger
logrusLogger *logrus.Logger
}
// NewLogger create the field logger object by giving zap logger.
func NewLogger(logger *zap.Logger) *Logger {
return &Logger{
logger: logger,
logrusLogger: logruslogger.NewLogger(logger),
}
}
// Print logs a message at level Print on the compatibleLogger.
func (l *Logger) Print(args ...interface{}) {
l.logger.Info(fmt.Sprint(args...))
}
// Println logs a message at level Print on the compatibleLogger.
func (l *Logger) Println(args ...interface{}) {
l.logger.Info(fmt.Sprint(args...))
}
// Printf logs a message at level Print on the compatibleLogger.
func (l *Logger) Printf(format string, args ...interface{}) {
l.logger.Info(fmt.Sprintf(format, args...))
}
// Trace logs a message at level Trace on the compatibleLogger.
func (l *Logger) Trace(args ...interface{}) {
l.logger.Debug(fmt.Sprint(args...))
}
// Traceln logs a message at level Trace on the compatibleLogger.
func (l *Logger) Traceln(args ...interface{}) {
l.logger.Debug(fmt.Sprint(args...))
}
// Tracef logs a message at level Trace on the compatibleLogger.
func (l *Logger) Tracef(format string, args ...interface{}) {
l.logger.Debug(fmt.Sprintf(format, args...))
}
// Debug logs a message at level Debug on the compatibleLogger.
func (l *Logger) Debug(args ...interface{}) {
l.logger.Debug(fmt.Sprint(args...))
}
// Debugln logs a message at level Debug on the compatibleLogger.
func (l *Logger) Debugln(args ...interface{}) {
l.logger.Debug(fmt.Sprint(args...))
}
// Debugf logs a message at level Debug on the compatibleLogger.
func (l *Logger) Debugf(format string, args ...interface{}) {
l.logger.Debug(fmt.Sprintf(format, args...))
}
// Info logs a message at level Info on the compatibleLogger.
func (l *Logger) Info(args ...interface{}) {
l.logger.Info(fmt.Sprint(args...))
}
// Infoln logs a message at level Info on the compatibleLogger.
func (l *Logger) Infoln(args ...interface{}) {
l.logger.Info(fmt.Sprint(args...))
}
// Infof logs a message at level Info on the compatibleLogger.
func (l *Logger) Infof(format string, args ...interface{}) {
l.logger.Info(fmt.Sprintf(format, args...))
}
// Warn logs a message at level Warn on the compatibleLogger.
func (l *Logger) Warn(args ...interface{}) {
l.logger.Warn(fmt.Sprint(args...))
}
// Warnln logs a message at level Warn on the compatibleLogger.
func (l *Logger) Warnln(args ...interface{}) {
l.logger.Warn(fmt.Sprint(args...))
}
// Warnf logs a message at level Warn on the compatibleLogger.
func (l *Logger) Warnf(format string, args ...interface{}) {
l.logger.Warn(fmt.Sprintf(format, args...))
}
// Warning logs a message at level Warn on the compatibleLogger.
func (l *Logger) Warning(args ...interface{}) {
l.logger.Warn(fmt.Sprint(args...))
}
// Warningln logs a message at level Warning on the compatibleLogger.
func (l *Logger) Warningln(args ...interface{}) {
l.logger.Warn(fmt.Sprint(args...))
}
// Warningf logs a message at level Warning on the compatibleLogger.
func (l *Logger) Warningf(format string, args ...interface{}) {
l.logger.Warn(fmt.Sprintf(format, args...))
}
// Error logs a message at level Error on the compatibleLogger.
func (l *Logger) Error(args ...interface{}) {
l.logger.Error(fmt.Sprint(args...))
}
// Errorln logs a message at level Error on the compatibleLogger.
func (l *Logger) Errorln(args ...interface{}) {
l.logger.Error(fmt.Sprint(args...))
}
// Errorf logs a message at level Error on the compatibleLogger.
func (l *Logger) Errorf(format string, args ...interface{}) {
l.logger.Error(fmt.Sprintf(format, args...))
}
// Fatal logs a message at level Fatal on the compatibleLogger.
func (l *Logger) Fatal(args ...interface{}) {
l.logger.Fatal(fmt.Sprint(args...))
}
// Fatalln logs a message at level Fatal on the compatibleLogger.
func (l *Logger) Fatalln(args ...interface{}) {
l.logger.Fatal(fmt.Sprint(args...))
}
// Fatalf logs a message at level Fatal on the compatibleLogger.
func (l *Logger) Fatalf(format string, args ...interface{}) {
l.logger.Fatal(fmt.Sprintf(format, args...))
}
// Panic logs a message at level Painc on the compatibleLogger.
func (l *Logger) Panic(args ...interface{}) {
l.logger.Panic(fmt.Sprint(args...))
}
// Panicln logs a message at level Painc on the compatibleLogger.
func (l *Logger) Panicln(args ...interface{}) {
l.logger.Panic(fmt.Sprint(args...))
}
// Panicf logs a message at level Painc on the compatibleLogger.
func (l *Logger) Panicf(format string, args ...interface{}) {
l.logger.Panic(fmt.Sprintf(format, args...))
}
// WithError return a logger with an error field.
func (l *Logger) WithError(err error) *logrus.Entry {
return logrus.NewEntry(l.logrusLogger).WithError(err)
}

18
encoder.go

@ -0,0 +1,18 @@
package log
import (
"go.uber.org/zap/zapcore"
"time"
)
func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
}
func autoDurationEncoder(d time.Duration, enc zapcore.PrimitiveArrayEncoder) {
//enc.AppendFloat64(float64(d) / float64(time.Millisecond))
if d > time.Minute {
d = d.Truncate(time.Second)
}
enc.AppendString(d.String())
}

59
example/context/main.go

@ -0,0 +1,59 @@
// Copyright 2020 Lingfei Kong <colin404@foxmail.com>. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package main
import (
"context"
"flag"
"cynking.com/pkg/log"
)
var (
h bool
level int
format string
)
func main() {
flag.BoolVar(&h, "h", false, "Print this help.")
flag.IntVar(&level, "l", 0, "Log level.")
flag.StringVar(&format, "f", "console", "log output format.")
flag.Parse()
if h {
flag.Usage()
return
}
// logger配置
opts := &log.Options{
Level: "debug",
Format: "console",
EnableColor: true,
DisableCaller: true,
OutputPaths: []string{"test.log", "stdout"},
ErrorOutputPaths: []string{"error.log"},
}
// 初始化全局logger
log.Init(opts)
defer log.Flush()
// WithValues使用
lv := log.WithValues("X-Request-ID", "7a7b9f24-4cae-4b2a-9464-69088b45b904")
// Context使用
lv.Infof("Start to call pirntString function")
ctx := lv.WithContext(context.Background())
pirntString(ctx, "World")
}
func pirntString(ctx context.Context, str string) {
lc := log.FromContext(ctx)
lc.Infof("Hello %s", str)
}

70
example/example.go

@ -0,0 +1,70 @@
// Copyright 2020 Lingfei Kong <colin404@foxmail.com>. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package main
import (
"context"
"flag"
"cynking.com/pkg/log"
)
var (
h bool
level int
format string
)
func main() {
flag.BoolVar(&h, "h", false, "Print this help.")
flag.IntVar(&level, "l", 0, "Log level.")
flag.StringVar(&format, "f", "console", "log output format.")
flag.Parse()
if h {
flag.Usage()
return
}
// logger配置
opts := &log.Options{
Level: "debug",
Format: "console",
EnableColor: true, // if you need output to local path, with EnableColor must be false.
DisableCaller: true,
OutputPaths: []string{"test.log", "stdout"},
ErrorOutputPaths: []string{"error.log"},
}
// 初始化全局logger
log.Init(opts)
defer log.Flush()
// Debug、Info(with field)、Warnf、Errorw使用
log.Debug("This is a debug message")
log.Info("This is a info message", log.Int32("int_key", 10))
log.Warnf("This is a formatted %s message", "warn")
log.Errorw("Message printed with Errorw", "X-Request-ID", "fbf54504-64da-4088-9b86-67824a7fb508")
// WithValues使用
lv := log.WithValues("X-Request-ID", "7a7b9f24-4cae-4b2a-9464-69088b45b904")
lv.Infow("Info message printed with [WithValues] logger")
lv.Infow("Debug message printed with [WithValues] logger")
// Context使用
ctx := lv.WithContext(context.Background())
lc := log.FromContext(ctx)
lc.Info("Message printed with [WithContext] logger")
ln := lv.WithName("test")
ln.Info("Message printed with [WithName] logger")
// V level使用
log.V(log.InfoLevel).Info("This is a V level message")
log.V(log.ErrorLevel).
Infow("This is a V level message with fields", "X-Request-ID", "7a7b9f24-4cae-4b2a-9464-69088b45b904")
}

11
example/simple/simple.go

@ -0,0 +1,11 @@
// Copyright 2020 Lingfei Kong <colin404@foxmail.com>. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package main
import "cynking.com/pkg/log"
func main() {
log.Infof("this is a test log, message: %s", "good")
}

16
example/vlevel/v_level.go

@ -0,0 +1,16 @@
// Copyright 2020 Lingfei Kong <colin404@foxmail.com>. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package main
import (
"cynking.com/pkg/log"
)
func main() {
defer log.Flush()
log.V(0).Info("This is a V level message")
log.V(0).Infow("This is a V level message with fields", "X-Request-ID", "7a7b9f24-4cae-4b2a-9464-69088b45b904")
}

47
gin/logMiddleware.go

@ -0,0 +1,47 @@
package ginLog
import (
"cynking.com/pkg/log"
"github.com/gin-gonic/gin"
"time"
)
func NamedLogger(msg string) gin.HandlerFunc {
return func(c *gin.Context) {
// Start timer
start := time.Now()
path := c.Request.URL.Path
raw := c.Request.URL.RawQuery
// Process request
c.Next()
param := gin.LogFormatterParams{
Request: c.Request,
Keys: c.Keys,
}
// Stop timer
param.TimeStamp = time.Now()
param.Latency = param.TimeStamp.Sub(start)
param.ClientIP = c.ClientIP()
param.Method = c.Request.Method
param.StatusCode = c.Writer.Status()
param.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String()
param.BodySize = c.Writer.Size()
if raw != "" {
path = path + "?" + raw
}
param.Path = path
// todo:请求处理过长时间提升日志等级 debug(正常),info(慢速),warn(可能浏览器已判定超时)处理
log.Debug(msg,
log.Int("Status", param.StatusCode),
log.Duration("Latency", param.Latency),
log.String("Ip", param.ClientIP),
log.String("Method", param.Method),
log.String("Path", param.Path),
log.String("ErrorMessage", param.ErrorMessage))
}
}

38
go.mod

@ -0,0 +1,38 @@
module cynking.com/pkg/log
go 1.18
require (
github.com/gin-gonic/gin v1.8.1
github.com/sirupsen/logrus v1.9.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.1
go.uber.org/zap v1.23.0
k8s.io/klog v1.0.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.10.0 // indirect
github.com/goccy/go-json v0.9.7 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
golang.org/x/text v0.3.6 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

108
go.sum

@ -0,0 +1,108 @@
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
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/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=

61
klog/logger.go

@ -0,0 +1,61 @@
// Package klog init klog logger. klog is used by kubernetes, this can compatible the kubernetes packages.
package klog
import (
"flag"
"go.uber.org/zap"
"k8s.io/klog"
)
// InitLogger init klog by zap logger.
func InitLogger(zapLogger *zap.Logger) {
fs := flag.NewFlagSet("klog", flag.ExitOnError)
klog.InitFlags(fs)
defer klog.Flush()
klog.SetOutputBySeverity("INFO", &infoLogger{logger: zapLogger})
klog.SetOutputBySeverity("WARNING", &warnLogger{logger: zapLogger})
klog.SetOutputBySeverity("FATAL", &fatalLogger{logger: zapLogger})
klog.SetOutputBySeverity("ERROR", &errorLogger{logger: zapLogger})
_ = fs.Set("skip_headers", "true")
_ = fs.Set("logtostderr", "false")
}
type infoLogger struct {
logger *zap.Logger
}
func (l *infoLogger) Write(p []byte) (n int, err error) {
l.logger.Info(string(p[:len(p)-1]))
return len(p), nil
}
type warnLogger struct {
logger *zap.Logger
}
func (l *warnLogger) Write(p []byte) (n int, err error) {
l.logger.Warn(string(p[:len(p)-1]))
return len(p), nil
}
type fatalLogger struct {
logger *zap.Logger
}
func (l *fatalLogger) Write(p []byte) (n int, err error) {
l.logger.Fatal(string(p[:len(p)-1]))
return len(p), nil
}
type errorLogger struct {
logger *zap.Logger
}
func (l *errorLogger) Write(p []byte) (n int, err error) {
l.logger.Error(string(p[:len(p)-1]))
return len(p), nil
}

553
log.go

@ -0,0 +1,553 @@
package log
import (
"context"
"fmt"
"log"
"sync"
"cynking.com/pkg/log/klog"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// InfoLogger represents the ability to log non-error messages, at a particular verbosity.
type InfoLogger interface {
// Info logs a non-error message with the given key/value pairs as context.
//
// The msg argument should be used to add some constant description to
// the log line. The key/value pairs can then be used to add additional
// variable information. The key/value pairs should alternate string
// keys and arbitrary values.
Info(msg string, fields ...Field)
Infof(format string, v ...interface{})
Infow(msg string, keysAndValues ...interface{})
// Enabled tests whether this InfoLogger is enabled. For example,
// commandline flags might be used to set the logging verbosity and disable
// some info logs.
Enabled() bool
}
// Logger represents the ability to log messages, both errors and not.
type Logger interface {
// All Loggers implement InfoLogger. Calling InfoLogger methods directly on
// a Logger value is equivalent to calling them on a V(0) InfoLogger. For
// example, logger.Info() produces the same result as logger.V(0).Info.
InfoLogger
Debug(msg string, fields ...Field)
Debugf(format string, v ...interface{})
Debugw(msg string, keysAndValues ...interface{})
Warn(msg string, fields ...Field)
Warnf(format string, v ...interface{})
Warnw(msg string, keysAndValues ...interface{})
Error(msg string, fields ...Field)
Errorf(format string, v ...interface{})
Errorw(msg string, keysAndValues ...interface{})
Panic(msg string, fields ...Field)
Panicf(format string, v ...interface{})
Panicw(msg string, keysAndValues ...interface{})
Fatal(msg string, fields ...Field)
Fatalf(format string, v ...interface{})
Fatalw(msg string, keysAndValues ...interface{})
// V returns an InfoLogger value for a specific verbosity level. A higher
// verbosity level means a log message is less important. It's illegal to
// pass a log level less than zero.
V(level Level) InfoLogger
Write(p []byte) (n int, err error)
// WithValues adds some key-value pairs of context to a logger.
// See Info for documentation on how key/value pairs work.
WithValues(keysAndValues ...interface{}) Logger
// WithName adds a new element to the logger's name.
// Successive calls with WithName continue to append
// suffixes to the logger's name. It's strongly recommended
// that name segments contain only letters, digits, and hyphens
// (see the package documentation for more information).
WithName(name string) Logger
// WithContext returns a copy of context in which the log value is set.
WithContext(ctx context.Context) context.Context
// Flush calls the underlying Core's Sync method, flushing any buffered
// log entries. Applications should take care to call Sync before exiting.
Flush()
}
var _ Logger = &zapLogger{}
// noopInfoLogger is a logr.InfoLogger that's always disabled, and does nothing.
type noopInfoLogger struct{}
func (l *noopInfoLogger) Enabled() bool { return false }
func (l *noopInfoLogger) Info(_ string, _ ...Field) {}
func (l *noopInfoLogger) Infof(_ string, _ ...interface{}) {}
func (l *noopInfoLogger) Infow(_ string, _ ...interface{}) {}
var disabledInfoLogger = &noopInfoLogger{}
// NB: right now, we always use the equivalent of sugared logging.
// This is necessary, since logr doesn't define non-suggared types,
// and using zap-specific non-suggared types would make uses tied
// directly to Zap.
// infoLogger is a logr.InfoLogger that uses Zap to log at a particular
// level. The level has already been converted to a Zap level, which
// is to say that `logrLevel = -1*zapLevel`.
type infoLogger struct {
level zapcore.Level
log *zap.Logger
}
func (l *infoLogger) Enabled() bool { return true }
func (l *infoLogger) Info(msg string, fields ...Field) {
if checkedEntry := l.log.Check(l.level, msg); checkedEntry != nil {
checkedEntry.Write(fields...)
}
}
func (l *infoLogger) Infof(format string, args ...interface{}) {
if checkedEntry := l.log.Check(l.level, fmt.Sprintf(format, args...)); checkedEntry != nil {
checkedEntry.Write()
}
}
func (l *infoLogger) Infow(msg string, keysAndValues ...interface{}) {
if checkedEntry := l.log.Check(l.level, msg); checkedEntry != nil {
checkedEntry.Write(handleFields(l.log, keysAndValues)...)
}
}
// zapLogger is a logr.Logger that uses Zap to log.
type zapLogger struct {
// NB: this looks very similar to zap.SugaredLogger, but
// deals with our desire to have multiple verbosity levels.
zapLogger *zap.Logger
infoLogger
}
// handleFields converts a bunch of arbitrary key-value pairs into Zap fields. It takes
// additional pre-converted Zap fields, for use with automatically attached fields, like
// `error`.
func handleFields(l *zap.Logger, args []interface{}, additional ...zap.Field) []zap.Field {
// a slightly modified version of zap.SugaredLogger.sweetenFields
if len(args) == 0 {
// fast-return if we have no suggared fields.
return additional
}
// unlike Zap, we can be pretty sure users aren't passing structured
// fields (since logr has no concept of that), so guess that we need a
// little less space.
fields := make([]zap.Field, 0, len(args)/2+len(additional))
for i := 0; i < len(args); {
// check just in case for strongly-typed Zap fields, which is illegal (since
// it breaks implementation agnosticism), so we can give a better error message.
if _, ok := args[i].(zap.Field); ok {
l.DPanic("strongly-typed Zap Field passed to logr", zap.Any("zap field", args[i]))
break
}
// make sure this isn't a mismatched key
if i == len(args)-1 {
l.DPanic("odd number of arguments passed as key-value pairs for logging", zap.Any("ignored key", args[i]))
break
}
// process a key-value pair,
// ensuring that the key is a string
key, val := args[i], args[i+1]
keyStr, isString := key.(string)
if !isString {
// if the key isn't a string, DPanic and stop logging
l.DPanic(
"non-string key argument passed to logging, ignoring all later arguments",
zap.Any("invalid key", key),
)
break
}
fields = append(fields, zap.Any(keyStr, val))
i += 2
}
return append(fields, additional...)
}
var (
std = New(NewOptions())
mu sync.Mutex
)
// Init initializes logger with specified options.
func Init(opts *Options) {
mu.Lock()
defer mu.Unlock()
std = New(opts)
}
// New create logger by opts which can custmoized by command arguments.
func New(opts *Options) *zapLogger {
if opts == nil {
opts = NewOptions()
}
var zapLevel zapcore.Level
if err := zapLevel.UnmarshalText([]byte(opts.Level)); err != nil {
zapLevel = zapcore.InfoLevel
}
encodeLevel := zapcore.CapitalLevelEncoder
// when output to local path, with color is forbidden
if opts.Format == ConsoleFormat && opts.EnableColor {
encodeLevel = zapcore.CapitalColorLevelEncoder
}
encoderConfig := zapcore.EncoderConfig{
MessageKey: "message",
LevelKey: "level",
TimeKey: "timestamp",
NameKey: "logger",
CallerKey: "caller",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: encodeLevel,
EncodeTime: timeEncoder,
EncodeDuration: autoDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
loggerConfig := &zap.Config{
Level: zap.NewAtomicLevelAt(zapLevel),
Development: opts.Development,
DisableCaller: opts.DisableCaller,
DisableStacktrace: opts.DisableStacktrace,
Sampling: &zap.SamplingConfig{
Initial: 100,
Thereafter: 100,
},
Encoding: opts.Format,
EncoderConfig: encoderConfig,
OutputPaths: opts.OutputPaths,
ErrorOutputPaths: opts.ErrorOutputPaths,
}
var err error
l, err := loggerConfig.Build(zap.AddStacktrace(zapcore.PanicLevel), zap.AddCallerSkip(1))
if err != nil {
panic(err)
}
logger := &zapLogger{
zapLogger: l.Named(opts.Name),
infoLogger: infoLogger{
log: l,
level: zap.InfoLevel,
},
}
klog.InitLogger(l)
zap.RedirectStdLog(l)
return logger
}
// SugaredLogger returns global sugared logger.
func SugaredLogger() *zap.SugaredLogger {
return std.zapLogger.Sugar()
}
// StdErrLogger returns logger of standard library which writes to supplied zap
// logger at error level.
func StdErrLogger() *log.Logger {
if std == nil {
return nil
}
if l, err := zap.NewStdLogAt(std.zapLogger, zapcore.ErrorLevel); err == nil {
return l
}
return nil
}
// StdInfoLogger returns logger of standard library which writes to supplied zap
// logger at info level.
func StdInfoLogger() *log.Logger {
if std == nil {
return nil
}
if l, err := zap.NewStdLogAt(std.zapLogger, zapcore.InfoLevel); err == nil {
return l
}
return nil
}
// V return a leveled InfoLogger.
func V(level Level) InfoLogger { return std.V(level) }
func (l *zapLogger) V(level Level) InfoLogger {
if l.zapLogger.Core().Enabled(level) {
return &infoLogger{
level: level,
log: l.zapLogger,
}
}
return disabledInfoLogger
}
func (l *zapLogger) Write(p []byte) (n int, err error) {
l.zapLogger.Info(string(p))
return len(p), nil
}
// WithValues creates a child logger and adds adds Zap fields to it.
func WithValues(keysAndValues ...interface{}) Logger { return std.WithValues(keysAndValues...) }
func (l *zapLogger) WithValues(keysAndValues ...interface{}) Logger {
newLogger := l.zapLogger.With(handleFields(l.zapLogger, keysAndValues)...)
return NewLogger(newLogger)
}
// WithName adds a new path segment to the logger's name. Segments are joined by
// periods. By default, Loggers are unnamed.
func WithName(s string) Logger { return std.WithName(s) }
func (l *zapLogger) WithName(name string) Logger {
newLogger := l.zapLogger.Named(name)
return NewLogger(newLogger)
}
// Flush calls the underlying Core's Sync method, flushing any buffered
// log entries. Applications should take care to call Sync before exiting.
func Flush() { std.Flush() }
func (l *zapLogger) Flush() {
_ = l.zapLogger.Sync()
}
// NewLogger creates a new logr.Logger using the given Zap Logger to log.
func NewLogger(l *zap.Logger) Logger {
return &zapLogger{
zapLogger: l,
infoLogger: infoLogger{
log: l,
level: zap.InfoLevel,
},
}
}
// ZapLogger used for other log wrapper such as klog.
func ZapLogger() *zap.Logger {
return std.zapLogger
}
// CheckIntLevel used for other log wrapper such as klog which return if logging a
// message at the specified level is enabled.
func CheckIntLevel(level int32) bool {
var lvl zapcore.Level
if level < 5 {
lvl = zapcore.InfoLevel
} else {
lvl = zapcore.DebugLevel
}
checkEntry := std.zapLogger.Check(lvl, "")
return checkEntry != nil
}
// Debug method output debug level log.
func Debug(msg string, fields ...Field) {
std.zapLogger.Debug(msg, fields...)
}
func (l *zapLogger) Debug(msg string, fields ...Field) {
l.zapLogger.Debug(msg, fields...)
}
// Debugf method output debug level log.
func Debugf(format string, v ...interface{}) {
std.zapLogger.Sugar().Debugf(format, v...)
}
func (l *zapLogger) Debugf(format string, v ...interface{}) {
l.zapLogger.Sugar().Debugf(format, v...)
}
// Debugw method output debug level log.
func Debugw(msg string, keysAndValues ...interface{}) {
std.zapLogger.Sugar().Debugw(msg, keysAndValues...)
}
func (l *zapLogger) Debugw(msg string, keysAndValues ...interface{}) {
l.zapLogger.Sugar().Debugw(msg, keysAndValues...)
}
// Info method output info level log.
func Info(msg string, fields ...Field) {
std.zapLogger.Info(msg, fields...)
}
func (l *zapLogger) Info(msg string, fields ...Field) {
l.zapLogger.Info(msg, fields...)
}
// Infof method output info level log.
func Infof(format string, v ...interface{}) {
std.zapLogger.Sugar().Infof(format, v...)
}
func (l *zapLogger) Infof(format string, v ...interface{}) {
l.zapLogger.Sugar().Infof(format, v...)
}
// Infow method output info level log.
func Infow(msg string, keysAndValues ...interface{}) {
std.zapLogger.Sugar().Infow(msg, keysAndValues...)
}
func (l *zapLogger) Infow(msg string, keysAndValues ...interface{}) {
l.zapLogger.Sugar().Infow(msg, keysAndValues...)
}
// Warn method output warning level log.
func Warn(msg string, fields ...Field) {
std.zapLogger.Warn(msg, fields...)
}
func (l *zapLogger) Warn(msg string, fields ...Field) {
l.zapLogger.Warn(msg, fields...)
}
// Warnf method output warning level log.
func Warnf(format string, v ...interface{}) {
std.zapLogger.Sugar().Warnf(format, v...)
}
func (l *zapLogger) Warnf(format string, v ...interface{}) {
l.zapLogger.Sugar().Warnf(format, v...)
}
// Warnw method output warning level log.
func Warnw(msg string, keysAndValues ...interface{}) {
std.zapLogger.Sugar().Warnw(msg, keysAndValues...)
}
func (l *zapLogger) Warnw(msg string, keysAndValues ...interface{}) {
l.zapLogger.Sugar().Warnw(msg, keysAndValues...)
}
// Error method output error level log.
func Error(msg string, fields ...Field) {
std.zapLogger.Error(msg, fields...)
}
func (l *zapLogger) Error(msg string, fields ...Field) {
l.zapLogger.Error(msg, fields...)
}
// Errorf method output error level log.
func Errorf(format string, v ...interface{}) {
std.zapLogger.Sugar().Errorf(format, v...)
}
func (l *zapLogger) Errorf(format string, v ...interface{}) {
l.zapLogger.Sugar().Errorf(format, v...)
}
// Errorw method output error level log.
func Errorw(msg string, keysAndValues ...interface{}) {
std.zapLogger.Sugar().Errorw(msg, keysAndValues...)
}
func (l *zapLogger) Errorw(msg string, keysAndValues ...interface{}) {
l.zapLogger.Sugar().Errorw(msg, keysAndValues...)
}
// Panic method output panic level log and shutdown application.
func Panic(msg string, fields ...Field) {
std.zapLogger.Panic(msg, fields...)
}
func (l *zapLogger) Panic(msg string, fields ...Field) {
l.zapLogger.Panic(msg, fields...)
}
// Panicf method output panic level log and shutdown application.
func Panicf(format string, v ...interface{}) {
std.zapLogger.Sugar().Panicf(format, v...)
}
func (l *zapLogger) Panicf(format string, v ...interface{}) {
l.zapLogger.Sugar().Panicf(format, v...)
}
// Panicw method output panic level log.
func Panicw(msg string, keysAndValues ...interface{}) {
std.zapLogger.Sugar().Panicw(msg, keysAndValues...)
}
func (l *zapLogger) Panicw(msg string, keysAndValues ...interface{}) {
l.zapLogger.Sugar().Panicw(msg, keysAndValues...)
}
// Fatal method output fatal level log.
func Fatal(msg string, fields ...Field) {
std.zapLogger.Fatal(msg, fields...)
}
func (l *zapLogger) Fatal(msg string, fields ...Field) {
l.zapLogger.Fatal(msg, fields...)
}
// Fatalf method output fatal level log.
func Fatalf(format string, v ...interface{}) {
std.zapLogger.Sugar().Fatalf(format, v...)
}
func (l *zapLogger) Fatalf(format string, v ...interface{}) {
l.zapLogger.Sugar().Fatalf(format, v...)
}
// Fatalw method output Fatalw level log.
func Fatalw(msg string, keysAndValues ...interface{}) {
std.zapLogger.Sugar().Fatalw(msg, keysAndValues...)
}
func (l *zapLogger) Fatalw(msg string, keysAndValues ...interface{}) {
l.zapLogger.Sugar().Fatalw(msg, keysAndValues...)
}
// L method output with specified context value.
func L(ctx context.Context) *zapLogger {
return std.L(ctx)
}
func (l *zapLogger) L(ctx context.Context) *zapLogger {
lg := l.clone()
if requestID := ctx.Value(KeyRequestID); requestID != nil {
lg.zapLogger = lg.zapLogger.With(zap.Any(KeyRequestID, requestID))
}
if username := ctx.Value(KeyUsername); username != nil {
lg.zapLogger = lg.zapLogger.With(zap.Any(KeyUsername, username))
}
if watcherName := ctx.Value(KeyWatcherName); watcherName != nil {
lg.zapLogger = lg.zapLogger.With(zap.Any(KeyWatcherName, watcherName))
}
return lg
}
//nolint:predeclared
func (l *zapLogger) clone() *zapLogger {
clone := *l
return &clone
}

42
log_test.go

@ -0,0 +1,42 @@
package log_test
import (
"cynking.com/pkg/log"
"github.com/spf13/pflag"
"github.com/stretchr/testify/assert"
"testing"
)
func Test_WithName(t *testing.T) {
defer log.Flush() // used for record logger printer
logger := log.WithName("test")
logger.Infow("Hello world!", "foo", "bar") // structed logger
}
func Test_WithValues(t *testing.T) {
defer log.Flush() // used for record logger printer
logger := log.WithValues("key", "value") // used for record context
logger.Info("Hello world!")
logger.Info("Hello world!")
}
func Test_V(t *testing.T) {
defer log.Flush() // used for record logger printer
log.V(0).Infow("Hello world!", "key", "value")
log.V(1).Infow("Hello world!", "key", "value")
}
func Test_Option(t *testing.T) {
fs := pflag.NewFlagSet("test", pflag.ExitOnError)
opt := log.NewOptions()
opt.AddFlags(fs)
args := []string{"--log.level=debug"}
err := fs.Parse(args)
assert.Nil(t, err)
assert.Equal(t, "debug", opt.Level)
}

59
logrus/hook.go

@ -0,0 +1,59 @@
package logrus
import (
"runtime"
"github.com/sirupsen/logrus"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
type hook struct {
logger *zap.Logger
}
func (h *hook) Levels() []logrus.Level {
return logrus.AllLevels
}
func (h *hook) Fire(entry *logrus.Entry) error {
fields := make([]zap.Field, 0, 10)
for key, value := range entry.Data {
if key == logrus.ErrorKey {
fields = append(fields, zap.Error(value.(error)))
} else {
fields = append(fields, zap.Any(key, value))
}
}
switch entry.Level {
case logrus.PanicLevel:
h.Write(zapcore.PanicLevel, entry.Message, fields, entry.Caller)
case logrus.FatalLevel:
h.Write(zapcore.FatalLevel, entry.Message, fields, entry.Caller)
case logrus.ErrorLevel:
h.Write(zapcore.ErrorLevel, entry.Message, fields, entry.Caller)
case logrus.WarnLevel:
h.Write(zapcore.WarnLevel, entry.Message, fields, entry.Caller)
case logrus.InfoLevel:
h.Write(zapcore.InfoLevel, entry.Message, fields, entry.Caller)
case logrus.DebugLevel, logrus.TraceLevel:
h.Write(zapcore.DebugLevel, entry.Message, fields, entry.Caller)
}
return nil
}
func (h *hook) Write(lvl zapcore.Level, msg string, fields []zap.Field, caller *runtime.Frame) {
if ce := h.logger.Check(lvl, msg); ce != nil {
if caller != nil {
ce.Caller = zapcore.NewEntryCaller(caller.PC, caller.File, caller.Line, caller.PC != 0)
}
ce.Write(fields...)
}
}
func newHook(logger *zap.Logger) logrus.Hook {
return &hook{logger: logger}
}

17
logrus/logger.go

@ -0,0 +1,17 @@
package logrus
import (
"io/ioutil"
"github.com/sirupsen/logrus"
"go.uber.org/zap"
)
// NewLogger create a logrus logger, add hook to it and return it.
func NewLogger(zapLogger *zap.Logger) *logrus.Logger {
logger := logrus.New()
logger.SetOutput(ioutil.Discard)
logger.AddHook(newHook(zapLogger))
return logger
}

143
options.go

@ -0,0 +1,143 @@
package log
import (
"encoding/json"
"fmt"
"github.com/spf13/pflag"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"strings"
)
const (
flagLevel = "log.level"
flagDisableCaller = "log.disable-caller"
flagDisableStacktrace = "log.disable-stacktrace"
flagFormat = "log.format"
flagEnableColor = "log.enable-color"
flagOutputPaths = "log.output-paths"
flagErrorOutputPaths = "log.error-output-paths"
flagDevelopment = "log.development"
flagName = "log.name"
ConsoleFormat = "console"
JsonFormat = "json"
)
// Options 日志有关的配置. 设置参考zap的配置
type Options struct {
OutputPaths []string `json:"output-paths" mapstructure:"output-paths"`
ErrorOutputPaths []string `json:"error-output-paths" mapstructure:"error-output-paths"`
Level string `json:"level" mapstructure:"level"`
Format string `json:"format" mapstructure:"format"`
DisableCaller bool `json:"disable-caller" mapstructure:"disable-caller"`
DisableStacktrace bool `json:"disable-stacktrace" mapstructure:"disable-stacktrace"`
EnableColor bool `json:"enable-color" mapstructure:"enable-color"`
Development bool `json:"development" mapstructure:"development"`
Name string `json:"name" mapstructure:"name"`
}
// NewOptions 新建带默认参数的日志配置
func NewOptions() *Options {
return &Options{
Level: zapcore.InfoLevel.String(),
DisableCaller: false,
DisableStacktrace: false,
Format: ConsoleFormat,
EnableColor: false,
Development: false,
OutputPaths: []string{"stdout"},
ErrorOutputPaths: []string{"stderr"},
}
}
// Validate 验证日志配置.
func (o *Options) Validate() []error {
var errs []error
var zapLevel zapcore.Level
if err := zapLevel.UnmarshalText([]byte(o.Level)); err != nil {
errs = append(errs, err)
}
format := strings.ToLower(o.Format)
if format != ConsoleFormat && format != JsonFormat {
errs = append(errs, fmt.Errorf("not a valid log format: %q", o.Format))
}
return errs
}
// AddFlags 读取命令行参数设置日志配置
func (o *Options) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&o.Level, flagLevel, o.Level, "Minimum log output `LEVEL`.")
fs.BoolVar(&o.DisableCaller, flagDisableCaller, o.DisableCaller, "Disable output of caller information in the log.")
fs.BoolVar(&o.DisableStacktrace, flagDisableStacktrace,
o.DisableStacktrace, "Disable the log to record a stack trace for all messages at or above panic level.")
fs.StringVar(&o.Format, flagFormat, o.Format, "Log output `FORMAT`, support plain or json format.")
fs.BoolVar(&o.EnableColor, flagEnableColor, o.EnableColor, "Enable output ansi colors in plain format logs.")
fs.StringSliceVar(&o.OutputPaths, flagOutputPaths, o.OutputPaths, "Output paths of log.")
fs.StringSliceVar(&o.ErrorOutputPaths, flagErrorOutputPaths, o.ErrorOutputPaths, "Error output paths of log.")
fs.BoolVar(
&o.Development,
flagDevelopment,
o.Development,
"Development puts the logger in development mode, which changes "+
"the behavior of DPanicLevel and takes stacktraces more liberally.",
)
fs.StringVar(&o.Name, flagName, o.Name, "The name of the logger.")
}
func (o *Options) String() string {
data, _ := json.Marshal(o)
return string(data)
}
// Build 将设置安装到全局zap logger
func (o *Options) Build() error {
var zapLevel zapcore.Level
if err := zapLevel.UnmarshalText([]byte(o.Level)); err != nil {
zapLevel = zapcore.InfoLevel
}
encodeLevel := zapcore.CapitalLevelEncoder
if o.Format == ConsoleFormat && o.EnableColor {
encodeLevel = zapcore.CapitalColorLevelEncoder
}
zc := &zap.Config{
Level: zap.NewAtomicLevelAt(zapLevel),
Development: o.Development,
DisableCaller: o.DisableCaller,
DisableStacktrace: o.DisableStacktrace,
Sampling: &zap.SamplingConfig{
Initial: 100,
Thereafter: 100,
},
Encoding: o.Format,
EncoderConfig: zapcore.EncoderConfig{
MessageKey: "message",
LevelKey: "level",
TimeKey: "timestamp",
NameKey: "logger",
CallerKey: "caller",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: encodeLevel,
EncodeTime: timeEncoder,
EncodeDuration: autoDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
EncodeName: zapcore.FullNameEncoder,
},
OutputPaths: o.OutputPaths,
ErrorOutputPaths: o.ErrorOutputPaths,
}
logger, err := zc.Build(zap.AddStacktrace(zapcore.PanicLevel))
if err != nil {
return err
}
zap.RedirectStdLog(logger.Named(o.Name))
zap.ReplaceGlobals(logger)
return nil
}

23
options_test.go

@ -0,0 +1,23 @@
package log_test
import (
"cynking.com/pkg/log"
"fmt"
"github.com/stretchr/testify/assert"
"testing"
)
func Test_Options_Validate(t *testing.T) {
opts := &log.Options{
Level: "test",
Format: "test",
EnableColor: true,
DisableCaller: false,
OutputPaths: []string{"stdout"},
ErrorOutputPaths: []string{"stderr"},
}
errs := opts.Validate()
expected := `[unrecognized level: "test" not a valid log format: "test"]`
assert.Equal(t, expected, fmt.Sprintf("%s", errs))
}

93
types.go

@ -0,0 +1,93 @@
package log
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// 这里配置全局log字段
const (
KeyRequestID string = "requestID"
KeyUsername string = "username"
KeyWatcherName string = "watcher"
)
// 下面是zap包的别名
// Field is an alias for the field structure in the underlying log frame.
type Field = zapcore.Field
// Level is an alias for the level structure in the underlying log frame.
type Level = zapcore.Level
var (
// DebugLevel logs are typically voluminous, and are usually disabled in
// production.
DebugLevel = zapcore.DebugLevel
// InfoLevel is the default logging priority.
InfoLevel = zapcore.InfoLevel
// WarnLevel logs are more important than Info, but don't need individual
// human review.
WarnLevel = zapcore.WarnLevel
// ErrorLevel logs are high-priority. If an application is running smoothly,
// it shouldn't generate any error-level logs.
ErrorLevel = zapcore.ErrorLevel
// PanicLevel logs a message, then panics.
PanicLevel = zapcore.PanicLevel
// FatalLevel logs a message, then calls os.Exit(1).
FatalLevel = zapcore.FatalLevel
)
// Alias for zap type functions.
var (
Any = zap.Any
Array = zap.Array
Object = zap.Object
Binary = zap.Binary
Bool = zap.Bool
Bools = zap.Bools
ByteString = zap.ByteString
ByteStrings = zap.ByteStrings
Complex64 = zap.Complex64
Complex64s = zap.Complex64s
Complex128 = zap.Complex128
Complex128s = zap.Complex128s
Duration = zap.Duration
Durations = zap.Durations
Err = zap.Error
Errors = zap.Errors
Float32 = zap.Float32
Float32s = zap.Float32s
Float64 = zap.Float64
Float64s = zap.Float64s
Int = zap.Int
Ints = zap.Ints
Int8 = zap.Int8
Int8s = zap.Int8s
Int16 = zap.Int16
Int16s = zap.Int16s
Int32 = zap.Int32
Int32s = zap.Int32s
Int64 = zap.Int64
Int64s = zap.Int64s
Namespace = zap.Namespace
Reflect = zap.Reflect
Stack = zap.Stack
String = zap.String
Stringer = zap.Stringer
Strings = zap.Strings
Time = zap.Time
Times = zap.Times
Uint = zap.Uint
Uints = zap.Uints
Uint8 = zap.Uint8
Uint8s = zap.Uint8s
Uint16 = zap.Uint16
Uint16s = zap.Uint16s
Uint32 = zap.Uint32
Uint32s = zap.Uint32s
Uint64 = zap.Uint64
Uint64s = zap.Uint64s
Uintptr = zap.Uintptr
Uintptrs = zap.Uintptrs
)
Loading…
Cancel
Save