Interface

Kratos contains only the simplest Log interface for business-adapted log access. When your business logic needs to use custom logs inside the kratos framework, you only need to implement the Log method simply. Kratos logs also provide some log helpful features such as valuer, helper, filter, and so on, which can be implemented directly using the framework’s built-in implementations when we need them, or by ourselves.

  1. type Logger interface {
  2. Log(level Level, keyvals ...interface{}) error
  3. }

Implementation

Implement logger

  1. // https://github.com/go-kratos/kratos/blob/main/log/std.go
  2. var _ Logger = (*stdLogger)(nil)
  3. type stdLogger struct {
  4. log *log.Logger
  5. pool *sync.Pool
  6. }
  7. // NewStdLogger new a logger with writer.
  8. func NewStdLogger(w io.Writer) Logger {
  9. return &stdLogger{
  10. log: log.New(w, "", 0),
  11. pool: &sync.Pool{
  12. New: func() interface{} {
  13. return new(bytes.Buffer)
  14. },
  15. },
  16. }
  17. }
  18. // Log print the kv pairs log.
  19. func (l *stdLogger) Log(level Level, keyvals ...interface{}) error {
  20. if len(keyvals) == 0 {
  21. return nil
  22. }
  23. if len(keyvals)%2 != 0 {
  24. keyvals = append(keyvals, "")
  25. }
  26. buf := l.pool.Get().(*bytes.Buffer)
  27. buf.WriteString(level.String())
  28. for i := 0; i < len(keyvals); i += 2 {
  29. fmt.Fprintf(buf, " %s=%v", keyvals[i], keyvals[i+1])
  30. }
  31. l.log.Output(4, buf.String())~~~~
  32. buf.Reset()
  33. l.pool.Put(buf)
  34. return nil
  35. }

Implement valuer

  1. func Timestamp(layout string) Valuer {
  2. return func(context.Context) interface{} {
  3. return time.Now().Format(layout)
  4. }
  5. }

Usage

Print log with Logger

  1. logger := log.DefaultLogger
  2. logger.Log(LevelInfo, "key1", "value1")

Print log with Helper

  1. log := log.NewHelper(DefaultLogger)
  2. log.Debug("test debug")
  3. log.Info("test info")
  4. log.Warn("test warn")
  5. log.Error("test error")

Add some default fields with Valuer

  1. logger := DefaultLogger
  2. logger = With(logger, "ts", DefaultTimestamp, "caller", DefaultCaller)
  3. logger.Log(LevelInfo, "msg", "helloworld")

Log to multiple loggers

  1. out := log.NewStdLogger(os.Stdout)
  2. err := log.NewStdLogger(os.Stderr)
  3. l := log.With(MultiLogger(out, err))
  4. l.Log(LevelInfo, "msg", "test")

Bind context to logger

  1. logger := log.With(NewStdLogger(os.Stdout),
  2. "trace", Trace(),
  3. )
  4. log := log.NewHelper(logger)
  5. ctx := context.WithValue(context.Background(), "trace_id", "2233")
  6. log.WithContext(ctx).Info("got trace!")

Log Filtering

If you need to filter some fields in the log that should not be printed in plain text, such as password, you can do so by log. NewFilter() implements filtering.

Filter by level
  1. l := log.NewHelper(log.NewFilter(log.DefaultLogger, log.FilterLevel(log.LevelWarn)))
  2. l.Log(LevelDebug, "msg1", "te1st debug")
  3. l.Debug("test debug")
  4. l.Debugf("test %s", "debug")
  5. l.Debugw("log", "test debug")
  6. l.Warn("warn log")
Filter by key
  1. l := log.NewHelper(log.NewFilter(log.DefaultLogger, log.FilterKey("password")))
  2. l.Debugw("password", "123456")
Filter by value
  1. l := log.NewHelper(log.NewFilter(log.DefaultLogger, log.FilterValue("kratos")))
  2. l.Debugw("name", "kratos")
Filter with hook function
  1. l := log.NewHelper(log.NewFilter(log.DefaultLogger, log.FilterFunc(testFilterFunc)))
  2. l.Debug("debug level")
  3. l.Infow("password", "123456")
  4. func testFilterFunc(level Level, keyvals ...interface{}) bool {
  5. if level == LevelWarn {
  6. return true
  7. }
  8. for i := 0; i < len(keyvals); i++ {
  9. if keyvals[i] == "password" {
  10. keyvals[i+1] = "***"
  11. }
  12. }
  13. return false
  14. }

Print to STDOUT

You can create standard output log objects using the StdLogger that comes with it. With the NewHelper log module, Helper generates log modules that provide different levels of log output.

  1. logger := log.NewStdLogger()
  2. log := log.NewHelper(logger)
  3. // Levels
  4. log.Info("some log")
  5. log.Infof("format %s", "some log")
  6. log.Infow("field_name", "some log")

Print to fluentd

Import fluent sdk

  1. import "github.com/go-kratos/fluent"
  2. addr := "unix:///var/run/fluent/fluent.sock"
  3. logger,err := fluent.NewLogger(addr)
  4. if err != nil {
  5. return
  6. }
  7. log := log.NewHelper(logger)
  8. // Levels
  9. log.Info("some log")
  10. log.Infof("format %s", "some log")
  11. log.Infow("field_name", "some log")

Advanced Usage

Implement Zap Logger

  1. var _ log.Logger = (*ZapLogger)(nil)
  2. type ZapLogger struct {
  3. log *zap.Logger
  4. Sync func() error
  5. }
  6. // NewZapLogger return ZapLogger
  7. func NewZapLogger(encoder zapcore.EncoderConfig, level zap.AtomicLevel, opts ...zap.Option) *ZapLogger {
  8. core := zapcore.NewCore(
  9. zapcore.NewConsoleEncoder(encoder),
  10. zapcore.NewMultiWriteSyncer(
  11. zapcore.AddSync(os.Stdout),
  12. ), level)
  13. zapLogger := zap.New(core, opts...)
  14. return &ZapLogger{log: zapLogger, Sync: zapLogger.Sync}
  15. }
  16. // Log Implementation of logger interface
  17. func (l *ZapLogger) Log(level log.Level, keyvals ...interface{}) error {
  18. if len(keyvals) == 0 || len(keyvals)%2 != 0 {
  19. l.log.Warn(fmt.Sprint("Keyvalues must appear in pairs: ", keyvals))
  20. return nil
  21. }
  22. // Zap.Field is used when keyvals pairs appear
  23. var data []zap.Field
  24. for i := 0; i < len(keyvals); i += 2 {
  25. data = append(data, zap.Any(fmt.Sprint(keyvals[i]), fmt.Sprint(keyvals[i+1])))
  26. }
  27. switch level {
  28. case log.LevelDebug:
  29. l.log.Debug("", data...)
  30. case log.LevelInfo:
  31. l.log.Info("", data...)
  32. case log.LevelWarn:
  33. l.log.Warn("", data...)
  34. case log.LevelError:
  35. l.log.Error("", data...)
  36. }
  37. return nil
  38. }