王海涛
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