go - 日志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...)
等等,因为zap
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
}
解决方案
建议以不同的方式思考问题,并完全避免类型/界面的恶作剧。使用您喜欢的 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-kit
s 日志接口功能强大,我会在任何家庭发展之前对其进行评估: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))
}
...
}
希望这会激发您自己实施的想法!
推荐阅读
- php - PHP / Laravel - 一个具有多个多态关系的模型
- python - 如何在 Python 中导入 r 包
- facebook-graph-api - 当我的应用程序已经具有页面公共内容访问权限时如何生成正确的访问令牌
- keras - How to undestand timestamp in Keras LSTM
- java - 尝试连接配对设备时如何修复“java.io.IOException:读取失败,套接字可能关闭或超时”?
- unicode - 使用十六进制名称组合字符
- powershell - Powershell - 遍历数组
- node.js - 无法使用 Angular 7 将授权标头发送到节点 11/Express 4 服务器
- r - ggsave 给出空白的 jpeg 图像
- python - 熊猫:提高滚动窗口的速度(应用自定义功能)