golang 日志库logrus实践

news2024/11/24 18:47:16

logrus完全兼容标准的log库,还支持文本、JSON 两种日志输出格式。很多知名的开源项目都使用了这个库,如大名鼎鼎的 docker。

快速使用

第三方库需要先安装:

$ go get github.com/sirupsen/logrus

后使用:

package main

import (
  "github.com/sirupsen/logrus"
)

func main() {
  logrus.SetLevel(logrus.TraceLevel)

  logrus.Trace("trace msg")
  logrus.Debug("debug msg")
  logrus.Info("info msg")
  logrus.Warn("warn msg")
  logrus.Error("error msg")
  logrus.Fatal("fatal msg")
  logrus.Panic("panic msg")
}

logrus的使用非常简单,与标准库log类似。logrus支持更多的日志级别:

  • Panic:记录日志,然后panic

  • Fatal:致命错误,出现错误时程序无法正常运转。输出日志后,程序退出;

  • Error:错误日志,需要查看原因;

  • Warn:警告信息,提醒程序员注意;

  • Info:关键操作,核心流程的日志;

  • Debug:一般程序中输出的调试信息;

  • Trace:很细粒度的信息,一般用不到;

日志级别从上向下依次增加,Trace最大,Panic最小。logrus有一个日志级别,高于这个级别的日志不会输出。默认的级别为InfoLevel。所以为了能看到TraceDebug日志,我们在main函数第一行设置日志级别为TraceLevel

运行程序,输出:

$ go run main.go
time="2020-02-07T21:22:42+08:00" level=trace msg="trace msg"
time="2020-02-07T21:22:42+08:00" level=debug msg="debug msg"
time="2020-02-07T21:22:42+08:00" level=info msg="info msg"
time="2020-02-07T21:22:42+08:00" level=info msg="warn msg"
time="2020-02-07T21:22:42+08:00" level=error msg="error msg"
time="2020-02-07T21:22:42+08:00" level=fatal msg="fatal msg"
exit status 1

由于logrus.Fatal会导致程序退出,下面的logrus.Panic不会执行到。

另外,我们观察到输出中有三个关键信息,timelevelmsg

  • time:输出日志的时间;

  • level:日志级别;

  • msg:日志信息。

定制

输出文件名

调用logrus.SetReportCaller(true)设置在输出日志中添加文件名和方法信息:

package main

import (
  "github.com/sirupsen/logrus"
)

func main() {
  logrus.SetReportCaller(true)

  logrus.Info("info msg")
}

输出多了两个字段file为调用logrus相关方法的文件名,method为方法名:

$ go run main.go
time="2020-02-07T21:46:03+08:00" level=info msg="info msg" func=main.main file="D:/code/golang/src/github.com/darjun/go-daily-lib/logrus/caller/main.go:10"

添加字段

有时候需要在输出中添加一些字段,可以通过调用logrus.WithFieldlogrus.WithFields实现。logrus.WithFields接受一个logrus.Fields类型的参数,其底层实际上为map[string]interface{}

// github.com/sirupsen/logrus/logrus.go
type Fields map[string]interface{}

下面程序在输出中添加两个字段nameage

package main

import (
  "github.com/sirupsen/logrus"
)

func main() {
  logrus.WithFields(logrus.Fields{
    "name": "dj",
    "age": 18,
  }).Info("info msg")
}

如果在一个函数中的所有日志都需要添加某些字段,可以使用WithFields的返回值。例如在 Web 请求的处理器中,日志都要加上user_idip字段:

package main

import (
  "github.com/sirupsen/logrus"
)

func main() {
  requestLogger := logrus.WithFields(logrus.Fields{
    "user_id": 10010,
    "ip":      "192.168.32.15",
  })

  requestLogger.Info("info msg")
  requestLogger.Error("error msg")
}

实际上,WithFields返回一个logrus.Entry类型的值,它将logrus.Logger和设置的logrus.Fields保存下来。调用Entry相关方法输出日志时,保存下来的logrus.Fields也会随之输出。

重定向输出

默认情况下,日志输出到io.Stderr。可以调用logrus.SetOutput传入一个io.Writer参数。后续调用相关方法日志将写到io.Writer中。现在,我们就能像上篇文章介绍log时一样,可以搞点事情了。传入一个io.MultiWriter,同时将日志写到bytes.Buffer、标准输出和文件中:

package main

import (
  "bytes"
  "io"
  "log"
  "os"

  "github.com/sirupsen/logrus"
)

func main() {
  writer1 := &bytes.Buffer{}
  writer2 := os.Stdout
  writer3, err := os.OpenFile("log.txt", os.O_WRONLY|os.O_CREATE, 0755)
  if err != nil {
    log.Fatalf("create file log.txt failed: %v", err)
  }

  logrus.SetOutput(io.MultiWriter(writer1, writer2, writer3))
  logrus.Info("info msg")
}

自定义

实际上,考虑到易用性,库一般会使用默认值创建一个对象,包最外层的方法一般都是操作这个默认对象。

我们之前好几篇文章都提到过这点:

  • Go 每日一库之 flag:flag标准库中的CommandLine对象;

  • Go 每日一库之 log:log标准库中的std对象。

这个技巧应用在很多库的开发中,logrus也是如此:

// github.com/sirupsen/logrus/exported.go
var (
  std = New()
)

func StandardLogger() *Logger {
  return std
}

func SetOutput(out io.Writer) {
  std.SetOutput(out)
}

func SetFormatter(formatter Formatter) {
  std.SetFormatter(formatter)
}

func SetReportCaller(include bool) {
  std.SetReportCaller(include)
}

func SetLevel(level Level) {
  std.SetLevel(level)
}

首先,使用默认配置定义一个Logger对象stdSetOutput/SetFormatter/SetReportCaller/SetLevel这些方法都是调用std对象的对应方法!

我们当然也可以创建自己的Logger对象,使用方式与直接调用logrus的方法类似:

package main

import "github.com/sirupsen/logrus"

func main() {
  log := logrus.New()

  log.SetLevel(logrus.InfoLevel)
  log.SetFormatter(&logrus.JSONFormatter{})

  log.Info("info msg")
}

日志格式

logrus支持两种日志格式,文本和 JSON,默认为文本格式。可以通过logrus.SetFormatter设置日志格式:

package main

import (
  "github.com/sirupsen/logrus"
)

func main() {
  logrus.SetLevel(logrus.TraceLevel)
  logrus.SetFormatter(&logrus.JSONFormatter{})

  logrus.Trace("trace msg")
  logrus.Debug("debug msg")
  logrus.Info("info msg")
  logrus.Warn("warn msg")
  logrus.Error("error msg")
  logrus.Fatal("fatal msg")
  logrus.Panic("panic msg")
}

程序输出 JSON 格式的日志:

$ go run main.go 
{"level":"trace","msg":"trace msg","time":"2020-02-07T21:40:04+08:00"}
{"level":"debug","msg":"debug msg","time":"2020-02-07T21:40:04+08:00"}
{"level":"info","msg":"info msg","time":"2020-02-07T21:40:04+08:00"}
{"level":"info","msg":"warn msg","time":"2020-02-07T21:40:04+08:00"}
{"level":"error","msg":"error msg","time":"2020-02-07T21:40:04+08:00"}
{"level":"fatal","msg":"fatal msg","time":"2020-02-07T21:40:04+08:00"}
exit status 1

第三方格式

除了内置的TextFormatterJSONFormatter,还有不少第三方格式支持。我们这里介绍一个nested-logrus-formatter。

先安装:

$ go get github.com/antonfisher/nested-logrus-formatter

后使用:

package main

import (
  nested "github.com/antonfisher/nested-logrus-formatter"
  "github.com/sirupsen/logrus"
)

func main() {
  logrus.SetFormatter(&nested.Formatter{
    HideKeys:    true,
    FieldsOrder: []string{"component", "category"},
  })

  logrus.Info("info msg")
}

程序输出:

Feb  8 15:22:59.077 [INFO] info msg

nested格式提供了多个字段用来定制行为:

// github.com/antonfisher/nested-logrus-formatter/formatter.go
type Formatter struct {
  FieldsOrder     []string
  TimestampFormat string  
  HideKeys        bool    
  NoColors        bool    
  NoFieldsColors  bool    
  ShowFullLevel   bool    
  TrimMessages    bool    
}

  • 默认,logrus输出日志中字段是key=value这样的形式。使用nested格式,我们可以通过设置HideKeystrue隐藏键,只输出值;

  • 默认,logrus是按键的字母序输出字段,可以设置FieldsOrder定义输出字段顺序;

  • 通过设置TimestampFormat设置日期格式。

package main

import (
  "time"

  nested "github.com/antonfisher/nested-logrus-formatter"
  "github.com/sirupsen/logrus"
)

func main() {
  logrus.SetFormatter(&nested.Formatter{
    // HideKeys:        true,
    TimestampFormat: time.RFC3339,
    FieldsOrder:     []string{"name", "age"},
  })

  logrus.WithFields(logrus.Fields{
    "name": "dj",
    "age":  18,
  }).Info("info msg")
}

如果不隐藏键,程序输出:

$ 2020-02-08T15:40:07+08:00 [INFO] [name:dj] [age:18] info msg

隐藏键,程序输出:

$ 2020-02-08T15:41:58+08:00 [INFO] [dj] [18] info msg

注意到,我们将时间格式设置成time.RFC3339,即2006-01-02T15:04:05Z07:00这种形式。

通过实现接口logrus.Formatter可以实现自己的格式。

// github.com/sirupsen/logrus/formatter.go
type Formatter interface {
  Format(*Entry) ([]byte, error)
}

设置钩子

还可以为logrus设置钩子,每条日志输出前都会执行钩子的特定方法。所以,我们可以添加输出字段、根据级别将日志输出到不同的目的地。logrus也内置了一个syslog的钩子,将日志输出到syslog中。这里我们实现一个钩子,在输出的日志中增加一个app=awesome-web字段。

钩子需要实现logrus.Hook接口:

// github.com/sirupsen/logrus/hooks.go
type Hook interface {
  Levels() []Level
  Fire(*Entry) error
}

Levels()方法返回感兴趣的日志级别,输出其他日志时不会触发钩子。Fire是日志输出前调用的钩子方法。

package main

import (
  "github.com/sirupsen/logrus"
)

type AppHook struct {
  AppName string
}

func (h *AppHook) Levels() []logrus.Level {
  return logrus.AllLevels
}

func (h *AppHook) Fire(entry *logrus.Entry) error {
  entry.Data["app"] = h.AppName
  return nil
}

func main() {
  h := &AppHook{AppName: "awesome-web"}
  logrus.AddHook(h)

  logrus.Info("info msg")
}

只需要在Fire方法实现中,为entry.Data添加字段就会输出到日志中。

程序输出:

$ time="2020-02-08T15:51:52+08:00" level=info msg="info msg" app=awesome-web

logrus的第三方 Hook 很多,我们可以使用一些 Hook 将日志发送到 redis/mongodb 等存储中:

  • mgorus:将日志发送到 mongodb;

  • logrus-redis-hook:将日志发送到 redis;

  • logrus-amqp:将日志发送到 ActiveMQ。

这里我们演示一个 redis,感兴趣自行验证其他的。先安装logrus-redis-hook

$ go get github.com/rogierlommers/logrus-redis-hook

然后编写程序:

package main

import (
  "io/ioutil"

  logredis "github.com/rogierlommers/logrus-redis-hook"
  "github.com/sirupsen/logrus"
)

func init() {
  hookConfig := logredis.HookConfig{
    Host:     "localhost",
    Key:      "mykey",
    Format:   "v0",
    App:      "aweosome",
    Hostname: "localhost",
    TTL:      3600,
  }

  hook, err := logredis.NewHook(hookConfig)
  if err == nil {
    logrus.AddHook(hook)
  } else {
    logrus.Errorf("logredis error: %q", err)
  }
}

func main() {
  logrus.Info("just some info logging...")

  logrus.WithFields(logrus.Fields{
    "animal": "walrus",
    "foo":    "bar",
    "this":   "that",
  }).Info("additional fields are being logged as well")

  logrus.SetOutput(ioutil.Discard)
  logrus.Info("This will only be sent to Redis")
}

为了程序能正常工作,我们还需要安装redis

windows 上直接使用choco安装 redis:

PS C:\Users\Administrator> choco install redis-64
Chocolatey v0.10.15
Installing the following packages:
redis-64
By installing you accept licenses for the packages.
Progress: Downloading redis-64 3.0.503... 100%

redis-64 v3.0.503 [Approved]
redis-64 package files install completed. Performing other installation steps.
 ShimGen has successfully created a shim for redis-benchmark.exe
 ShimGen has successfully created a shim for redis-check-aof.exe
 ShimGen has successfully created a shim for redis-check-dump.exe
 ShimGen has successfully created a shim for redis-cli.exe
 ShimGen has successfully created a shim for redis-server.exe
 The install of redis-64 was successful.
  Software install location not explicitly set, could be in package or
  default install location if installer.

Chocolatey installed 1/1 packages.
 See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log).

直接输入redis-server,启动服务器:

运行程序后,我们使用redis-cli查看:

我们看到mykey是一个list,每过来一条日志,就在list后新增一项。

总结

本文介绍了logrus的基本用法。logrus的可扩展性非常棒,可以引入第三方格式和 Hook 增强功能。在社区也比较受欢迎。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/766836.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GPlates——【将化石点标记在古地理图上时,改变标记点的大小和形状】

GPlates在还原古地理图和计算古坐标点上功能十分卓越,最近在使用它标注某个类群的化石坐标点时,这些点在地图上显示得非常小,让人不易察觉。作为一款优秀的GUI应用,应该具备更改标记点外观的功能,然而在查阅了官方手册…

C# 快乐数

202 快乐数 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为: 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。 如果这个过程 结果为…

智安云重磅上线,“数智一体“赋能智安云生态价值实现!

智安网络作为互联网行业的先行者和持续创新者,一直秉承着为客户创造更多数字化价值的理念,在这一信念下,致力于为用户打造一个开放、安全、透明和便捷的云计算平台。2023年7月19日,智安云平台V1.0.2版本正式上线,开始面…

深入理解C语言链表

链表是一种常见的数据结构,广泛应用于计算机科学中。C语言提供了丰富的指针操作,使得链表的实现相对简便。本博客将介绍链表的基本概念,以及使用C语言实现链表的代码示例。 目录 一、链表的基本概念 二、链表的分类 三、通俗例子&#xff…

90%的人都不知道的聊天机器人作用和应用场景,你知道吗?

现在是信息爆炸的时代,随着人工智能的飞速发展,智能聊天机器人正在以惊人的速度崭露头角。简单来说,聊天机器人是一种可以模拟和处理人类会话(无论是书面还是口头会话)的计算机程序,数字设备可以像真人一样…

浅谈下mvc和mvp、mvvm到mvvm+Jetpack

作者:抓不住老鼠的猫 三种架构模式 MVC MVC全名为Model-View-Controller,图解如下 View:负责与用户交汇,显示界面。Controller:负责接收来自view的请求,处理业务逻辑。Model:负责数据逻辑&…

透过展会看思创·破茧突围稳中向好,开创激光智造新生态

日前,2023年第十七届慕尼黑光博会在上海国家会展中心成功举办。作为激光行业内快速崛起的“国家高新技术企业”,四川思创激光科技携最新的增材制造专用光纤激光器、高功率单模光纤激光器、手持激光焊设备等新一代产品,展示了在全新战略领域的创新成果。 本届展会思创激光科技以…

python flask 返回中文乱码

使用flask返回数据中带有中文的时候会显示成乱码(ascii), 中文报文: ABAP中的三大财务报表是:* **资产负债表** * **收入证明** * **现金流量表**这些报表全面概述了公司的财务状况和业绩。* **资产负债表**显示公司在特定时间点的资产、负…

招聘小程序制作:连接人才与企业

随着人才市场的竞争日益激烈,招聘小程序成为了企业寻找优秀人才和求职者找到理想工作的重要工具。通过招聘小程序,企业可以发布招聘信息、筛选简历,而求职者可以浏览职位、提交简历等。 招聘小程序的好处 精准匹配人才:招聘小程序…

【SpringBoot】从零开始封装自己的starter并且引入到其他项目中使用

从零开始封装自己的starter并且引入到其他项目中使用 简介 本文将介绍如何从零开始封装自己的starter并且引入到其他项目中使用 为什么要自己封装starter? 这样可以对spring以及其他第三方提供的starter做二次封装或者封装一些自己需要的内容提供给其他项目使用&…

实现大文件远程传输、备份和共享的小秘诀

随着公司规模的增长和业务量的提升,大文件远程传输、备份和共享已经成为了公司必须处理的重要问题。大文件传输和备份涉及到数据安全、数据传输速度、存储空间等多个方面,是一项复杂而又艰巨的任务。本文将从技术层面出发,介绍如何实现大文件…

C# OpenCvSharp+DlibDotNet 人脸替换 换脸

效果 Demo下载 项目 VS2022.net4.8OpenCvSharp4DlibDotNet 相关介绍参考 代码 using DlibDotNet; using OpenCvSharp.Extensions; using OpenCvSharp; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Dra…

开发与测试的相杀相爱

无论开发或测试,都只是一份工作,要认真工作但不要私人感情。若是不下心产生了仇恨心理,请公私分明,在心里扎小人吧;若是产生了不可描述的感情,请低调点,毕竟IT工作已经很辛苦,不能再…

After Effects应该如何选择笔记本电脑?(专家指南)

与其他视觉效果软件相比,After Effects 对笔记本电脑显卡的依赖程度不如对 CPU 的依赖程度。单核性能对于 After Effects 来说非常重要。 然而,这并不意味着您的 GPU 不重要,由于强大的 GPU,某些效果的表现要好得多。简而言之&a…

Authing 入选长城战略咨询《2023 中国潜在独角兽企业》报告

2023 年 6 月 20 日,长城战略咨询(GEI)发布《2023 中国潜在独角兽企业研究》报告。Authing 作为国内首家身份云(IDaaS)厂商入选中国潜在独角兽企业榜单。独角兽企业指具有发展速度快、数量稀少、备受投资者青睐等属性的…

Baklib VS Helplook,企业帮助中心搭建软件对比

2023年,搭建帮助文档的方法与之前相比发生了质的变化,许多企业产品不再依托于传统的方式-本地文档、实体操作手册书,而是选择寻找工具搭建一个在线文档,直接搭建一个在线的帮助中心,用户直接可以访问查阅解决方案。Bak…

【C++初阶】list的模拟实现 附源码

一.list介绍 list底层是一个双向带头循环链表,这个我们以前用C语言模拟实现过,->双向带头循环链表 下面是list的文档介绍: list文档介绍 我们会根据 list 的文档来模拟实现 list 的增删查改及其它接口。 二.list模拟实现思路 既然是用C模拟…

Vue-Router相关理解4

两个新的生命周期钩子 activated和deactivated是路由组件所独有的两个钩子&#xff0c;用于捕获路由组件的激活状态具体使用 activated路由组件被激活时触发 deactivated路由组件失活时触发 src/pages/News.vue <template><ul><li :style"{opacity}&qu…

前端vue项目,加入pre-commit格式化工具prettier

husky工具 husky是一个工具&#xff0c;帮我们处理git hooks&#xff0c;在我们提交代码时候运行我们想要的脚本。工作原理是&#xff1a; 在package.json中加入对象&#xff0c;配置husky运行脚本。husky会决定在我们git的生命周期哪个阶段来运行。 安装husky: npm install …

聊聊用户故事的估算和拆解

这是鼎叔的第六十七篇原创文章。行业大牛和刚毕业的小白&#xff0c;都可以进来聊聊。 欢迎关注本专栏和微信公众号《敏捷测试转型》&#xff0c;星标收藏&#xff0c;大量原创思考文章陆续推出。 对于Scrum和用户故事实践的最大难点&#xff0c;我相信是如何估算用户故事的大…