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))