猿问

日志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,

   }

}

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


type Logger struct {

    label      string

    version    string

    loggerImpl *zap.Logger

}

此代码不起作用(适用于 logrus 的所有函数代码)


logger.loggerImpl.SetLevel(lvl)


并且


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


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


慕慕森
浏览 131回答 4
4回答

炎炎设计

建议以不同的方式思考问题并完全避免类型/接口的恶作剧。使用您喜欢的 API 创建无状态记录器,并且在不同记录器之间实现感觉合理。如果要更新底层记录器,就更新它,用户只需要更新其依赖项就可以看到它。如果您想保持分离,可以通过导入路径隔离记录器后端。https://gitlab.com/tight5/kit/blob/master/logger/logger.go遵循这一点,可能会激发您自己的目的。它主要基于 go-kit 的记录器,我们一开始就喜欢它,并且希望能够根据仪器的需要将其放入其中。它还重定向我们重视的 stdlib 日志包,因此用户不必将所有现有日志语句更新到我们的库。package mainimport (    "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/6sBfI85Yx6ggo-kit日志界面非常强大,我会在任何家庭成长之前对其进行评估:https://github.com/go-kit/kit/blob/master/log/log.go只是func Log(keyvals ...interface{}),并且现在支持zap和后端。logrushttps://github.com/go-kit/kit/tree/master/log/logrushttps://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))    }    ...}希望这能激发您自己实施的想法!

绝地无双

我创建了这个存储库供个人使用,我认为可以对其进行改进以满足您的目的。你可以看看。PS:在添加新的记录器(如(zerolog))时,您可以根据需要更改变量的值logger并更改方法(Info(args ...)等)。Debug(args ...)

慕村225694

老实说,我有点困惑你想要做什么。一般来说,回答你问题的核心:为了能够在代码中的不同日志记录库之间切换,您必须定义一个特定的接口,然后为每个库实现它。由于您无法在另一个包中的结构上实现方法,因此您必须包装其他库并在包装器上定义方法。您的示例代码将“级别”作为记录器的属性;我猜你希望你的记录器决定你想要什么级别的日志记录,并且只使用库记录器作为泵送消息的管道。因此,我们假设一个简化版本:type LogLevel intconst (    LevelInfo LogLevel = iota    LevelDebug    LevelWarn    LevelError    LevelFatal)type interface ILogger {    Log(args ...interface{})}type struct Logger {    Level LogLevel    internal ILogger}这将是其他一切的基础。有一个问题值得暂停:不同的记录器是否提供兼容的接口?如果您在“zap”和“logrus”之间切换,是因为您可能实际上想使用它们的特定接口吗?也许它们提供了一些您真正想要的更专业的功能。如果您将它们隐藏在像ILogger这里这样的通用界面后面,您将失去这些记录器在实际记录方面提供的任何好处。无论如何,我们将继续,忽略这个问题,让我们看看如何使用这些原语:func NewLogger(internal ILogger) *Logger {    return &Logger{        Level: LeveLInfo,        internal: internal,    }}func (logger *Logger) Log(level LogLevel, args ...interface{}) {    if level >= logger.Level {        logger.internal.Log(args...)    }}func (logger *Logger) Logf(level LogLevel, fmt string, args ...interface{}) {    if level >= logger.Level {        msg := fmt.Sprintf(fmt, args...)        logger.internal.Log(msg)    }}  现在您可以将InfoandInfof和Debug..Debugf等作为简单便捷的方法来实现。这是一个例子。其余的将留给读者作为练习。func (logger *Logger) Infof(format string, args ...interface{}) {    logger.Logf(LevelInfo, format, args...)}func (logger *Logger) Info(args ...interface{}) {    logger.Log(LevelInfo, args...)}现在最困难的部分是:强制所有第三方库符合您的界面。我不熟悉所有不同的日志库,所以这可能证明不是那么简单。您可能必须更改界面的设计ILogger以使其更加可行。无论如何,您通常会这样做:type ZapLogger *zap.Loggerfunc (z ZapLogger) Log(args ...interface{}) {    zz := *zap.Logger(z)    // TODO: do something to pump `args` into `zz`}type LogrusLogger *logrus.Loggerfunc (g LogrusLogger) Log(args ...interface{}) {    gg := *logrus.Logger(g)    // TODO: do something to pump `args` into `gg`}现在,您拥有了像LogrusLoggerand这样的类型ZapLogger,它们实现了ILogger并且可以轻松地与底层日志记录库来回转换,无需任何成本。因此,您可以实例化自己的记录器来包装底层的第 3 方记录器var underlying *logrus.Logger = MakeMyLogrusLogger(...)var myLogger = NewLogger(LogrusLogger(underlying))现在每个人都可以打电话myLogger.Infof(....)来记录东西。如果您决定切换到zap或其他什么,您将更改上面的行(并且您还必须为 zap 定义 ILogger 接口的实现)var underlying *zap.Logger = MakeMyZapLogger(...)var myLogger = NewLogger(ZapLogger(underlying))话虽如此,我认为这整个努力是徒劳的,而且不值得。由于这些库似乎不提供兼容的接口,并且使用一个库而不是另一个库的全部目的是因为您喜欢另一个库为您提供的不同接口。

FFIVE

您可以使用适配器:package mainimport ( "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 {   Debugf(string,...interface{})}type StdLoggerAdapter struct {}func (l StdLoggerAdapter) Debugf(fmt string,args...interface{}) {   log.Printf(fmt,args...)}type LogrusAdapter struct {}func (l LogrusAdapter) Debugf(fmt string,args...interface{}) {   logrus.Debugf(fmt,args...)}func NewLogger(a Adapter) Logger {   return Logger{adapter:a}}func main() {    logger:=NewLogger(StdLoggerAdapter{})    logger.Debugf("stdlib logger debug msg")    logger.SetLogger(LogrusAdapter{})    logger.Debugf("logrus debug msg")}
随时随地看视频慕课网APP

相关分类

Go
我要回答