首页 > 解决方案 > 日志repo的抽象实现

问题描述

我想要wrap / abstract日志 API(隐藏实现)并使用一些库我们想要隐藏实现的原因是我们想要提供我们的日志 API 并隐藏在 hod 下使用的记录器库,现在[logrus][1]它可以当zap klog我切换到不同的记录器实现时,使用日志 api 的人也不需要更改他的日志代码使用情况(我们只是在更改引擎......)

我所做的是创建结构并init logger返回我的结构,此外还创建包装功能的函数(见下文),

package logger

import (
   "fmt"
   "os"

   "github.com/sirupsen/logrus"
)

const (
   AppLogLevel = "APP_LOG"
   defLevel    = "error"
)

type Logger struct {
   label      string
   version    string
   loggerImpl *logrus.Logger
}

// init logger
func NewLogger(level string,version string) *Logger {

   lvl := logLevel(level)
   logger := &logrus.Logger{
      Out:       os.Stdout,
      Level:     lvl,
      Formatter: &logrus.TextFormatter{},
   }
   return &Logger{
      version: version,
      loggerImpl: logger,
   }
}

// GetLogLevel - Get level from env
func getLogLevel() string {
   lvl, _ := os.LookupEnv(AppLogLevel)
   if lvl != "" {
      return lvl
   }
   return defLevel
}

func logLevel(lvl string) logrus.Level {

   switch lvl {
   case "debug":
      return logrus.DebugLevel
   case "info":
      return logrus.InfoLevel
   case "error":
      return logrus.ErrorLevel
   case "warn":
      return logrus.WarnLevel
   case "fatal":
      return logrus.FatalLevel
   case "panic":
      return logrus.PanicLevel
   default:
      panic(fmt.Sprintf("the specified %s log level is not supported", lvl))
   }
}

func (logger *Logger) SetLevel(level string) {
   lvl := logLevel(level)
   logger.loggerImpl.SetLevel(lvl)
}



func (logger *Logger) Debugf(format string, args ...interface{}) {
   logger.loggerImpl.Debugf(format, args...)
}
func (logger *Logger) Infof(format string, args ...interface{}) {
   logger.loggerImpl.Infof(format, args...)
}


func (logger *Logger) Errorf(format string, args ...interface{}) {
   logger.loggerImpl.Errorf(format, args...)
}

func (logger *Logger) Fatalf(format string, args ...interface{}) {
   logger.loggerImpl.Fatalf(format, args...)
}

func (logger *Logger) Panicf(format string, args ...interface{}) {
   logger.loggerImpl.Panicf(format, args...)
}

func (logger *Logger) Debug(args ...interface{}) {
   logger.loggerImpl.Debug(args...)
}

func (logger *Logger) Info(args ...interface{}) {
   logger.loggerImpl.Info(args...)
}

func (logger *Logger) Warn(args ...interface{}) {
   logger.loggerImpl.Warn(args...)
}

func (logger *Logger) Error(args ...interface{}) {
   logger.loggerImpl.Error(args...)
}

func (logger *Logger) Fatal(args ...interface{}) {
   logger.loggerImpl.Fatal(args...)
}

func (logger *Logger) Panic(args ...interface{}) {
   logger.loggerImpl.Panic(args...)
}

...

我错过了什么吗?当我尝试将其更改为 zap 时(将结构更改为以下内容:

type Logger struct {
    label      string
    version    string
    loggerImpl *zap.Logger
}

此代码不起作用(适用于 logrus 的所有功能代码)

logger.loggerImpl.SetLevel(lvl)

并且

logger.loggerImpl.Tracef(format, args...)

等等,因为zaplib没有它们,知道如何抽象它可以在未来支持两者或更多吗?

更新

我尝试使用以下(适配器模式):(但看起来在我现在有递归调用的方法内部)知道如何避免它吗?

package logger

import (
    log "github.com/sirupsen/logrus"
)

type Logger struct {
    adapter Adapter
}

func (l *Logger) SetLogger(a Adapter) {
    l.adapter = a
}

func (l *Logger) Debugf(fmt string, args ...interface{}) {
    l.adapter.Debugf(fmt, args...)
}

type Adapter interface {
    SetLevel(level string)
    Tracef(format string, args ...interface{})
    Debugf(string, ...interface{})
    Infof(format string, args ...interface{})
    Warnf(format string, args ...interface{})
    Errorf(format string, args ...interface{})
    Fatalf(format string, args ...interface{})
    Panicf(format string, args ...interface{})
    Trace(args ...interface{})
    Debug(args ...interface{})
    Info(args ...interface{})
    Warn(args ...interface{})
    Error(args ...interface{})
    Fatal(args ...interface{})
}

type StdLoggerAdapter struct {
}

func (l StdLoggerAdapter) SetLevel(level string) {
    lvl := logLevel(level)
    l.SetLevel(string(lvl))
}

func (l StdLoggerAdapter) Tracef(format string, args ...interface{}) {
    l.Tracef(format, args...)
}

func (l StdLoggerAdapter) Infof(format string, args ...interface{}) {
    l.Infof(format,args)
}

func (l StdLoggerAdapter) Warnf(format string, args ...interface{}) {
    l.Warnf(format,args)
}

...
func (l StdLoggerAdapter) Debugf(fmt string, args ...interface{}) {
    log.Printf(fmt, args...)
}

func NewLogger(a Adapter) Logger {
    return Logger{adapter: a}
}

func main() {
    logger := NewLogger(StdLoggerAdapter{})
    logger.Debugf("stdlib logger debug msg")
}

func logLevel(lvl string) log.Level {
    var level log.Level
    switch lvl {
    //case "trace":
    //  level = log.TraceLevel
    case "debug":
        level = log.DebugLevel
    case "info":
        level = log.InfoLevel
    case "warn":
        level = log.WarnLevel
    case "error":
        level = log.ErrorLevel
    case "fatal":
        level = log.FatalLevel
    case "panic":
        level = log.PanicLevel
    default:
        level = log.ErrorLevel
    }
    return level
}

标签: gologging

解决方案


建议以不同的方式思考问题,并完全避免类型/界面的恶作剧。使用您喜欢的 API 创建一个无状态记录器,并且在不同的记录器中实现该 API 是合理的。如果要更新底层的logger,就更新吧,用户只需要更新自己的依赖就可以看到了。如果要保持分离,可以通过导入路径隔离记录器后端。

https://gitlab.com/tight5/kit/blob/master/logger/logger.go遵循这一点,可能会激发您自己的目的。它主要基于 go-kit 的记录器,我们喜欢从它开始,并希望能够根据需要将其放入仪器中。它还重定向了我们重视的 stdlib 日志包,因此用户不必将所有现有的日志语句更新到我们的库中。

package main
import (
    "gitlab.com/tight5/kit/logger"
    "log"
)

func main() {
    logger.Info().Log("msg", "this is a logging example")
    log.Println("also logs through kit logger")

}

https://play.golang.org/p/6sBfI85Yx6g

go-kits 日志接口功能强大,我会在任何家庭发展之前对其进行评估:https ://github.com/go-kit/kit/blob/master/log/log.go只是,并且现在func Log(keyvals ...interface{})支持zap和后端。logrus

https://github.com/go-kit/kit/tree/master/log/logrus
https://github.com/go-kit/kit/tree/master/log/zap

例如,在链接的包级记录器中,为用户更改后端就像更改默认值一样简单:https ://gitlab.com/tight5/kit/blob/master/logger/logger.go#L42-53 (我们在抽象中将其称为格式,但您实际上只是在选择记录器实现)

这是上面的摘录,显示了 logrus 的示例实现(zap 将非常相似)。

... other imports ... 
import kitlogrus "github.com/go-kit/kit/log/logrus"
import "github.com/sirupsen/logrus"
func Init(format LogFormat, logLevel LogLevel) {
    var l log.Logger
    switch format {
    case FormatJson:
        l = log.NewJSONLogger(os.Stdout)
    case FormatLogfmt:
        l = log.NewLogfmtLogger(os.Stdout)
    case FormatNop:
        l = log.NewNopLogger()
    case FormatLogrus:
        l = kitlogrus.NewLogrusLogger(logrus.New())
    case FormatZap:
    default:
        panic(fmt.Errorf("invalid log format: %v", format))
    }
    ...
}

希望这会激发您自己实施的想法!


推荐阅读