文章目录

  • 前言
  • 一、自己设计log包的重要性
  • 二、日志包的基本需求
    • 1. 全局logger和传递参数的logger的用法
    • 2. 日志包的基本需求
      • logger最基本的功能
    • 3. 日志debug、info、error等级别的使用场景
      • 日志打印的实践经验
  • 三、生产环境中的日志系统架构
  • 四、自定义log包
    • 1. 自定义log的options
    • 2. 自定义log接口
    • 调用
  • 五、使用基于zap封装的自定义log包

前言

在 Go 语言项目中自己设计日志包是非常重要的,原因如下:

  1. 提高代码可读性和可维护性:良好的日志设计可以让代码更加易读和易于维护。日志可以帮助开发人员理解代码的运行过程,方便调试和错误排查。

  2. 支持调试和错误排查:日志可以帮助开发人员跟踪代码的执行路径,从而更容易发现潜在的问题和错误。通过在不同的位置记录不同的日志信息,可以更精确地定位问题所在。

  3. 支持性能分析和优化:日志可以记录代码执行的时间和资源使用情况,从而帮助开发人员进行性能分析和优化。例如,可以记录代码中每个函数的执行时间和调用次数,以及内存使用情况等信息。

  4. 支持安全审计:日志可以记录系统中的操作行为和事件,从而帮助开发人员进行安全审计和漏洞分析。例如,可以记录用户登录和操作行为等信息,以便跟踪恶意行为或异常情况。

总之,自己设计日志包可以帮助开发人员更好地理解和管理代码,提高代码质量和效率。


一、自己设计log包的重要性

开发,debug,故障排查,数据分析,监控告警,保存现场
我们需要设计一个优秀的日志包,如果我们要扩展就比较麻烦,1.基于zap封装,2.自己实现3.改zap的源码

  1. 是否可以替换后期我们想要替换成另一个日志框架
  2. 我们要考虑扩展性,log打印的时候是否支持打印当前的goroutine的id是否支持打印当前的context
  3. 我们给大家提供的日志包,还能支持集成tracing(open-telemetry, metrics,logging),就可以集成jaeger
  4. 是否每个日志打印都能知道这个日志是哪个请求的

封装日志包很重要!最好是自己封装

gorm,go-redis、我们自己业务代码

二、日志包的基本需求

Kratos的日志处理:https://go-kratos.dev/docs/component/log

1. 全局logger和传递参数的logger的用法

  1. 全局 logger

全局 logger 的设计思想是在整个应用程序中都可以方便地使用同一个 logger,避免了在不同的代码段中都要创建 logger 的麻烦。这个 logger 通常是在程序启动时初始化,并通过包级别的变量暴露出来,以便其他代码使用。

全局 logger 的优点是简单易用,可以方便地在整个应用程序中记录日志,但缺点是不能很好地控制日志输出的格式、级别和目标。

  1. 传递参数的 logger

传递参数的 logger 的设计思想是通过将 logger 作为参数传递给需要记录日志的函数,让函数可以控制日志的格式、级别和目标。这个 logger 可以是标准库的 log 包中的 logger,也可以是自己定义的 logger。

传递参数的 logger 的优点是可以更灵活地控制日志输出,但缺点是需要在每个函数调用时都传递 logger,代码可能会变得更复杂。

总的来说,全局 logger 更适合简单的应用程序,而传递参数的 logger 更适合复杂的应用程序,其中需要更灵活地控制日志输出的方式。

2. 日志包的基本需求

logger最基本的功能

  1. 日志基本debug、 info、warn、error . fatal、panic
  2. 打印方式2020-12-02T01:16:18+08:00 INF0 example.go:11 std log json (zap)
  3. 日志是否支持轮转、单文件不能太大,压缩,切割
  4. 日志包是否支持hook,gorm

其他的需求:

是否支持颜色显示是否兼容表中的Log
error打印到error文件,info打印到info文件
error能否发送到其他的监控软件,统计一个metrics错误指标error是否能支持发送到jaeger

其他需求:
高性能
并发安全
插件化:错误告警,发邮件 sentry
参数控制

我会使用基于zap封装

3. 日志debug、info、error等级别的使用场景

log使用经验:

  1. if分支的时候可以打印日志
  2. 写操作要尽量写日志 gorm,要记录数据
  3. for循环打印日志的时候要慎重,for+上万次
  4. 错误产生的原始位置打印日志 A(这里打印行不行)->B->C(error,应该在此处打印日志) 这样做比较保险,所有error一律采用记录stack 同时采用fail fast

debug:
我们为了方便排查错误很多时候会在很多地方使用debug,debug往往很多,上了生产如果开启debvug会导致性能受影响,在上线的时候尽量关闭到debug

info:
关键的地方打印一些信息,这些信息数据可以交给大数据进行分析,info量来说相对比较适中。如果你发现了你的info使用量特别大,你就该考虑是不是可以换成debug

warn(警告):
warn往往不会导致一个请求失败,但是我们还是应该关注的一些数据,
比如:服务端页面要求请求1才是第一页,结果客户端传递的是a,这时,我正常返回 但是打印一次warn,如果有大量的warn,这时我们就能知道 应该是一种爬虫行为

error:
这就是程序失败,我们的函数没有做好错误兼容,由于业务运行过程中的bug,请求第三方资源,创建数据库记录,这种错误一定要关注

panic:
panic会导致整个系统直接挂掉,我们一开始项目启动的时候会链接数据库,可以使用panic去结束掉程序,panic是可以被recover住的
有一些情况 比如slice越界 2/0,业务中遇到这种panic你的程序挂了 这就要命了

Fatal:
最高级别错误,当你使用这个方法的时候你心里应该清楚,这个错误不应该被原谅,就应该导致程序挂掉

日志打印的实践经验

写日志的注意事项

  1. 日志中不能记录敏感数据,密码、token等
  2. 日志打印的时候音量写清楚错误的原因 log.Warnf(“[getDB] init database:%v”,err)
  3. 如果可以,每一条日志尽量和请求的id关联起来
  4. info和error不要乱用,很常见 - 要注意

实践

  1. 好的日志不可能一开始就设计的很好,这是一个演进的过程,日志打印要重视
  2. 日志不是越多越好,越少越好,关键信息要打印
  3. 日志要兼容本地打印
  4. 能否支持动态调整日志级别(能不能拿到nacos中?)

三、生产环境中的日志系统架构

四、自定义log包

自定义 log 包可以根据具体需求提供更加灵活、定制化的日志输出方式,提高了日志的可用性和可维护性。

1. 自定义log的options

允许用户自定义日志记录选项

package logimport ("fmt""github.com/spf13/pflag""go.uber.org/zap/zapcore""strings"
)const (FORAMT_CONSOLE = "console"FORAMT_JSON    = "json"OUTPUT_STD     = "stdout"OUTPUT_STD_ERR = "stderr"flagLevel = "log.level"
)type Options struct {OutputPaths      []string `json:"output-paths" mapstructure:"output-paths"`             //输出文件ErrorOutputPaths []string `json:"error-output-paths" mapstructure:"error-output-paths"` //err输出文件Level            string   `json:"level" mapstructure:"level"`                           //日志级别Format           string   `json:"format" mapstructure:"format"`                         //日志打印格式Name             string   `json:"name" mapstructure:"name"`                             //名称
}
type Option func(o *Options)func NewOptions(opts ...Option) *Options {options := &Options{Level:            zapcore.InfoLevel.String(),Format:           FORAMT_CONSOLE,OutputPaths:      []string{OUTPUT_STD},ErrorOutputPaths: []string{OUTPUT_STD_ERR},}for _, opt := range opts {opt(options)}return options
}
func WithLevel(level string) Option {return func(o *Options) {o.Level = level}
}// 就可以自定义检查规则
func (o *Options) Validate() []error {var errs []errorformat := strings.ToLower(o.Format)if format != FORAMT_CONSOLE && format != FORAMT_JSON {errs = append(errs, fmt.Errorf("not supppor format %s", o.Format))}return errs
}// 可以自己将options具体的列映射到flog的字段上
func (o *Options) AddFloags(fs pflag.FlagSet) *Options {fs.StringVar(&o.Level, flagLevel, o.Level, "log level")return o
}

2. 自定义log接口

它提供了日志记录的功能。包中的主要结构是 zapLogger,它实现了 Logger 接口。它包含了一些方法,如 Debug、DebugC、Debugf、DebugfC、DebugW 和 DebugWC,可以用来记录不同级别的日志消息。同时,它还提供了一个全局默认的 logger 和初始化函数 Init,以便用户可以方便地记录日志。在包的顶部还定义了一些别名类型,如 Field 和 Logger。它们与 zap 包中的类型相同,以方便使用 zap 包的其他功能。

package logimport ("context""go.uber.org/zap""go.uber.org/zap/zapcore""sync"
)type Field = zapcore.Field
type Logger interface {Debug(msg string)DebugC(context context.Context, msg string)Debugf(format string, args ...interface{})DebugfC(context context.Context, format string, args ...interface{})DebugW(msg string, keysAndValues ...interface{})DebugWC(context context.Context, msg string, keysAndValues ...interface{})
}var _ Logger = &zapLogger{}type zapLogger struct {zapLogger *zap.Logger
}func (z *zapLogger) Debug(msg string) {z.zapLogger.Debug(msg)
}
func Debug(msg string) {defaultLogger.zapLogger.Debug(msg)
}func (z *zapLogger) DebugC(context context.Context, msg string) {//TODO implement mepanic("implement me")
}func (z *zapLogger) Debugf(format string, args ...interface{}) {//TODO implement mepanic("implement me")
}func (z *zapLogger) DebugfC(context context.Context, format string, args ...interface{}) {//TODO implement mepanic("implement me")
}func (z *zapLogger) DebugW(msg string, keysAndValues ...interface{}) {//TODO implement mepanic("implement me")
}func (z *zapLogger) DebugWC(context context.Context, msg string, keysAndValues ...interface{}) {//TODO implement mepanic("implement me")
}var (defaultLogger = New(NewOptions())mu            sync.Mutex
)func Logs() *zapLogger {return defaultLogger
}func New(opts *Options) *zapLogger {if opts == nil {opts = NewOptions()}//实例化zapvar zapLevel zapcore.Level//如果你设置的有问题  默认使用消息日志级别if err := zapLevel.UnmarshalText([]byte(opts.Level)); err != nil {zapLevel = zapcore.InfoLevel}loggerConfig := zap.Config{Level: zap.NewAtomicLevelAt(zapLevel),}l, err := loggerConfig.Build(zap.AddStacktrace(zapcore.PanicLevel))if err != nil {panic(err)}logger := &zapLogger{zapLogger: l.Named(opts.Name),}return logger}
func Init(opt *Options) {//看起来没有问题,并发问题,因为我们后面可能希望我们这个全局logger是动态的mu.Lock()defer mu.Unlock()defaultLogger = New(opt)
}

调用

使用了一个自定义的日志库NewGo/log,在main函数中调用了log.Init()和log.Debug()函数。

log.Init()函数用于初始化日志库,接受一个log.Options类型的参数作为配置。在本例中,调用了log.NewOptions()函数创建一个默认的配置,然后传递给log.Init()函数。

log.Debug()函数用于输出调试信息。在本例中,输出了一条hello的调试信息。

package mainimport "NewGo/log"func main() {log.Init(log.NewOptions())log.Debug("hello")/*我们自己封装了一个options,用于隔开zap.config日志初始化,Init(options),整个过程中调用法看不到zap的信息,*/
}

五、使用基于zap封装的自定义log包

这个逻辑和上述自定义的差不多

链接:https://pan.baidu.com/s/1GiCW8basUso5LLWv7eJ0sA?pwd=1234
提取码:1234

【日志包】go语言如何设计日志包 - 基于zap封装适合自己的日志包相关推荐

  1. c语言数字滤波器设计软件,[转载]基于单片机的数字滤波器设计

    数据采集,又称,是利用一种装置,从系统外部采集数据并输入到系统内部的一个接口.数据采集技术广泛引用在各个领域.比如摄像头,麦克风,都是数据采集工具.被采集数据是已被转换为电讯号的各种物理量,如温度.水 ...

  2. c语言课程设计-旅馆管理系统 基于链表

    自己写的一个基础的管理系统,使用链表实现,能实现登入.建立链表.插入信息.查询信息.信息排序.信息删减.保存链表至本地和从本地数据中创建链表等功能 首先是链表的建立和节点插入,我是首先建立链表(第一次 ...

  3. 实时海量日志分析系统的架构设计、实现以及思考

    1 序 对ETL系统中数据转换和存储操作的相关日志进行记录以及实时分析有助于我们更好的观察和监控ETL系统的相关指标(如单位时间某些操作的处理时间),发现系统中出现的缺陷和性能瓶颈. 由于需要对日志进 ...

  4. Python零基础速成班-第11讲-Python日志Logging,小游戏设计game of life

    Python零基础速成班-第11讲-Python日志Logging,小游戏设计game of life 学习目标 Python日志Logging 小游戏设计game of life 课后作业(2必做) ...

  5. go语言复数包_Go语言中包的风格指南

    Go 语言也有自己的命名与代码组织规则.漂亮的代码,布局清晰.易读易懂,就像是设计严谨的 API 一样.拿到代码,用户首先看到和接触的就是布局.命名还有包的结构. 这篇文章不是为了给大家设立硬性的规定 ...

  6. pcap文件解析工具_【免费毕设】PHP网络数据包分析工具的设计与开发(源代码+论文)...

    点击上方"蓝字"关注我们目录 系统设计 网络数据包分析系统的设计 整个网络数据报分析工具采用模块化的设计思想,原因是许多程序太长或太复杂,很难写在单一单元中.如果把代码分为较小的功 ...

  7. 如何设计日志系统_架构 - 如何设计一个百亿级日志系统

    " 日志是记录系统中各种问题信息的关键,也是一种常见的海量数据. 日志平台为集团所有业务系统提供日志采集.消费.分析.存储.索引和查询的一站式日志服务. 主要为了解决日志分散不方便查看.日志 ...

  8. 基于Flume的美团日志收集系统-----架构和设计

    问题导读: 1.Flume-NG与Scribe对比,Flume-NG的优势在什么地方? 2.架构设计考虑需要考虑什么问题? 3.Agent死机该如何解决? 4.Collector死机是否会有影响? 5 ...

  9. 【php毕业设计】基于php+mysql+apache的网络数据包分析工具设计与实现(毕业论文+程序源码)——网络数据包分析工具

    基于php+mysql+apache的网络数据包分析工具设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于php+mysql+apache的网络数据包分析工具设计与实现,文章末尾附有本毕业设 ...

最新文章

  1. 数据蒋堂 | 有序分组
  2. ETSI GS MEC 013,UE 位置 API
  3. Action中五个常量解释,(success,error,input,login,none)
  4. 编程之美-求二叉树中节点的最大距离方法整理
  5. 服务器无显示器,服务器无显示器 远程桌面
  6. 字符编码解码整合工具
  7. 牛客练习赛 71 AC
  8. B端可视化: 图表设计(2)
  9. 黑客成功破解 PlayStation 4 支持运行 Linux
  10. mysql开源内库_MySQL数据库(查询语句)
  11. JavaScript二进制、八进制和十六进制数值
  12. 前端人员的服务端实用学习建议
  13. Lambda拉姆达表达式
  14. 新型企业最重视的评估手段:360评估
  15. 基于matlab的微分例题,基于MATLAB的rlc电路模型仿真例题.doc
  16. 最容易理解的LSM树--以示例讲解合并查找过程
  17. C语言三个点“...“符号含义之数组批量赋相同值
  18. LTE学习笔记--LTE无线连接过程--UE Attach过程和Detach过程
  19. java实现一元多项式减法_一元多项式 加法 减法 乘法
  20. 做自媒体没素材怎么办?

热门文章

  1. 如何用PHP/MySQL为 iOS App 写一个简单的web服务器(译) PART1
  2. C语言 实现4位奇校验的校验生成和检验
  3. LED显示屏亮度不足原因分析
  4. 网赚网盘赚钱,一个自动来钱的小项目,日入200+!
  5. clear Cache for google 浏览器一键清理缓存
  6. Git分支合并请求冲突处理规则
  7. Java实现二分图的最大权匹配
  8. Matlab 中的 isnan 函数
  9. Hack The Box Starting Point 渗透测试入门靶场 TIER 1 - Responder
  10. EMQ压力测试工具emqtt-bench