Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Go 日志repo的抽象实现_Go_Logging - Fatal编程技术网

Go 日志repo的抽象实现

Go 日志repo的抽象实现,go,logging,Go,Logging,我想wrap/abstract日志API(隐藏实现)并使用一些库 我们想要隐藏实现的原因是我们想要提供我们的日志API并隐藏hod下使用的日志库,现在它的[logrus][1],也可以是zapklog,当我切换到不同的日志实现时,使用日志API的人不需要更改his日志代码的用法(我们正在更换发动机…) 我所做的是创建struct和init记录器返回我的struct,此外还创建包装功能的函数(见下文) package logger import ( "fmt" "os" "g

我想
wrap/abstract
日志API(隐藏实现)并使用一些库 我们想要隐藏实现的原因是我们想要提供我们的日志API并隐藏hod下使用的日志库,现在它的
[logrus][1]
,也可以是
zap
klog
,当我切换到不同的日志实现时,使用日志API的人不需要更改his日志代码的用法(我们正在更换发动机…)

我所做的是创建struct和
init记录器
返回我的struct,此外还创建包装功能的函数(见下文)

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(格式,参数…)

等等,既然lib没有它们,你知道如何抽象它,以便在将来支持这两种或更多种功能吗

更新

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

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
}

通过创建接口抽象出所需的方法或常用方法,并实现如下接口:

type Logger interface {
    SetLevel(level string)
    Errorf(format string, args ...interface{})
}

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

type zapLogger struct {
    label      string
    version    string
    loggerImpl *logrus.Logger
}
根据要求初始化日志:

Logger log := new LogrusLogger{}

将其用作:

log.Errorf("message")

建议以不同的方式思考问题,并完全避免类型/接口骗局。使用您喜欢的API创建一个无状态记录器,并在不同记录器之间实现该API。如果要更新基础记录器,请更新它,用户只需更新其依赖项即可查看它。如果要维护在分离中,可以通过导入路径隔离记录器后端

遵循这一点,可能会激发您自己的目的。它主要基于go kit的logger,我们喜欢从它开始,并希望能够根据需要插入它以进行检测。它还重定向stdlib日志包,我们重视它,因此用户不必将所有现有的日志语句更新到我们的库中

主程序包
进口(
“gitlab.com/tight5/kit/logger”
“日志”
)
func main(){
logger.Info().Log(“msg”,“这是一个日志示例”)
Println(“也通过工具包记录器记录”)
}

go-kit
s日志界面功能强大,在任何家庭成长之前,我都会对其进行评估:它只是
func-log(keyvals…interface{})
,现在支持
zap
logrus
后端


例如,在链接包级记录器中,更改用户的后端与更改中的默认值一样简单:(我们在抽象中称之为格式,但实际上您只是选择记录器实现)

下面是上面的一个摘录,展示了logrus的一个示例实现(zap非常类似)


希望这能为您自己的实现激发灵感!

您可以使用适配器:

package main

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 {
   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")
}

我创建了这个存储库供个人使用,我认为它可以改进以满足您的需要

你可以看看

PS:在添加新的记录器(zerolog)时,您可以根据需要更改变量
logger
的值,并更改方法(
Info(args…
Debug(args…
等)


老实说,我对你想做的事有点困惑

一般来说,要回答您问题的核心内容:

若要在代码中的不同日志库之间切换,必须定义一个特定的接口,然后为每个库实现它。由于无法在另一个包的结构上实现方法,因此必须包装其他库,并在包装上定义方法

您的示例代码将“level”作为日志记录器的属性;我猜您希望日志记录器决定您想要的日志记录级别,并将库日志记录器用作传输消息的管道

那么,让我们假设一个简化的版本:

type LogLevel int

const (
    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)
    }
}  
现在您可以实现
Info
Infof
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.Logger

func (z ZapLogger) Log(args ...interface{}) {
    zz := *zap.Logger(z)
    // TODO: do something to pump `args` into `zz`
}

type LogrusLogger *logrus.Logger

func (g LogrusLogger) Log(args ...interface{}) {
    gg := *logrus.Logger(g)
    // TODO: do something to pump `args` into `gg`
}
现在,您有了像
LogrusLogger
ZapLogger
这样的类型,它们实现了
ILogger
,并且可以轻松地使用底层日志库来回转换,而无需任何成本

因此,您可以实例化自己的记录器来包装底层的第三方记录器

var underlying *logrus.Logger = MakeMyLogrusLogger(...)
var myLogger = NewLogger(LogrusLogger(underlying))
现在每个人都可以调用
myLogger.Infof(..)
来记录东西

如果您决定切换到
zap
或其他任何方式,您将更改上面的行(并且您还必须为zap定义ILogger接口的实现)


说了这么多,我觉得整个
type ZapLogger *zap.Logger

func (z ZapLogger) Log(args ...interface{}) {
    zz := *zap.Logger(z)
    // TODO: do something to pump `args` into `zz`
}

type LogrusLogger *logrus.Logger

func (g LogrusLogger) Log(args ...interface{}) {
    gg := *logrus.Logger(g)
    // TODO: do something to pump `args` into `gg`
}
var underlying *logrus.Logger = MakeMyLogrusLogger(...)
var myLogger = NewLogger(LogrusLogger(underlying))
var underlying *zap.Logger = MakeMyZapLogger(...)
var myLogger = NewLogger(ZapLogger(underlying))