Go红队开辟—日记打印优化

十念  论坛元老 | 2025-3-17 14:43:18 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1795|帖子 1795|积分 5385

日记

最终实现的效果:
(这里是json格式化了,你到时候可以不格式化看起来更装b点)

log

输出打印

log日记最轻易上手,默认自带时间戳打印日记内容

  • 打印

  1. //打印,默认带时间戳
  2. log.Print("log Print")
  3. log.Println("log Println")
  4. log.Printf("%s", "log Printf")
复制代码

  • 日记前缀

  1. //日志前缀(可以用来区别日志级别)
  2. log.SetPrefix("[info] ")
  3. log.Println("info log")
  4. log.SetPrefix("[warn] ")
  5. log.Println("warn log")
复制代码

  • 终止步调
    使用日记的时候大概会希望在某些严重错误日记的时候退出步调
  • fatal终止

  1. //终止程序
  2. //fatal 终止
  3. log.Fatal("触发日志,终止程序!")
  4. fmt.Println("test log") //终止后并不会执行后面代码
复制代码

  • panic终止

  1. //panic 终止
  2. log.Panic("触发恐慌日志,终止程序!")
  3. fmt.Println("test log") //panic程序崩溃的同时,并不会执行后面代码
复制代码
日记控制


  • 格式控制

  1. log.SetFlags(log.Ldate) //日期(YYYY/MM/DD)
  2. log.Println("test log")
  3. log.SetFlags(log.Ltime) //时间(HH:MM:SS)
  4. log.Println("test log")
  5. log.SetFlags(log.Lmicroseconds) //微秒
  6. log.Println("test log")
  7. log.SetFlags(log.Llongfile) //完整文件路径
  8. log.Println("test log")
  9. log.SetFlags(log.Lshortfile) //文件名+行号
  10. log.Println("test log")
复制代码

  • 日记输出目的

    代码中log.Println("123")是输出到标准输出,让你go run main.go > xx.txt的时候那个123才会输出到你的xx.txt文件中
  1. //输出到文件
  2. file, err := os.OpenFile("log.txt", os.O_CREATE|os.O_RDWR, 0666)
  3. if err != nil {
  4.         log.Println("打开文件失败:", err)
  5. }
  6. defer file.Close()
  7. log.SetOutput(file)
  8. log.Println("输出到文件中的日志:xxx")
  9. //设置标准输出
  10. log.SetOutput(os.Stdout) //标准输出,这里就是当你 go run main.go > log.txt的时候,log输出的都回到log.txt中
  11. //设置输出标准错误
  12. //log.SetOutput(os.Stderr) //输出标准错误,这个是一直在终端显示的,在终端使用 > 的时候,打印的日志是无法重定向到文件中去
  13. log.Println("123")
复制代码

  • 自界说日记器

  1. file2, err := os.OpenFile("info.log", os.O_CREATE|os.O_RDWR, 0666)
  2. if err != nil {
  3.         log.Println("打开文件失败:", err)
  4. }
  5. defer file2.Close()
  6. infoLogger := log.New(file2, "[info] ", log.Ldate)
  7. infoLogger.Println("i am info log")
  8. file3, err := os.OpenFile("warn.log", os.O_CREATE|os.O_RDWR, 0666)
  9. if err != nil {
  10.         log.Println("打开文件失败:", err)
  11. }
  12. defer file3.Close()
  13. warnlogger := log.New(file3, "[warn] ", log.Ltime)
  14. warnlogger.Println("i am warn log")
复制代码
测试源码
  1. // log包测试
  2. func log_test() {
  3.     //打印,默认带时间戳
  4.     log.Print("log Print")
  5.     log.Println("log Println")
  6.     log.Printf("%s", "log Printf")
  7.   
  8.     //日志前缀(可以用来区别日志级别)
  9.     log.SetPrefix("[info] ")
  10.     log.Println("info log")
  11.     log.SetPrefix("[warn] ")
  12.     log.Println("warn log")
  13.   
  14.     //终止程序
  15.     //fatal 终止
  16.     // log.Fatal("触发日志,终止程序!")
  17.     // fmt.Println("test log") //终止后并不会执行后面代码
  18.   
  19.     //panic 终止
  20.     // log.Panic("触发恐慌日志,终止程序!")
  21.     // fmt.Println("test log") //panic程序崩溃的同时,并不会执行后面代码
  22.   
  23.     //日志控制
  24.     //格式控制
  25.     log.SetFlags(log.Ldate) //日期(YYYY/MM/DD)
  26.     log.Println("test log")
  27.     log.SetFlags(log.Ltime) //时间(HH:MM:SS)
  28.     log.Println("test log")
  29.     log.SetFlags(log.Lmicroseconds) //微秒
  30.     log.Println("test log")
  31.     log.SetFlags(log.Llongfile) //完整文件路径
  32.     log.Println("test log")
  33.     log.SetFlags(log.Lshortfile) //文件名+行号
  34.     log.Println("test log")
  35.   
  36.     //日志输出目的
  37.     //输出到文件
  38.     file, err := os.OpenFile("log.txt", os.O_CREATE|os.O_RDWR, 0666)
  39.     if err != nil {
  40.         log.Println("打开文件失败:", err)
  41.     }
  42.     defer file.Close()
  43.     log.SetOutput(file)
  44.     log.Println("输出到文件中的日志:xxx")
  45.     //设置标准输出
  46.     log.SetOutput(os.Stdout) //标准输出,这里就是当你 go run main.go > log.txt的时候,log输出的都回到log.txt中
  47.     //设置输出标准错误
  48.     //log.SetOutput(os.Stderr) //输出标准错误,这个是一直在终端显示的,在终端使用 > 的时候,打印的日志是无法重定向到文件中去
  49.     log.Println("123")
  50.   
  51.     //创建自定义日志器
  52.     file2, err := os.OpenFile("info.log", os.O_CREATE|os.O_RDWR, 0666)
  53.     if err != nil {
  54.         log.Println("打开文件失败:", err)
  55.     }
  56.     defer file2.Close()
  57.     infoLogger := log.New(file2, "[info] ", log.Ldate)
  58.     infoLogger.Println("i am info log")
  59.   
  60.     file3, err := os.OpenFile("warn.log", os.O_CREATE|os.O_RDWR, 0666)
  61.     if err != nil {
  62.         log.Println("打开文件失败:", err)
  63.     }
  64.     defer file3.Close()
  65.     warnlogger := log.New(file3, "[warn] ", log.Ltime)
  66.     warnlogger.Println("i am warn log")
  67. }
复制代码
slog

输出打印


  • json格式输出
  1. //日志打印
  2. fmt.Println("slog日志:json格式输出")
  3. logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
  4. fmt.Println("注意:debug不会输出,没有改默认等级")
  5. logger.Debug("json logger Debug") //没有设置默认等级,现在处于info,所以这里debug不会打印出来
  6. logger.Info("json logger Info")
  7. logger.Warn("json logger Warning")
  8. logger.Error("json logger Error")
复制代码

  • text格式输出
  1. fmt.Println("slog日志:text格式输出")
  2. logger = slog.New(slog.NewTextHandler(os.Stdout, nil))
  3. logger.Info("json logger info")
复制代码
修改默认等级

等级排序为:debug < info < warn < error,在log包中一样平常默认等级都是info,以是不修改打印debug的日记范例是打印不出来的,只能修改。
  1. fmt.Println("更改默认日志等级:")
  2. logger2 := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
  3.         Level: slog.LevelDebug,
  4. }))
  5. logger2.Debug("logger2 Debug")
复制代码
修改输出目的


  • slog.NewJSONHandler(os.Stdout, nil):这里的os.Stdout表示标准输出,可以通过>输出到文件中,其他stderr等等其他自己按照情况切换即可,在log包中具体讲了这几个有什么区别了。
  • slog.NewJSONHandler(os.Stdout, nil): 这里第二个参数nil是给自界说log的时候用的,常用的就是可以控制默认等级操作。
    (不做修改的默认等级就是info,你打印debug的时候是打印不出来的)
  1. logger3 := slog.New(slog.NewJSONHandler(os.Stdout, nil)) //标准输出
  2. //logger3 := slog.New(slog.NewJSONHandler(os.Stderr, nil)) //标准错误输出
  3. logger3.Debug("logger3 Debug")
复制代码
自界说logger

意思是说当你通过slog.New控制好一切参数后,将一个实例给到slog默认的logger,那么之后使用slog打印的时候都是使用你那个控制好的参数来打印。
好比这里默认是slog.info是单纯打印一个info日记而已,我们通过修改后以后得slog.info打印出来的是json格式
  1. //自定义slog
  2. logger4 := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
  3.         Level: slog.LevelDebug, //设置默认等级为debug
  4. }))
  5. slog.SetDefault(logger4) //slog默认设置为我们配置好的logger
  6. slog.Debug("debug test") //现在debug默认的时候就能够打印出来了同时是以json格式打印的
复制代码
添加日记细节

当我们想要打印日记的时候肯定是希望在发生一些错误的时候我们能够第一时间定位到错误代码以及错误原因,但是普通的打印错误给到的细节大概会比力少,一股脑堆在一起又难看,slog就可以通过添加细节来给到你的log日记。
slog.Group:意思是分组,很简单看截图就知道,json最明显,就是属性中开一个{}继承存信息。
看截图:
  1. //为slog日志添加更详细的说明
  2. logger5 := slog.New(slog.NewJSONHandler(os.Stdout, nil))
  3. logger5.Info(
  4.         "错误代码",
  5.         slog.String("函数名", "xxx"),
  6.         slog.String("参数值", "xxx xxx"),
  7.         slog.Int("返回值", 666),
  8.         slog.Group("分组",
  9.                 slog.String("code信息", "xxxx"),
  10.                 slog.Int("随便整点", 123),
  11.         ),
  12. )
复制代码
子logger

这里我认为非常重要,没看到解释比力好的文章,有看到大佬务必告诉我,我这里就只管以实用以及意义来讲

  • 子logger是继承父logger的,父logger可以理解为我们最初界说的那个logger,子logger创建的意义就是系统在父logger的日记解释上,加上自己独有的日记解释。
    以下代码就是实现了database_logger继承了logger6的一些参数设置,然后自己扩展属于自己的一些日记字段解释。
  1. fmt.Println("子logger")
  2. logger6 := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
  3.         Level: slog.LevelDebug, //这里多弄点基础性的东西,后面子logger继承的时候就不用设置这么多
  4. }))
  5. database_logger := logger6.With(
  6.         //这里就是添加你的log其他解释就行了
  7.         slog.String("我是数据库的日志分支", "sql语句报错"),
  8.         slog.Int("报错代码code", 111),
  9. )
  10. database_logger.Debug("依旧可以debug日志出来", slog.Int("当然,当你log的时候依旧可以添加更多信息解释你这段log", 123123))
复制代码
日记颜色修改

结合slog日记,颜色修改需要借助其他包
下载fatih/color包,不要下载错了,这里使用的是fatih/color,我记得有另外一个包也可以修改日记颜色,后续可自行学习,目前用一个就够了
  1. go get github.com/fatih/color
复制代码
下载完了记得go mod tidy找到这个包才能用,之前忘记说了
  1. go mod tidy
复制代码
有了/fatih/color,其实不管是不是日记其实都可以修改颜色,这里最简单一个例子:
  1. fmt.Println(color.CyanString("日志打印完毕"))
复制代码
结合slog只是为了可控性以及方便性更强,而不是说每次打印都要修改一下颜色这样子,明白了需求下面就开始对slog的改造。

  • 实现slog.Handler的接口Handle(context.Context, Record) error
  • 实现上面接口就要自界说一个布局体,里面slog.Handler的接口其实都有默认实现,以是我们许多接口都不消实现,只需要实现一个Handle就能代表你是slog.Handler范例了
  • slog.Handler:这个是 布局体内嵌(也叫匿名字段)这样 ColorHander 继承了 slog.Handler 的行为,但这里 slog.Handler,它不会自动实现 Handler 方法,你仍然需要手动实现 Handle()
  • colorlogger *log.Logger:目的是拿*log.Logger的打印方式来打印,也可以用*slog.Logger但是这种的话只能是分级打印,handle方法打印的时候大概需要多写代码了,方便点使用*log.Logger
  1. type ColorHander struct {
  2. slog.Handler
  3. colorlogger *log.Logger
  4. }
复制代码

  • Handle接口实现
  • 为什么只实现Handle?
    因为Handle在slog.Handler中我没看到默认实现,以是想要继承slog.Handler作为他的范例的的话肯定要实现它,他是一个接口,同时也时日记打印的核心调用函数,我们实现了这个,之后的打印过程中都会调用Handle的参数设置
  • ColorHander在函数代码最后举行调用了*log.logger的Println函数举行打印,如果说你当初在布局体给的是*slog.Logger范例的话大概你又要举行分级的一次打印了,同时也时没有须要,其实这里已经将日记完全拆分开给了颜色,然后最后打印即可,甚至你可以使用fmt.Println()举行打印,给一个*log.Logger的话就是为了可控性更加强,我们有一个自界说的外部布局体的logger在handle核心函数里面,控制的时候尽大概少的去动handle核心函数,只需要修改每一个实例化出来的logger就行了。
    (不知道讲明白没有)
  1. // 修改打印颜色其实随时可以修改的
  2. // 这里只是实现slog的自定义日志接口,在里面操作修改日志颜色
  3. func (h *ColorHander) Handle(ctx context.Context, r slog.Record) error {
  4.     //这里就是实现了slog.handler的接口
  5.     level := r.Level.String()
  6.     switch r.Level {
  7.     //将level更改颜色
  8.     case slog.LevelDebug:
  9.         level = color.MagentaString(level)
  10.     case slog.LevelInfo:
  11.         level = color.GreenString(level)
  12.     case slog.LevelWarn:
  13.         level = color.YellowString(level)
  14.     case slog.LevelError:
  15.         level = color.RedString(level)
  16.     }
  17.     //获取日志字段
  18.     //开辟空间后续要用,r.NumAttrs()是获取日志字段的个数//开辟空间后续要用,r.NumAttrs()是获取日志字段的个数
  19.     logContent := make(map[string]interface{}, r.NumAttrs())
  20.     //Attrs是获取日志字段的方法并且返回一个迭代器遍历
  21.     //参数给一个匿名函数,这个匿名函数的参数类型和返回值对应上即可自定义遍历日志字段
  22.     r.Attrs(func(i slog.Attr) bool { //目的就是获取日志字段,放到logContent里后续使用
  23.         logContent[i.Key] = i.Value
  24.         return true
  25.     })
  26.   
  27.     //map类型的logContent拿到数据后,格式化为json
  28.     dataJson, err := json.MarshalIndent(logContent, "", "\t")
  29.     if err != nil {
  30.         return err
  31.     }
  32.   
  33.     //打印日志,因为我们拿到handle函数,就是要修改为自己的日志形式,所以需要进行打印日志
  34.     //1.日志中有时间,那么自定义的话自然要将日志的时间格式化
  35.     timeStr := r.Time.Format("[15:05:05.000]")
  36.     //2.日志的内容也可以在这里修改颜色,上面只是对level进行了颜色修改
  37.     message := color.CyanString(r.Message)
  38.     //3.设置完成后就可以打印了属于你自己的日志了
  39.     //这里的h就派上用场了,其实不给那个colorlogger也行,只是你在使用的时候无法支持log.Logger的用法
  40.     //没有colorlogger 的话,我们外部创建logger的单个示例的时候就无法应用到全部身上了
  41.     h.colorlogger.Println(timeStr, level, message, string(dataJson))
  42.   
  43.     return nil
  44. }
复制代码
源码

想必看到日记打印出来的那一刻,是十分的优雅
  1.   // 实现日记颜色修改type ColorHander struct {      //这个是 布局体内嵌(也叫匿名字段)这样 ColorHander 继承了 slog.Handler 的行为,但这里 slog.Handler 只是一个接口,它不会自动实现 Handler 方法,你仍然需要手动实现 Handle()    slog.Handler      //用来实现slog.handler的接口,因为接口只有一个,实现了就能作为*log.handler传参了    //目的是拿*log.Logger的打印方式来打印,也可以用*slog.Logger但是这种的话只能是分级打印,handle方法打印的时候大概需要多写代码了,方便点使用*log.Logger    colorlogger *log.Logger}  // 修改打印颜色其实随时可以修改的
  2. // 这里只是实现slog的自定义日志接口,在里面操作修改日志颜色
  3. func (h *ColorHander) Handle(ctx context.Context, r slog.Record) error {
  4.     //这里就是实现了slog.handler的接口
  5.     level := r.Level.String()
  6.     switch r.Level {
  7.     //将level更改颜色
  8.     case slog.LevelDebug:
  9.         level = color.MagentaString(level)
  10.     case slog.LevelInfo:
  11.         level = color.GreenString(level)
  12.     case slog.LevelWarn:
  13.         level = color.YellowString(level)
  14.     case slog.LevelError:
  15.         level = color.RedString(level)
  16.     }
  17.     //获取日志字段
  18.     //开辟空间后续要用,r.NumAttrs()是获取日志字段的个数//开辟空间后续要用,r.NumAttrs()是获取日志字段的个数
  19.     logContent := make(map[string]interface{}, r.NumAttrs())
  20.     //Attrs是获取日志字段的方法并且返回一个迭代器遍历
  21.     //参数给一个匿名函数,这个匿名函数的参数类型和返回值对应上即可自定义遍历日志字段
  22.     r.Attrs(func(i slog.Attr) bool { //目的就是获取日志字段,放到logContent里后续使用
  23.         logContent[i.Key] = i.Value
  24.         return true
  25.     })
  26.   
  27.     //map类型的logContent拿到数据后,格式化为json
  28.     dataJson, err := json.MarshalIndent(logContent, "", "\t")
  29.     if err != nil {
  30.         return err
  31.     }
  32.   
  33.     //打印日志,因为我们拿到handle函数,就是要修改为自己的日志形式,所以需要进行打印日志
  34.     //1.日志中有时间,那么自定义的话自然要将日志的时间格式化
  35.     timeStr := r.Time.Format("[15:05:05.000]")
  36.     //2.日志的内容也可以在这里修改颜色,上面只是对level进行了颜色修改
  37.     message := color.CyanString(r.Message)
  38.     //3.设置完成后就可以打印了属于你自己的日志了
  39.     //这里的h就派上用场了,其实不给那个colorlogger也行,只是你在使用的时候无法支持log.Logger的用法
  40.     //没有colorlogger 的话,我们外部创建logger的单个示例的时候就无法应用到全部身上了
  41.     h.colorlogger.Println(timeStr, level, message, string(dataJson))
  42.   
  43.     return nil
  44. }  func slog_colorTest() {    fmt.Println("-----------------------slog_colorTest------------------------")    myColorLogger := slog.New(&ColorHander{        Handler: slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{            Level: slog.LevelDebug,        }),        colorlogger: log.New(os.Stdout, "", 0),    })    myColorLogger.Debug("debug日记", slog.String("code信息", "xxxx"), slog.Int("任意整点", 123))    myColorLogger.Info("info日记", slog.String("code信息", "xxxx"), slog.Int("任意整点", 123))    myColorLogger.Warn("warn日记", slog.String("code信息", "xxxx"), slog.Int("任意整点", 123))    myColorLogger.Error("error日记", slog.String("code信息", "xxxx"), slog.Int("任意整点", 123))  }func main() {    slog_colorTest()}
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

十念

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表