首页 > 其他分享 >Go的日志库Logrus

Go的日志库Logrus

时间:2023-02-23 23:12:32浏览次数:48  
标签:... logrus args interface Go 日志 os Logrus

一 logrus介绍

文章转载于:https://www.cnblogs.com/liuqingzheng/p/16244545.html

1.1 log标准库优缺点

优点

Go标准库的log日志库非常简单

可以便设置任何io.Writer作为日志输出位置

缺点

1 仅仅提供了print,panic和fatal三个函数,不支持info/debug等多个级别

2 记录错误有Fatal和Panic;Fatal通过调用os.Exit(1)来结束程序;Panic在写入日志后抛出一个panic;缺少ERROR日志级别,在不抛出异常和退出程序的情况下记录日志

3 不支持多输出 - 同时支持标准输出,文件等

4 缺乏日志格式化的能力,例如:记录函数名和行号,格式化日期和时间格式等

5 可读性与结构化差,没有Json格式或有分隔符,不方便后续的日志采集、监控等

6 对于更精细的日志级别、日志文件分割,以及日志分发等,没有提供支持

1.2 Go中常用第三方日志库

在Go的世界,流行的日志框架有logrus、zap、zerolog等

logrus

目前Github上star数量最多的日志库

项目地址: https://github.com/sirupsen/logrus

Stars数量:20.3k

zap

是Uber推出的一个快速、结构化的分级日志库

项目地址:https://github.com/uber-go/zap

官方文档:https://pkg.go.dev/go.uber.org/zap

Stars数量:20.3k

zerolog

它的 API 设计非常注重开发体验和性能。zerolog只专注于记录 JSON 格式的日志,号称 0 内存分配

项目地址:https://github.com/rs/zerolog

Stars数量:6.2k

二 logrus

2.1 logrus特点

优点

  • 完全兼容Go标准库日志模块:logrus拥有六种日志级别:debug、info、warn、error、fatal和panic,这是golang标准库日志模块的API的超集。如果之前项目使用标准库日志模块,完全可以以最低的代价迁移到logrus上
  • 可扩展的Hook机制:允许使用者通过hook的方式将日志分发到任意地方,如本地文件系统、标准输出、linfluxdb、logstash、elasticsearch或者mq等,或者通过hook定义日志内容和格式等
  • 可选的日志输出格式:logrus内置了两种日志格式,JSONFormatter和TextFormatter,如果这两个格式不满足需求,可以自己动手实现接口Formatter,来定义自己的日志格式
  • Field机制:logrus鼓励通过Field机制进行精细化的、结构化的日志记录,而不是通过冗长的消息来记录日志
  • logrus是一个可插拔的、结构化的日志框架,很多开源项目,如docker,prometheus等,都是用了logrus来记录其日志

缺点

尽管 logrus有诸多优点,但是为了灵活性和可扩展性,官方也削减了很多实用的功能,例如:

  • 没有提供行号和文件名的支持
  • 输出到本地文件系统没有提供日志分割功能
  • 官方没有提供输出到ELK等日志处理中心的功能

但是这些功能都可以通过自定义hook来实现

2.2 logrus配置

日志级别

logrus有7个日志级别,依次是Trace --> Debug --> Info --> Warning -->Error --> Fatal -->Panic

//  只输出不低于当前级别是日志数据
logrus.SetLevel(logrus.DebugLevel)

日志格式

logrus内置了JSONFormatterTextFormatter两种格式,也可以通过Formatter接口定义日志格式

 // TextFormatter格式
 logrus.SetFormatter(&logrus.TextFormatter{
    ForceColors:               true,
    EnvironmentOverrideColors: true,
    TimestampFormat:           "2006-01-02 15:04:05", //时间格式
    // FullTimestamp:true,
    // DisableLevelTruncation:true,
 })
 // JSONFormatter格式
 logrus.SetFormatter(&logrus.JSONFormatter{
    PrettyPrint:     false,                 //格式化
    TimestampFormat: "2006-01-02 15:04:05", //时间格式
 })

输出文件

 logfile, _ := os.OpenFile("./log.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
 logrus.SetOutput(logfile) //默认为os.stderr
//logrus.SetOutput(io.MultiWriter(os.Stdout, logfile)) // 输出到多个位置

日志定位

定位行号(如:func=main.main file="./xxx.go:38"

logrus.SetReportCaller(true)

2.3 快速使用

go get github.com/sirupsen/logrus

package main

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

func init() {
// 1 日志级别为debug
logrus.SetLevel(logrus.DebugLevel)
//2 日志格式为json格式
logrus.SetFormatter(&logrus.JSONFormatter{
TimestampFormat: "2006-01-02 15:04:05",
})
// 日志格式为文本格式
//logrus.SetFormatter(&logrus.TextFormatter{
//ForceColors:               true,
//EnvironmentOverrideColors: true,
//TimestampFormat:           "2006-01-02 15:04:05", //时间格式
//FullTimestamp:true,  // 显示完整时间
//DisableLevelTruncation:true,
//})

//3 输出文件为app.log
logfile, _ := os.OpenFile("./app.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
//logrus.SetOutput(io.MultiWriter(os.Stdout,logfile)) // 即写到控制台,又写到文件中
logrus.SetOutput(logfile) // 只写到文件中
//不写默认为os.stderr
// 4 日志定位--显示打印日志文件和位置
logrus.SetReportCaller(true)
}
func main() {
logrus.Infoln("info--日志数据")
logrus.Debugln("debug--日志数据")
logrus.Errorln("err--日志数据")
}


2.4 两个自带formatter和自定义

TextFormatter

type TextFormatter struct {
DisableColors bool // 开启颜色显示

DisableTimestamp bool // 开启时间显示

TimestampFormat string// 自定义时间格式

QuoteEmptyFields bool//空字段括在引号中

CallerPrettyfier func(*runtime.Frame) (function string, file string) //用于自定义方法名和文件名的输出
}

JsonFormatter

type JSONFormatter struct {
TimestampFormat string // 自定义时间格式

DisableTimestamp bool // 开启时间显示

CallerPrettyfier func(*runtime.Frame) (function string, file string) //用于自定义方法名和文件名的输出

PrettyPrint bool //将缩进所有json日志
}

自定义Formatter

//只需要实现该接口
type Formatter interface {
Format(*Entry) ([]byte, error)
}

// 其中entry参数
type Entry struct {
// Contains all the fields set by the user.
Data Fields

// Time at which the log entry was created
Time time.Time

// Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic
Level Level

//Calling method, with package name
Caller *runtime.Frame

//Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic  
Message string 

//When formatter is called in entry.log(), a Buffer may be set to entry
Buffer *bytes.Buffer
}

2.5 日志打印方法

FieldLogger接口: FieldLogger定义了所有日志打印的方法

type FieldLogger interface {
   WithField(key string, value interface{}) *Entry
   WithFields(fields Fields) *Entry
   WithError(err error) *Entry

   Debugf(format string, args ...interface{})
   Infof(format string, args ...interface{})
   Printf(format string, args ...interface{})
   Warnf(format string, args ...interface{})
   Warningf(format string, args ...interface{})
   Errorf(format string, args ...interface{})
   Fatalf(format string, args ...interface{})
   Panicf(format string, args ...interface{})

   Debug(args ...interface{})
   Info(args ...interface{})
   Print(args ...interface{})
   Warn(args ...interface{})
   Warning(args ...interface{})
   Error(args ...interface{})
   Fatal(args ...interface{})
   Panic(args ...interface{})

   Debugln(args ...interface{})
   Infoln(args ...interface{})
   Println(args ...interface{})
   Warnln(args ...interface{})
   Warningln(args ...interface{})
   Errorln(args ...interface{})
   Fatalln(args ...interface{})
   Panicln(args ...interface{})
}

2.6 logrus实例

实例日志打印方式一

默认实例 (函数),即通过logrus包提供的函数(覆盖了FieldLogger接口的所有方法),直接打印日志。但其实logrus包函数是调用了logrus.Loger默认实例。

// 直接调用包函数
func main() {
   logrus.Infoln("info--日志")
   logrus.Errorln("err--日志")
}

实例日志打印方式二

Logger实例(对象),它实现了FieldLogger接口。

func main() {
//var loger = logrus.New()
var loger = logrus.StandardLogger() // 看源码,本质就是logrus.New()
loger.Formatter = &logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"}
loger.Infoln("info--日志")
}

实例日志打印方式三

Entry示例(对象),它也实现了FieldLogger接口,是最终是日志打印入口。

  • 这里用到了Field机制,logrus鼓励通过Field机制进行精细化的、结构化的日志记录,而不是通过冗长的消息来记录日志。
func main() {
logrus.SetFormatter(&logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"})
// Entry实例
entry := logrus.WithFields(logrus.Fields{
"global": "全局字段-每个日志都会输出",
})
entry.WithFields(logrus.Fields{"module": "自定义字段--用户模块"}).Info("info--日志")
entry.WithFields(logrus.Fields{"module": "自定义字段--商品模块"}).Error("Error--日志")
}

2.7 HOOK机制

  • hook即钩子,拦截器。它为logrus提供了强大的功能扩展,如将日志分发到任意地方,如本地文件系统、logstashes等,或者切割日志、定义日志内容和格式等。hook接口原型如下:
type Hook interface {
   Levels() []Level   //日志级别
   Fire(*Entry) error //打印入口(Entry对象)
}

Hook - 实现日志切割功能

需要借助于第三方(日志轮转库):github.com/lestrrat-go/file-rotatelogs
和:github.com/rifflock/lfshook

package main

import (
"github.com/lestrrat-go/file-rotatelogs"
"github.com/rifflock/lfshook"
"github.com/sirupsen/logrus"
"time"
)

//  说明:按时间切割日志文件(2秒创建一个日志文件)
func main() {
// 保存日志文件名为app_hook开头,2s切换一个日志文件,最多保留5份
hook := NewLfsHook("app_hook", time.Second*2, 5)
// 加入钩子
logrus.AddHook(hook)
// 先打印一句日志
logrus.Infoln("info---测试开始")
// 通过WithFields方式创建log,写入通用内容module
log := logrus.WithFields(logrus.Fields{"module": "用户模块"})
// 每隔一秒,调用一次info,一次err,最终只保留5个日志文件
for i := 0; i < 15; i++ {
log.Infoln("info--->成功", i)
time.Sleep(time.Second)
log.Errorln("err--->成功", i)
}
}

// 日志钩子(日志拦截,并重定向)
func NewLfsHook(logName string, rotationTime time.Duration, leastDay uint) logrus.Hook {
writer, err := rotatelogs.New(
// 1 日志文件名字
logName+".%Y%m%d%H%M%S",
// 2 日志周期(默认每86400秒/一天旋转一次)
rotatelogs.WithRotationTime(rotationTime),
// 3 清除历史 (WithMaxAge和WithRotationCount只能选其一)
//rotatelogs.WithMaxAge(time.Hour*24*7), //默认每7天清除下日志文件
rotatelogs.WithRotationCount(leastDay), //只保留最近的N个日志文件
)
if err != nil {
panic(err)
}

// 可设置按不同level创建不同的文件名,咱们把6中日志都写到同一个writer中
lfsHook := lfshook.NewHook(lfshook.WriterMap{
logrus.DebugLevel: writer,
logrus.InfoLevel:  writer,
logrus.WarnLevel:  writer,
logrus.ErrorLevel: writer,
logrus.FatalLevel: writer,
logrus.PanicLevel: writer,
}, &logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"})

return lfsHook
}

Hook - 写入Redis

将日志输出到redis

需要借助于第三方模块:github.com/rogierlommers/logrus-redis-hook

package main

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

func init() {
hookConfig := logredis.HookConfig{
Host:     "localhost",
Key:      "test",
Format:   "v1",
App:      "my_app_name",
Port:     6379,
Hostname: "my_app_hostname",
DB:       0,
TTL:      3600,
}
hook, err := logredis.NewHook(hookConfig)
if err == nil {
logrus.AddHook(hook)
} else {
logrus.Errorf("日志写入redis配置出错: %q", err)
}
}

func main() {
logrus.WithFields(logrus.Fields{"module": "用户模块"}).Info("info--日志--写入redis")
logrus.WithFields(logrus.Fields{"module": "用户模块"}).Error("Error--日志--写入redis")

}

// 测试:
// 1.启动redis服务: redis-server
// 2.监控redis数据: redis-cli monitor

其他Hook

2.8 Fatal处理

logrus的Fatal输出,会执行os.Exit(1)。logrus提供RegisterExitHandler方法,可以在系统异常时调用一些资源释放api等,让应用正确地关闭。

func main() {
logrus.RegisterExitHandler(func() {
fmt.Println("发生了fatal异常,执行关闭文件等工作")
})

logrus.Warnln("warn测试")
logrus.Fatalln("fatal测试")
logrus.Infoln("info测试") //不会执行

}

三 Gin中集成

  • 将gin框架的日志定向到logrus日志文件
package main

import (
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"io"
"os"
)

func init() {
// 日志输出格式
logrus.SetFormatter(&logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"})

// 日志输出路径
logfile, _ := os.OpenFile("./app.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
logrus.SetOutput(io.MultiWriter(os.Stdout, logfile)) // 日志写到控制台和文件中

// Gin日志重定向
gin.DisableConsoleColor()                              //不需要颜色
// gin的日志写到控制台和日志文件中
gin.DefaultWriter = io.MultiWriter(os.Stdout, logfile) //默认是:os.Stdout
//gin.DefaultWriter = logfile 
}

//测试:curl 0.0.0.0:8080/index
func main() {
log := logrus.WithFields(logrus.Fields{
"module": "用户模块",
})

r := gin.Default()
r.GET("/", func(c *gin.Context) {
log.Infoln("info--->gin日志数据")
c.String(200, "ok")
})
r.Run(":8080")
}

四 logrus线程安全

  • 默认情况下,logrus的api都是线程安全的,其内部通过互斥锁来保护并发写。
  • 互斥锁在调用hooks或者写日志的时候执行。
  • 如果不需要锁,可以调用logger.SetNoLock()来关闭。

可以关闭logrus互斥锁的情形:

  • 没有设置hook,或者所有的hook都是线程安全的实现。
  • 写日志到logger.Out已经是线程安全的了。例如,logger.Out已经被锁保护,或者写文件时,文件是以O_APPEND方式打开的,并且每次写操作都小于4k。

标签:...,logrus,args,interface,Go,日志,os,Logrus
From: https://www.cnblogs.com/oaoa/p/17149826.html

相关文章

  • Vulnhub之GlasgowSmile靶机测试过程(部分)
    GlasgowSmile识别目标主机IP地址(kali㉿kali)-[~/Desktop/Vulnhub/GlasgowSmile]└─$sudonetdiscover-ieth1-r10.1.1.0/24Currentlyscanning:10.1.1.0/24|......
  • 机器学习日志 泰坦尼克号获救预测 决策树
    我是链接第一次做机器学习的题目题目要求:给定一堆已知的泰坦尼克号船员信息,每个人的信息包括PassengerId=>乘客IDPclass=>客舱等级(1/2/3等舱位)Name=>乘客姓名......
  • Django级联删除的选项
    Django模型中的on_delete属性具有如下选项:CASCADE级联删除,也就是被引用的实体被删除后,相关的记录信息都会被删除。PROTECT阻止删除被引用的实体,除非先把所有引用的记录......
  • golang-小技巧:slice与sort包
    s1:=make([]string,2)s1[0]="good"s1[1]="morning"//根据val寻找下标i:=sort.SearchStrings(s1,"morning")fmt.Println(i)so......
  • 记录一个使用go的grpc搭建负载均衡去连接consul的错误 ---单纯是因为grpc版本太高了
    直接上错误:  好家伙,直接报源码错误,害得我直接去改源码   原因:grpc版本太高,原本1.53.0  降低为1.51.0就能解决问题 ......
  • Django CSRF认证的几种解决方案
    什么是CSRF浏览器在发送请求的时候,会自动带上当前域名对应的cookie内容,发送给服务端,不管这个请求是来源A网站还是其它网站,只要请求的是A网站的链接,就会带上A网站的cookie......
  • Django 模型 Meta 选项详解
    1.了解Meta内部类每个模型类(Model)下都有一个子类Meta,这个子类就是定义元数据的地方。Meta类封装了一些数据库的信息,称之为Model的元数据。Django会将Meta中的......
  • springboot集成flume实现多系统日志收集
    本次demo实现的功能:使用flume框架收集目标工程的日志信息,并发送到kafka,最终完成kafka的消费1、配置工程配置logback:<!--此处为flume日志采集的配置--><appende......
  • Windows 上 Docker 部署 MongoDb 并构建数据持久化
    拉取镜像老样子先拉取一个镜像。dockerpullmongo:latest运行容器dockerrun-p27017:27017--namemongo-v/d/mongo/data:/data/db-eMONGO_INITDB_ROOT_USERNA......
  • GoAccess实现请求监
    GoAccess实现请求监控简介GoAccess是一款开源的实时web日志分析器和交互式查看器,用于可视化查看HTTP统计信息,可以系统的终端上运行,也可以通过浏览器运行;本文通过使用GoA......