Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: use zap instead of logrus #31

Merged
merged 9 commits into from
Oct 18, 2024
Merged

Conversation

mutezebra
Copy link
Member

使用zap替代logrus,维护依赖的统一性,减少依赖的复杂性,增强性能(zap强于logrus4倍)
依赖关系
Refs #19

@SchwarzSail SchwarzSail requested a review from ozline October 15, 2024 02:37
@@ -67,7 +70,7 @@ func Init() {
// log
EsInit()
klog.SetLevel(klog.LevelDebug)
klog.SetLogger(kitexlogrus.NewLogger(kitexlogrus.WithHook(EsHookLog())))
klog.SetLogger(kitexzap.NewLogger(EsHookLog()...))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这边 setlogger 后,我们后续是否直接用 klog 就可以记录日志并尝试推送到 es 服务器了?

pkg 里还有一个 logger 的封装,这个 logger 封装是否是多余的?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

并不多余,因为直接使用zap的logger是无法实现kitex中Logger接口的。
kitexzap.NewLogger()中对zap.Logger进行了封装,增加了一些方法来实现klog定义的Logger接口。
那我觉得我们专注于添加Hook和自定义输出格式以及路径即可,没有必要再专门去实现他缺少的方法。因为还蛮复杂的。

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

那后续就直接使用 klog 就可以了是这样吗?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

是这样的,等明天考完试我会构建一遍项目验证一下。

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
显然,是成功的。

Copy link
Member Author

@mutezebra mutezebra Oct 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

任何使用klog输出的日志都会触发ES的hook

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我觉得可以把logger替换成klog了

@mutezebra
Copy link
Member Author

mutezebra commented Oct 16, 2024

嗯,现在已经重构了Logger,并且把kitexzap也集成进了logger包里,暴露了两个函数出来。
一个 DefaultLogger(options ...zap.Option) *kitexzap.Logger 用来简便的获得与之前相同配置的zapLogger
一个 NewLogger(lvl zapcore.Level, cfg Config, options ...zap.Option) *kitexzap.Logger 用来实现需要自定义参数的zapLogger,比如我们现在一直使用的是 os.Stdout 后面有需要的话可以通过这个函数将Output替换为某个文件,或者是其他的符合接口定义的输出位置

另外,还对以前的实现保留了兼容性,现在你既可以使用klog.Log() 也可以使用 logger.Log()
或者我们把以前的引用包直接一个个更改为klog也可以。

@SchwarzSail
Copy link
Member

可以考虑保持和以前一样的开箱即用,不需要在其他地方显式初始化👍

@mutezebra
Copy link
Member Author

可以考虑保持和以前一样的开箱即用,不需要在其他地方显式初始化👍

我们可以使用init()吗?

@mutezebra mutezebra closed this Oct 16, 2024
@mutezebra mutezebra reopened this Oct 16, 2024
@ozline
Copy link
Member

ozline commented Oct 16, 2024

可以考虑保持和以前一样的开箱即用,不需要在其他地方显式初始化👍

我们可以使用init()吗?

这个init似乎会在这个包第一次用到的时候初始化,但我不确定是否会导致其他地方引用的时候出现没有初始化的问题

init肯定没有问题,就应该做到这样的无感,然后在服务端入口直接调用就好

@ozline
Copy link
Member

ozline commented Oct 16, 2024

嗯,现在已经重构了Logger,并且把kitexzap也集成进了logger包里,暴露了两个函数出来。

一个 DefaultLogger(options ...zap.Option) *kitexzap.Logger 用来简便的获得与之前相同配置的zapLogger

一个 NewLogger(lvl zapcore.Level, cfg Config, options ...zap.Option) *kitexzap.Logger 用来实现需要自定义参数的zapLogger,比如我们现在一直使用的是 os.Stdout 后面有需要的话可以通过这个函数将Output替换为某个文件,或者是其他的符合接口定义的输出位置

另外,还对以前的实现保留了兼容性,现在你既可以使用klog.Log() 也可以使用 logger.Log()

或者我们把以前的引用包直接一个个更改为klog也可以。

直接改用klog吧,这个替换很方便,一行命令的事。私有不暴露loggerObj,后续统一用klog

@mutezebra
Copy link
Member Author

就应该做到这样的无感

我可以实现正常kitexzap的无感,也就是直接调用logger包或者使用klog(推荐logger)。
但如果想要将klog同步到Eshook的话,还是需要在一个地方显式的传入Fire方法,毕竟我们需要在Es启动之后才能获取EsClient
而且包与包之间最好不要有过高的耦合,所以我觉得还是提供基础zap的无感,需要Hook的业务还是要手动初始化

@mutezebra
Copy link
Member Author

嗯,现在已经重构了Logger,并且把kitexzap也集成进了logger包里,暴露了两个函数出来。
一个 DefaultLogger(options ...zap.Option) *kitexzap.Logger 用来简便的获得与之前相同配置的zapLogger
一个 NewLogger(lvl zapcore.Level, cfg Config, options ...zap.Option) *kitexzap.Logger 用来实现需要自定义参数的zapLogger,比如我们现在一直使用的是 os.Stdout 后面有需要的话可以通过这个函数将Output替换为某个文件,或者是其他的符合接口定义的输出位置
另外,还对以前的实现保留了兼容性,现在你既可以使用klog.Log() 也可以使用 logger.Log()
或者我们把以前的引用包直接一个个更改为klog也可以。

直接改用klog吧,这个替换很方便,一行命令的事。私有不暴露loggerObj,后续统一用klog

好的

@mutezebra
Copy link
Member Author

mutezebra commented Oct 16, 2024

嗯,现在已经重构了Logger,并且把kitexzap也集成进了logger包里,暴露了两个函数出来。
一个 DefaultLogger(options ...zap.Option) *kitexzap.Logger 用来简便的获得与之前相同配置的zapLogger
一个 NewLogger(lvl zapcore.Level, cfg Config, options ...zap.Option) *kitexzap.Logger 用来实现需要自定义参数的zapLogger,比如我们现在一直使用的是 os.Stdout 后面有需要的话可以通过这个函数将Output替换为某个文件,或者是其他的符合接口定义的输出位置
另外,还对以前的实现保留了兼容性,现在你既可以使用klog.Log() 也可以使用 logger.Log()
或者我们把以前的引用包直接一个个更改为klog也可以。

直接改用klog吧,这个替换很方便,一行命令的事。私有不暴露loggerObj,后续统一用klog

嗯,现在已经重构了Logger,并且把kitexzap也集成进了logger包里,暴露了两个函数出来。
一个 DefaultLogger(options ...zap.Option) *kitexzap.Logger 用来简便的获得与之前相同配置的zapLogger
一个 NewLogger(lvl zapcore.Level, cfg Config, options ...zap.Option) *kitexzap.Logger 用来实现需要自定义参数的zapLogger,比如我们现在一直使用的是 os.Stdout 后面有需要的话可以通过这个函数将Output替换为某个文件,或者是其他的符合接口定义的输出位置
另外,还对以前的实现保留了兼容性,现在你既可以使用klog.Log() 也可以使用 logger.Log()
或者我们把以前的引用包直接一个个更改为klog也可以。

直接改用klog吧,这个替换很方便,一行命令的事。私有不暴露loggerObj,后续统一用klog

直接使用klog的话,假如我们只有一个main.go ,他直接调用了 klog.Fatalf ,这个时候我们的zap并没有初始化,因为没引用包,init()没响应。如果不用init改为显示初始化,又不优雅。所以不如统一用logger.Fatalf这样由于引用了包所以一定会初始化,有其他需要的话再给klog设置带有Hook的Logger。
再比如服务不是要Init嘛,你不从logger包调用而是直接调用klog的话,那无论是哪种方法,klog都没有设置zapLogger。

我们的Logger包里已经没有loggerObj了,Logger包内就是调用的klog

@mutezebra
Copy link
Member Author

mutezebra commented Oct 16, 2024

我觉得现在有两种前提:

  1. 我们所有的日志都应该同步到es:
    那我们显然要在任何服务的main文件里显式的初始化esclient,然后把Hook丢给Logger包封装好的函数里从而设置klog的Logger,接下来我们就可以在这个进程中的任意位置使用klog。(因为我们调用Logger包封装的函数时引用了包)

  2. 不是所有的日志都需要同步到es:
    那自然会有地方不需要初始化esclient从而获取hook,那如果我们不使用Logger包而是使用klog的话,这时的klog只是kitex自带的普通klog,我们需要有一个地方来设置klog的logger为我们自定义的logger。
    不管是init()来设置logger还是像以前一样的使用once,我们如果只调用klog而不是logger包的话,klog的logger将不会被初始化。(不显式初始化的前提下)

@SchwarzSail
Copy link
Member

逃不开初始化,所以我的理解是还是调用logger包吧。或者logger暴露一个将klog集成zap的init,之后应该都可以直接使用klog了吧

@mutezebra
Copy link
Member Author

逃不开初始化,所以我的理解是还是调用logger包吧。或者logger暴露一个将klog集成zap的init,之后应该都可以直接使用klog了吧

是的,这就是我说的两种前提,一种直接使用logger包,我们用zap也可以,用集成了zap的klog也可以,在不需要额外参数(hook)的条件下都是开箱即用的。

一种就是你说的第二种方法,暴露一个显式的接口出来用于初始化klog。 然后在所有的地方调用klog

总的比起来我觉得还是前者更优,因为第一种方法实际上也是在隐式初始化之后封装klog然后暴露那些函数。
而且我认为第一种方法可以给我们更大的自由度,我们可以自定义logger,以防止以后的更多需求。事实上现在就有一个需求,kafka的日志集成需要实现了Printf的logger,而目前klog的logger是没有这个方法的,显然如果我们只调用logger包的话,可以给我们带来更大的灵活度,不管是初始化方面还是未来的扩展。

@SchwarzSail SchwarzSail requested a review from ozline October 17, 2024 00:34
@SchwarzSail
Copy link
Member

我也建议第一种,高可拓展性优先

@ozline ozline requested a review from jiuxia211 October 17, 2024 02:42
Copy link
Contributor

@jiuxia211 jiuxia211 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

作为对日志的优化我觉得没啥问题,但是作为福uu的后端,我们是否真的需要把日志上传到es?或者说这些日志信息有什么价值让我们必须要使用es吗?
因为我之前试过将日志信息上传到es,要让es中的日志信息有价值,必须要给日志做非常完善的index,我们才能从众多日志中筛选出一些信息,我现在不认为上es有什么很高的价值

@SchwarzSail
Copy link
Member

作为拓展使用,实际部署上去肯定用不了es
不过es太臃肿了,可以考虑使用tracer?

@ozline
Copy link
Member

ozline commented Oct 17, 2024

作为对日志的优化我觉得没啥问题,但是作为福uu的后端,我们是否真的需要把日志上传到es?或者说这些日志信息有什么价值让我们必须要使用es吗?

因为我之前试过将日志信息上传到es,要让es中的日志信息有价值,必须要给日志做非常完善的index,我们才能从众多日志中筛选出一些信息,我现在不认为上es有什么很高的价值

日志肯定需要一个集中存放的地方,本地提供 1-3M的临时缓存即可。这在持久运行后是一个很严重的问题,活动室的Clash容器日志半年不到有整整 27G的日志,单独记录肯定不行。

但是我非常同意不用es,这个java项目过于臃肿了,也许有更轻量化的解决办法,但是tracer和log不是一个东西,一个提供追踪支持,一个提供日志记录

实际上,拥有日志也可以让AI那边参与进行数据分析,统计高频问题和用户习惯

@ozline
Copy link
Member

ozline commented Oct 17, 2024

逃不开初始化,所以我的理解是还是调用logger包吧。或者logger暴露一个将klog集成zap的init,之后应该都可以直接使用klog了吧

是的,这就是我说的两种前提,一种直接使用logger包,我们用zap也可以,用集成了zap的klog也可以,在不需要额外参数(hook)的条件下都是开箱即用的。

一种就是你说的第二种方法,暴露一个显式的接口出来用于初始化klog。 然后在所有的地方调用klog

总的比起来我觉得还是前者更优,因为第一种方法实际上也是在隐式初始化之后封装klog然后暴露那些函数。
而且我认为第一种方法可以给我们更大的自由度,我们可以自定义logger,以防止以后的更多需求。事实上现在就有一个需求,kafka的日志集成需要实现了Printf的logger,而目前klog的logger是没有这个方法的,显然如果我们只调用logger包的话,可以给我们带来更大的灵活度,不管是初始化方面还是未来的扩展。

在程序入口不是有统一的初始化吗?比如 klog.SetLogger ,在这里完成钩子初始化就可以了吧

实际上可以考虑把这个初始化抽象到pkg里,因为基本所有模块初始化逻辑是一样的,做到keep once

kafka的日志不是应该在kafka容器中做的吗?为什么看起来是在程序中的?

@mutezebra
Copy link
Member Author

逃不开初始化,所以我的理解是还是调用logger包吧。或者logger暴露一个将klog集成zap的init,之后应该都可以直接使用klog了吧

是的,这就是我说的两种前提,一种直接使用logger包,我们用zap也可以,用集成了zap的klog也可以,在不需要额外参数(hook)的条件下都是开箱即用的。
一种就是你说的第二种方法,暴露一个显式的接口出来用于初始化klog。 然后在所有的地方调用klog
总的比起来我觉得还是前者更优,因为第一种方法实际上也是在隐式初始化之后封装klog然后暴露那些函数。
而且我认为第一种方法可以给我们更大的自由度,我们可以自定义logger,以防止以后的更多需求。事实上现在就有一个需求,kafka的日志集成需要实现了Printf的logger,而目前klog的logger是没有这个方法的,显然如果我们只调用logger包的话,可以给我们带来更大的灵活度,不管是初始化方面还是未来的扩展。

在程序入口不是有统一的初始化吗?比如 klog.SetLogger ,在这里完成钩子初始化就可以了吧

实际上可以考虑把这个初始化抽象到pkg里,因为基本所有模块初始化逻辑是一样的,做到keep once

kafka的日志不是应该在kafka容器中做的吗?为什么看起来是在程序中的?

我现在所实现的是无感的直接Logger,以及需要初始化的带有Hook的Logger。

已经把带有Hook的Logger的初始化抽象了,但现在存在包与包之间的引用,我不确定这是好是坏(无环)。

关于kafka的日志是我理解错了,他暴露的不是kafka内部的日志,而是框架返回error。而我们会处理这些error的,所以不必在意这个 :)

@mutezebra
Copy link
Member Author

给出两个使用Demo
第一种开箱即用的,集成了zap的logger

func main() {
    logger.Info("Hello World!")
}

第二种显式初始化的,带有hook的Logger

func main() {
    logger.InitLoggerWithHook(serviceName)
    logger.Info("Hello World!")
}

两种方法均已实现

@SchwarzSail
Copy link
Member

正常来说是先init logger再 init config,但是要使用hook的话要先init config.es?

@mutezebra
Copy link
Member Author

正常来说是先init logger再 init config,但是要使用hook的话要先init config.es?

是这样的。不然的话hook的es.client哪里来呢?

@SchwarzSail
Copy link
Member

可是在init config的过程中已经使用了logger来记录....

@mutezebra
Copy link
Member Author

可是在init config的过程中已经使用了logger来记录....

稍等,现在有环,我解决一下

@mutezebra
Copy link
Member Author

可是在init config的过程中已经使用了logger来记录....

但是这是无可避免的吧?你不init config的话,程序甚至不知道你的配置,那怎么给你推送到相应的位置呢?
有种做法是将除了配置初始化期间的其他一切日志同步到es

func Init() {
    config.Init(*path, serviceName)
    utils.InitLoggerWithHook(serviceName)
    dal.Init()
    tracer.Init()
    ...
}

这样的话除了init config期间的日志只输出不同步,其他日志都会同步。

@SchwarzSail
Copy link
Member

后续记录日志是直接logger.Info还是klog.Info
但是不管是哪一种,由于config包下是直接使用了logger来记录了日志信息的,这个问题怎么解决的?

@mutezebra
Copy link
Member Author

后续记录日志是直接logger.Info还是klog.Info 但是不管是哪一种,由于config包下是直接使用了logger来记录了日志信息的,这个问题怎么解决的?

直接调用logger.Info

调用logger包的时候会触发一个初始化,此时的logger只是zap,不包含hook,所以不会同步到es,只会输出到指定的output。
想要加上eshook的话,我封装好了一个util,直接调用就好了,原本的logger就会加上hook了。

总之,在config init阶段的日志不带有hook,只有在config init之后手动调用utils.InitLoggerWithHook 之后的日志才同步到es

具体代码可以看commit记录

后续记录日志是直接logger.Info还是klog.Info 但是不管是哪一种,由于config包下是直接使用了logger来记录了日志信息的,这个问题怎么解决的?

直接调用logger.Info

调用logger包的时候会触发一个初始化,此时的logger只是zap,不包含hook,所以不会同步到es,只会输出到指定的output。
想要加上eshook的话,我封装好了一个util,直接调用就好了,原本的logger就会加上hook了。

总之,在config init阶段的日志不带有hook,只有在config init之后手动调用utils.InitLoggerWithHook 之后的日志才同步到es

具体代码可以看commit记录

@SchwarzSail
Copy link
Member

utils.InitLoggerWithHook这个函数放在logger包里面会更合适一点?

@SchwarzSail SchwarzSail requested a review from jiuxia211 October 18, 2024 03:29
@mutezebra
Copy link
Member Author

utils.InitLoggerWithHook这个函数放在logger包里面会更合适一点?

两方面考虑

  1. 职能
    logger包应该专注于日志配置和输出,而想要封装一个带有eshook的函数需要依赖很多包,比如config包,比如es包。
    我希望logger能只做自己的事情,它可以接收外来参数进行logger的配置,但我觉得还是最好不要封装太多方法。
    尤其是需要引入更多依赖的时候。logger包应该专注于logger,utils这个名字听起来就是一个整合包的好地方。:)
  2. 包关系
    前面有提到说eshook的创建需要esclient,那初始化esclient的过程显然要利用config,但是config又引用了logger作为日志输
    出, 这样就会导致循环引入
    所以我引入了一个第三包,也就是项目中已经存在的utils

@SchwarzSail
Copy link
Member

嗯,那我没啥问题了,写得很好

@ozline
Copy link
Member

ozline commented Oct 18, 2024

config 的日志很明显除了hot reload 外都不需要做钩子,没什么逻辑毛病

因为你程序一启动就出问题,那不是直接 panic 了吗?

Copy link
Member

@ozline ozline left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

做一些简单修改就可以 merge 了

pkg/utils/utils.go Show resolved Hide resolved
pkg/eshook/eshook.go Outdated Show resolved Hide resolved
@ozline
Copy link
Member

ozline commented Oct 18, 2024

好像梓玄还没上班,你顺便帮忙把目前仓库现存的几个项目的初始化改了吧,当成一个大 pr 合了

@mutezebra
Copy link
Member Author

好像梓玄还没上班,你顺便帮忙把目前仓库现存的几个项目的初始化改了吧,当成一个大 pr 合了

done

@ozline ozline merged commit f900b56 into west2-online:main Oct 18, 2024
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants