4 回答
TA贡献1808条经验 获得超4个赞
建议以不同的方式思考问题并完全避免类型/接口的恶作剧。使用您喜欢的 API 创建无状态记录器,并且在不同记录器之间实现感觉合理。如果要更新底层记录器,就更新它,用户只需要更新其依赖项就可以看到它。如果您想保持分离,可以通过导入路径隔离记录器后端。
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-kit日志界面非常强大,我会在任何家庭成长之前对其进行评估: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))
}
...
}
希望这能激发您自己实施的想法!
TA贡献1946条经验 获得超4个赞
我创建了这个存储库供个人使用,我认为可以对其进行改进以满足您的目的。
你可以看看。
PS:在添加新的记录器(如(zerolog))时,您可以根据需要更改变量的值logger
并更改方法(Info(args ...)
等)。Debug(args ...)
TA贡献1880条经验 获得超4个赞
老实说,我有点困惑你想要做什么。
一般来说,回答你问题的核心:
为了能够在代码中的不同日志记录库之间切换,您必须定义一个特定的接口,然后为每个库实现它。由于您无法在另一个包中的结构上实现方法,因此您必须包装其他库并在包装器上定义方法。
您的示例代码将“级别”作为记录器的属性;我猜你希望你的记录器决定你想要什么级别的日志记录,并且只使用库记录器作为泵送消息的管道。
因此,我们假设一个简化版本:
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)
}
}
现在您可以将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.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`
}
现在,您拥有了像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))
话虽如此,我认为这整个努力是徒劳的,而且不值得。由于这些库似乎不提供兼容的接口,并且使用一个库而不是另一个库的全部目的是因为您喜欢另一个库为您提供的不同接口。
TA贡献1797条经验 获得超6个赞
您可以使用适配器:
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")
}
- 4 回答
- 0 关注
- 130 浏览
添加回答
举报