1
0
mirror of https://github.com/go-kratos/kratos.git synced 2026-05-22 10:15:24 +02:00

log: add level printer (#918)

* add level printer

* fix helper args

* fix test

* Update README.md
This commit is contained in:
Tony Chen
2021-05-17 16:34:28 +08:00
committed by GitHub
parent 24b1ca6bc3
commit d78eb3ee4b
22 changed files with 84 additions and 186 deletions
+12 -33
View File
@@ -5,41 +5,20 @@
### Structured logging
```go
logger := log.NewStdLogger(os.Stdout)
logger = log.With(logger, "caller", log.DefaultCaller, "ts", log.DefaultTimestamp)
Logger logger = log.MultiLogger(log.NewStdLogger(os.Stdout), syslog.NewLogger())
// Levels
log.Debug(logger).Log("msg", "foo bar")
log.Info(logger).Log("msg", "foo bar")
log.Warn(logger).Log("msg", "foo bar")
log.Error(logger).Log("msg", "foo bar")
errLogger := log.Error(logger)
errLogger.Log("msg", "xxx")
errLogger.Log("msg", "yyy")
errLogger.Log("msg", "zzz")
errLogger.Log(
"http.scheme", "https",
"http.host", "translate.googleapis.com",
"http.target", "/language/translate",
"http.method", "post",
"http.status_code", 500,
"http.flavor", "1.1.",
"http.user_agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36",
logger = log.With(logger,
"service.name", "hellworld",
"service.version", "v1.0.0",
"ts", log.DefaultTimestamp,
"caller", log.DefaultCaller,
)
// Helper
logger := log.NewHelper("github.com/project/foo", log.DefaultLogger)
logger.Info("hello")
logger.Infof("foo %s", "bar")
logger.Infow("key", "value")
logger.Log(log.LevelInfo, "key", "value")
// Verbose
v := NewVerbose(log.DefaultLogger, 20)
v.V(10).Log("foo", "bar1")
v.V(20).Log("foo", "bar2")
v.V(30).Log("foo", "bar3")
Helper helper = log.NewHelper(logger)
helper.Log(log.LevelInfo, "key", "value")
helper.Info("info message")
helper.Infof("info %s", "message")
helper.Infow("key", "value")
```
+24 -26
View File
@@ -6,79 +6,77 @@ import (
// Helper is a logger helper.
type Helper struct {
debug Logger
info Logger
warn Logger
err Logger
logger Logger
}
// NewHelper new a logger helper.
func NewHelper(name string, logger Logger) *Helper {
logger = With(logger, "module", name)
func NewHelper(logger Logger) *Helper {
return &Helper{
debug: Debug(logger),
info: Info(logger),
warn: Warn(logger),
err: Error(logger),
logger: logger,
}
}
// Log .
func (h *Helper) Log(level Level, keyvals ...interface{}) {
h.logger.Log(level, keyvals...)
}
// Debug logs a message at debug level.
func (h *Helper) Debug(a ...interface{}) {
h.debug.Log("msg", fmt.Sprint(a...))
h.logger.Log(LevelDebug, "msg", fmt.Sprint(a...))
}
// Debugf logs a message at debug level.
func (h *Helper) Debugf(format string, a ...interface{}) {
h.debug.Log("msg", fmt.Sprintf(format, a...))
h.logger.Log(LevelDebug, "msg", fmt.Sprintf(format, a...))
}
// Debugw logs a message at debug level.
func (h *Helper) Debugw(kv ...interface{}) {
h.debug.Log(kv...)
func (h *Helper) Debugw(keyvals ...interface{}) {
h.logger.Log(LevelDebug, keyvals...)
}
// Info logs a message at info level.
func (h *Helper) Info(a ...interface{}) {
h.info.Log("msg", fmt.Sprint(a...))
h.logger.Log(LevelInfo, "msg", fmt.Sprint(a...))
}
// Infof logs a message at info level.
func (h *Helper) Infof(format string, a ...interface{}) {
h.info.Log("msg", fmt.Sprintf(format, a...))
h.logger.Log(LevelInfo, "msg", fmt.Sprintf(format, a...))
}
// Infow logs a message at info level.
func (h *Helper) Infow(kv ...interface{}) {
h.info.Log(kv...)
func (h *Helper) Infow(keyvals ...interface{}) {
h.logger.Log(LevelInfo, keyvals...)
}
// Warn logs a message at warn level.
func (h *Helper) Warn(a ...interface{}) {
h.warn.Log("msg", fmt.Sprint(a...))
h.logger.Log(LevelWarn, "msg", fmt.Sprint(a...))
}
// Warnf logs a message at warnf level.
func (h *Helper) Warnf(format string, a ...interface{}) {
h.warn.Log("msg", fmt.Sprintf(format, a...))
h.logger.Log(LevelWarn, "msg", fmt.Sprintf(format, a...))
}
// Warnw logs a message at warnf level.
func (h *Helper) Warnw(kv ...interface{}) {
h.warn.Log(kv...)
func (h *Helper) Warnw(keyvals ...interface{}) {
h.logger.Log(LevelWarn, keyvals...)
}
// Error logs a message at error level.
func (h *Helper) Error(a ...interface{}) {
h.err.Log("msg", fmt.Sprint(a...))
h.logger.Log(LevelError, "msg", fmt.Sprint(a...))
}
// Errorf logs a message at error level.
func (h *Helper) Errorf(format string, a ...interface{}) {
h.err.Log("msg", fmt.Sprintf(format, a...))
h.logger.Log(LevelError, "msg", fmt.Sprintf(format, a...))
}
// Errorw logs a message at error level.
func (h *Helper) Errorw(kv ...interface{}) {
h.err.Log(kv...)
func (h *Helper) Errorw(keyvals ...interface{}) {
h.logger.Log(LevelError, keyvals...)
}
+7 -6
View File
@@ -6,16 +6,17 @@ import (
)
func TestHelper(t *testing.T) {
logger := With(DefaultLogger, "caller", DefaultCaller, "ts", DefaultTimestamp)
log := NewHelper("test", logger)
logger := With(DefaultLogger, "ts", DefaultTimestamp, "caller", DefaultCaller)
log := NewHelper(logger)
log.Log(LevelDebug, "msg", "test debug")
log.Debug("test debug")
log.Debugf("test %s", "debug")
log.Debugw("log", "test debug")
}
func TestHelperLevel(t *testing.T) {
log := NewHelper("test", DefaultLogger)
log := NewHelper(DefaultLogger)
log.Debug("test debug")
log.Info("test info")
log.Warn("test warn")
@@ -23,21 +24,21 @@ func TestHelperLevel(t *testing.T) {
}
func BenchmarkHelperPrint(b *testing.B) {
log := NewHelper("test", NewStdLogger(ioutil.Discard))
log := NewHelper(NewStdLogger(ioutil.Discard))
for i := 0; i < b.N; i++ {
log.Debug("test")
}
}
func BenchmarkHelperPrintf(b *testing.B) {
log := NewHelper("test", NewStdLogger(ioutil.Discard))
log := NewHelper(NewStdLogger(ioutil.Discard))
for i := 0; i < b.N; i++ {
log.Debugf("%s", "test")
}
}
func BenchmarkHelperPrintw(b *testing.B) {
log := NewHelper("test", NewStdLogger(ioutil.Discard))
log := NewHelper(NewStdLogger(ioutil.Discard))
for i := 0; i < b.N; i++ {
log.Debugw("key", "value")
}
+5 -25
View File
@@ -11,7 +11,7 @@ var (
// Logger is a logger interface.
type Logger interface {
Log(kv ...interface{}) error
Log(level Level, keyvals ...interface{}) error
}
type context struct {
@@ -20,15 +20,15 @@ type context struct {
hasValuer bool
}
func (c *context) Log(kv ...interface{}) error {
kvs := make([]interface{}, 0, len(c.prefix)+len(kv))
func (c *context) Log(level Level, keyvals ...interface{}) error {
kvs := make([]interface{}, 0, len(c.prefix)+len(keyvals))
kvs = append(kvs, c.prefix...)
if c.hasValuer {
bindValues(kvs)
}
kvs = append(kvs, kv...)
kvs = append(kvs, keyvals...)
for _, l := range c.logs {
if err := l.Log(kvs...); err != nil {
if err := l.Log(level, kvs...); err != nil {
return err
}
}
@@ -54,23 +54,3 @@ func With(l Logger, kv ...interface{}) Logger {
func MultiLogger(logs ...Logger) Logger {
return &context{logs: logs}
}
// Debug returns a debug logger.
func Debug(log Logger) Logger {
return With(log, LevelKey, LevelDebug)
}
// Info returns a info logger.
func Info(log Logger) Logger {
return With(log, LevelKey, LevelInfo)
}
// Warn return a warn logger.
func Warn(log Logger) Logger {
return With(log, LevelKey, LevelWarn)
}
// Error returns a error logger.
func Error(log Logger) Logger {
return With(log, LevelKey, LevelError)
}
+4 -15
View File
@@ -5,27 +5,16 @@ import (
"testing"
)
func TestLogger(t *testing.T) {
logger := DefaultLogger
Debug(logger).Log("msg", "test debug")
Info(logger).Log("msg", "test info")
Warn(logger).Log("msg", "test warn")
Error(logger).Log("msg", "test error")
}
func TestInfo(t *testing.T) {
logger := DefaultLogger
logger = With(logger, "caller", DefaultCaller, "ts", DefaultTimestamp)
infoLogger := Info(logger)
infoLogger.Log("key1", "value1")
infoLogger.Log("key2", "value2")
infoLogger.Log("key3", "value3")
logger = With(logger, "ts", DefaultTimestamp, "caller", DefaultCaller)
logger.Log(LevelInfo, "key1", "value1")
}
func TestWrapper(t *testing.T) {
out := NewStdLogger(os.Stdout)
err := NewStdLogger(os.Stderr)
l := With(MultiLogger(out, err), "caller", DefaultCaller, "ts", DefaultTimestamp)
l.Log("msg", "test")
l := With(MultiLogger(out, err), "ts", DefaultTimestamp, "caller", DefaultCaller)
l.Log(LevelInfo, "msg", "test")
}
+8 -7
View File
@@ -15,7 +15,7 @@ type stdLogger struct {
pool *sync.Pool
}
// NewStdLogger new a std logger with options.
// NewStdLogger new a logger with writer.
func NewStdLogger(w io.Writer) Logger {
return &stdLogger{
log: log.New(w, "", 0),
@@ -28,16 +28,17 @@ func NewStdLogger(w io.Writer) Logger {
}
// Log print the kv pairs log.
func (l *stdLogger) Log(kv ...interface{}) error {
if len(kv) == 0 {
func (l *stdLogger) Log(level Level, keyvals ...interface{}) error {
if len(keyvals) == 0 {
return nil
}
if len(kv)%2 != 0 {
kv = append(kv, "")
if len(keyvals)%2 != 0 {
keyvals = append(keyvals, "")
}
buf := l.pool.Get().(*bytes.Buffer)
for i := 0; i < len(kv); i += 2 {
fmt.Fprintf(buf, "%s=%v ", kv[i], kv[i+1])
buf.WriteString(level.String())
for i := 0; i < len(keyvals); i += 2 {
fmt.Fprintf(buf, " %s=%v", keyvals[i], keyvals[i+1])
}
l.log.Output(4, buf.String())
buf.Reset()
+4 -4
View File
@@ -6,8 +6,8 @@ func TestStdLogger(t *testing.T) {
logger := DefaultLogger
logger = With(logger, "caller", DefaultCaller, "ts", DefaultTimestamp)
Debug(logger).Log("msg", "test debug")
Info(logger).Log("msg", "test info")
Warn(logger).Log("msg", "test warn")
Error(logger).Log("msg", "test error")
logger.Log(LevelInfo, "msg", "test debug")
logger.Log(LevelInfo, "msg", "test info")
logger.Log(LevelInfo, "msg", "test warn")
logger.Log(LevelInfo, "msg", "test error")
}
+2 -7
View File
@@ -4,11 +4,6 @@ import "testing"
func TestValue(t *testing.T) {
logger := DefaultLogger
logger = With(logger, "caller", DefaultCaller, "ts", DefaultTimestamp)
logger.Log("msg", "helloworld")
Debug(logger).Log("msg", "debug value")
Info(logger).Log("msg", "info value")
Warn(logger).Log("msg", "warn value")
Error(logger).Log("msg", "error value")
logger = With(logger, "ts", DefaultTimestamp, "caller", DefaultCaller)
logger.Log(LevelInfo, "msg", "helloworld")
}
-31
View File
@@ -1,31 +0,0 @@
package log
// Verbose is a verbose type that implements Logger Print.
type Verbose struct {
log Logger
level Level
enabled bool
}
// NewVerbose new a verbose with level.
func NewVerbose(log Logger, level Level) Verbose {
return Verbose{log: log, level: level}
}
// Enabled will return true if this log level is enabled, guarded by the value of v.
func (v Verbose) Enabled(level Level) bool {
return v.level <= level
}
// V reports whether verbosity at the call site is at least the requested level.
func (v Verbose) V(level Level) Verbose {
return Verbose{log: v.log, enabled: v.Enabled(level)}
}
// Log is equivalent to the Print function, guarded by the value of v.
func (v Verbose) Log(a ...interface{}) error {
if v.enabled {
return v.log.Log(a...)
}
return nil
}
-12
View File
@@ -1,12 +0,0 @@
package log
import "testing"
func TestVerbose(t *testing.T) {
logger := With(DefaultLogger, "caller", DefaultCaller, "ts", DefaultTimestamp)
v := NewVerbose(logger, 20)
v.V(10).Log("foo", "bar1")
v.V(20).Log("foo", "bar2")
v.V(30).Log("foo", "bar3")
}