王海涛
2 years ago
commit
1e5870c065
21 changed files with 1705 additions and 0 deletions
-
1.gitignore
-
121README.md
-
33context.go
-
27cronlog/log.go
-
165distribution/logger.go
-
18encoder.go
-
59example/context/main.go
-
70example/example.go
-
11example/simple/simple.go
-
16example/vlevel/v_level.go
-
47gin/logMiddleware.go
-
38go.mod
-
108go.sum
-
61klog/logger.go
-
553log.go
-
42log_test.go
-
59logrus/hook.go
-
17logrus/logger.go
-
143options.go
-
23options_test.go
-
93types.go
@ -0,0 +1 @@ |
|||
.idea |
@ -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)。 |
@ -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") |
|||
} |
@ -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() |
|||
} |
@ -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) |
|||
} |
@ -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()) |
|||
} |
@ -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) |
|||
} |
@ -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") |
|||
} |
@ -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") |
|||
} |
@ -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") |
|||
} |
@ -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)) |
|||
} |
|||
} |
@ -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 |
|||
) |
@ -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= |
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -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) |
|||
} |
@ -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} |
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -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)) |
|||
} |
@ -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 |
|||
) |
Write
Preview
Loading…
Cancel
Save
Reference in new issue