Golang Go语言 日志记录

对于golang大家一般是怎么做错误日志记录的。

如果我的代码层级是controller->service->repo层,在repo层的错误一直往上抛,抛到最外层, 我的想法是用中间件errorMiddleware统一处理error并记录错误日志.

但有以下几个问题:

  1. 使用zap内置的错误zap.Error(err), err的堆栈信息如果原始err不记录,会直接丢失。
  2. 使用pkg/errors记录stack或者fmt.Errorf去记录调用/逻辑栈,但如果我在错误处记录一些现场数据,用起来相比zap.Field难用, 比如是个map结构体,里面还带指针什么,用%#v之类的也不能设置进去。
  3. 直接在errorlogger记录, 那每一层的err都会记录,导致重复logger

不知道大家有什么好的方式去处理


Golang Go语言 日志记录
20 回复
  1. 不受控的 err 无法强制要求 traceback
    2. https://github.com/davecgh/go-spew
    3. 只在最外层 logger ,中间函数均使用 fmt.Errorf

    相比成本高昂的 traceback ,我通常选择一个唯一的 ttl

更多关于Golang Go语言 日志记录的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


怎么这么 Java 味

我不确定我的做法是不是 idiomatic 只是简单描述一下我的思路。毕竟 slog 进标准库也没多久,看 api 也是受 zap 影响很大。

大的逻辑方面我比较倾向于全局只有一个唯一 logger ,中间调用过程只传递。另外我习惯把业务层面的日志和功能实现层面的日志分开,然后两者用不同的方式来处理。

业务层面的日志我倾向于用格式化的字符串:一方面它是给运维用的,需要一眼看明白在哪个环节出了问题,但并不需要知道出问题的细节;另一方面这类日志要进日志服务器做后续统计等功能,需要方便处理。

对应的就是你的描述里 controller->service->repo 这个部分,我会用 errors 传递字符串,每个环节用 errorf 来 wrap 上一个环节的错误,起到简单的 stack trace 的功能。这里一般不建议用 runtime 来获取完整的 stack trace ,主要是性能开销的问题。

包或者实现层面的日志,一般是给开发者方便调试用的,比如看到底是什么入参触发了什么样的边界条件导致出错。这个信息在开发和测试环节比较有用,实际线上是相对冗余的信息。

日志记录这个信息我用的是自定义的 context ,因为标准库里的 context 实际上只有下文没有上文。可以看这个帖子 https://s.v2ex.com/t/1012453 我的回复。

用 context 的话可以自定义数据结构,能够比较好处理那些复杂的结构体,也就是你说的第二条里的麻烦。

对于 context 信息的记录就没有必要走业务层面的 logger 了。对于不太敏感又注重性能的业务,我的处理是加一个开关,当线上出现的问题无法复现的时候,开启开关再记录完整的 runtime stack trace 信息。对于相对敏感的业务,会单独用一个 logger 导出到日志服务器单独的表中,使用 traceId 对业务层面的日志和实现层面的日志做个关联。业务层面的日志要保留很久,但功能层面的日志一般三个月就可以清理了。

这跟 java 与 go 好像没任何关系吧?你在业务处理的时候不记日志?既然记了日志,就会有碰到相关问题吧,函数调用链长,我的 err 的日志记在哪里合适

谢谢啦,一会去看看

嗯,谢谢了,我看看第二个库满足不满足我的需求。我也觉得记录 fmt.Errorf 这种逻辑栈记录下来好像也够了

非常感谢,感觉给我了很大的启发。特别是使用 ctx 的方式去记录复杂的一些数据结构,感觉是个不错的思路

不是日志的问题,而是 controller->service->repo 层 一看就是 Java 转过来的。

楼主你就说你是不是 Java 转的?纯好奇

这和 java 有啥关系呢,即使 php 古早的框架也有 controller 与 model 层呢。为啥重点放这里,难道你们代码都不需要分层?无论是 controller ,service 还是 repo ,model ,这只是个分层的名词而已。如果你们一点也不分层当我没说咯

我的习惯是 repo 这一层打印日志,上面零星会打印一些关键日志。

我会分层,但是不严格按照这个规范来。会以代码的量来区分不同的层级,也不会叫 controller

他们好奇楼主是 java 转过来的是因为 mvc+仓储,而 go 的框架大多是[注册路由和中间件-> 路由绑定函数 -> 函数内操作 dal 和 model]一般是这样

事实上,现在其他语言任何的成熟的框架都很少把业务逻辑层与数据访问层放在同一层或只整合成一层,这不仅仅是 java,php,c#主流的框架也一样,这是写业务代码,不是写中间件或一些基础组件啊。ps:我以前是写 php 的,并不写 java 的

不太懂怎么按代码量来分层的,如果是按代码量,我宁愿不分层,多建个文件写业务就行了。如果我一个业务,写着写着本来只有 1000 行,我再加个接口就变 1200 行了,我就开始分层了?那这个分层过程还要做抽离呢,还是仅仅把新增的接口业务放在另一层?

比如我有 a 、b 两个类型,如果围绕他俩的函数或其他方法的代码量很少,我就不会拆,大一点了就会拆成两个文件分别存。我是比较讨厌提前优化和过度优化的。go 本身也是不提倡严格分层的,而是提倡根据你的代码来

对于三层架构,我并不觉得这是多过度优化,过度优化的是复杂的设计模式,依赖注入控制反转那些。三层架构在写业务代码的时候是有优势的,我觉得这无关语言。而且这也不是我想探讨的主题

嗯,你用起来就完事儿了,反正咱俩又不一起写代码

关于记录一些数据,需要像 php 封装异常一样,去实现一些错误类型,例如:

还有一些指针想获得对应值可以使用反射

在Golang(Go语言)中,日志记录是开发过程中不可或缺的一部分,它不仅能帮助开发者追踪程序的运行状态,还能在出现问题时提供关键的调试信息。以下是一些关于Go语言日志记录的专业建议:

  1. 选择合适的日志库:Go社区提供了多个高质量的日志库,如logruszapzerolog等。这些库提供了灵活的日志级别、格式化选项以及高性能的日志输出。根据项目需求选择合适的日志库,可以显著提高日志管理的效率和效果。

  2. 配置日志级别:合理的日志级别设置(如DEBUG、INFO、WARN、ERROR等)有助于过滤掉不必要的日志信息,只保留关键信息,从而更容易定位和解决问题。

  3. 结构化日志:使用结构化日志记录方式,可以确保日志信息易于解析和搜索。这通常涉及将日志信息组织为键值对或JSON格式。

  4. 日志轮转与归档:为了避免日志文件无限增长,应配置日志轮转策略,如按时间或大小进行分割,并归档旧日志。这有助于节省存储空间,同时方便历史日志的查询和分析。

  5. 安全性考虑:在记录敏感信息(如用户密码、密钥等)时,应格外小心,避免这些信息泄露到日志中。可以通过日志脱敏或日志级别控制来保护这些信息。

综上所述,选择合适的日志库、合理配置日志级别、采用结构化日志记录、实施日志轮转与归档以及关注日志安全性,是Go语言项目中有效进行日志记录的关键。

回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!