Go编程快闪之 logrus日志库

go,编程,logrus,日志 · 浏览次数 : 202

小编点评

**logrus实践姿势** **1. 使用嵌套格式** 使用 `nested.Formatter` 来构建日志格式。嵌套格式可以帮助你组织日志信息,使其更易于理解。 **2. 设置默认字段** 使用 `logrus.WithFields` 设置默认字段。默认字段可以帮助你快速构建日志格式。 **3. 使用 Hook 用于自定义日志输出** Hook 是一个用于在触发特定事件时附加一些动作的接口。logrus 支持多种类型的 Hook,例如 `FixedFieldHook`、`LevelHook` 和 `LoggerHook`。 **4. 使用 `fmt.Sprintf` 来格式化日志值** `fmt.Sprintf` 可以用于格式化日志值,使其更易于阅读。 **5. 使用 `io.MultiWriter` 来并行输出日志数据** `io.MultiWriter` 可以用于并行输出日志数据到多个文件。这可以提高性能。 **6. 使用颜色控制输出** `logrus` 支持颜色控制输出。你可以使用 `SetFormatter` 设置颜色格式。 **7. 使用滚动日志** `logrus` 支持滚动日志。滚动日志将日志信息按日期或大小滚动到不同的文件中。 **示例代码:** ```go import ( "fmt" "io/ioutil" "log/logrus" ) // cfg 是配置文件结构 type cfg struct { LogDir string } // initLog 初始化日志器 func initLog(cfg config, logName string, log *logrus.Logger) { // 创建日志文件路径 fileDir, err := ioutil.MkdirAll(cfg.LogDir, os.ModeDir) if err != nil { log.Fatal("failed to create folder:", err) } // 创建日志文件 logf, err := rotatelogs.New( cfg.LogDir + logName + ".%Y%m%d%H%M", rotatelogs.WithLinkName(cfg.LogDir + logName), rotatelogs.WithMaxAge(24*time.Hour), rotatelogs.WithRotationTime(time.Hour), ) if err != nil { log.Fatal("failed to create rotatelogs:", err) } // 设置格式器 log.SetFormatter(&nested.Formatter{ HideKeys: true, FieldsOrder: []string{"region", "node", "topic"}, TimestampFormat: "2006-01-02 15:04:05.000", NoColors: true, }) // 设置默认字段 log.WithFields(&cfg) // 注册 Hook log.AddHook(&FixedFieldHook{ Name: "default-field", Value: "default-value", }) } ```

正文

战术卧倒

golang中常见的日志包是logrus, 根据logrus的胚子和我们的生产要求,给出一个生产可用的logrus实践姿势。

主谓宾定状补

logrus是一个结构化的、可插拔的、兼容golang标准log api的日志库。

快速过一下能力

  • 支持对output=TTY增加关键字颜色
  • 内置JSONFormatter和TextFormatter(默认)两种Formatter
  • 支持输出logger所在的函数行位置 log.SetReportCaller(true)
  • 可以兼容golang内置的标准log库, 建议无脑替换
  • 鼓励输出可解析的日志字段,而不是大段的无法结构化的文本日志
log.WithFields(log.Fields{
 "event": event,
 "topic": topic,
 "key": key,
}).Fatal("Failed to send event")

基于现状,凑了6个钱包上生产,下面给出一些自己的生产实践。

添砖加瓦

1. logrus不支持滚动日志

好马配好鞍 https://github.com/lestrrat-go/file-rotatelogs 让你下雨天不再哭泣。

它会根据配置自动按照时间切分日志,并滚动清理日志(不用配磁盘报警,不用担心磁盘满故障)。

	logf, err := rotatelogs.New(
  	cfg.Log.LogDir+logName+".%Y%m%d%H%M",
  	rotatelogs.WithLinkName(cfg.Log.LogDir+logName),
  	rotatelogs.WithMaxAge(24*time.Hour),
  	rotatelogs.WithRotationTime(time.Hour),
  )
  if err != nil {
  	stdLog.Printf("failed to create rotatelogs: %s", err)
  	return
  }

2. 日志格式化

java生态默认日志输出格式:

11:44:44.827 WARN [93ef3E0120160803114444] [main] [ClassPathXmlApplicationContext] Exception encountered during context initialization - cancelling refresh attempt

在公司中javaer占据主流,故java的默认格式就成了公司集中式日志的"标准"格式。

很明显,logrus默认的两种Formatter都不匹配。

github.com/antonfisher/nested-logrus-formatter 让你柳暗花明。

log.SetFormatter(&nested.Formatter{ // 嵌套日志兼容skynet日志格式
		HideKeys:        true,
		FieldsOrder:     []string{"region", "node", "topic"},
		TimestampFormat: "2006-01-02 15:04:05.000", // 显示ms
	})

3. 自定义Hook用法:输出默认字段

写本文的时候,发现logrus官方本身支持输出默认日志字段。

requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
requestLogger.Warn("something not great happened")

Hook: 通常 钩子函数用于在触发某种事件时附带一些动作。

logrus的Hook定义:logEntry满足指定的logLevel日志时, 你想要做的动作(你甚至可以不设置output直接在hook输出日志, 这就是内置write hook的实现)。

type Hook interface {
	Levels() []Level
	Fire(*Entry) error
}

示例代码为logLevel>=info的logEntry,固定了2个日志字段。

type FixedFieldHook struct {
	LogLevels  []logrus.Level
	FixedField map[string]string
}

// Fire will be called when some logging function is called with current hook
// It will format log entry to string and write it to appropriate writer
func (hook *FixedFieldHook) Fire(entry *logrus.Entry) error {
	for k, v := range hook.FixedField {
		entry.Data[k] = v
	}
	return nil
}

log.AddHook(&FixedFieldHook{ // Set fixed field
		FixedField: map[string]string{"region": cfg.LocalRegion, "node": ip},
		LogLevels: []logrus.Level{
			logrus.InfoLevel,
			logrus.ErrorLevel,
			logrus.WarnLevel,
			logrus.FatalLevel,
		},
	})

抛砖引玉,战术卧倒。

使用时是这样:

func initLog(cfg config, logName string, log *logrus.Logger) {
	_, err := os.Stat(cfg.Log.LogDir)
	if os.IsNotExist(err) {
		// stdLod.Debug("folder does not  exists.")
		os.MkdirAll(cfg.Log.LogDir, os.ModeDir)
	}
	logf, err := rotatelogs.New(                             // 基于file形成时间滚动日志
		cfg.Log.LogDir+logName+".%Y%m%d%H%M",
		rotatelogs.WithLinkName(cfg.Log.LogDir+logName), // 让你始终在一个位置查看文件,即使文件已经滚动切分
		rotatelogs.WithMaxAge(24*time.Hour),
		rotatelogs.WithRotationTime(time.Hour),
	)
	if err != nil {
		stdLog.Printf("failed to create rotatelogs: %s", err)
		return
	}
	log.SetFormatter(&nested.Formatter{    // 设置nested日志格式
		HideKeys:        true,
		FieldsOrder:     []string{"region", "node", "topic"},
		TimestampFormat: "2006-01-02 15:04:05.000", // 显示ms
		NoColors:        true,
	})
	log.ReportCaller = true
	log.AddHook(&FixedFieldHook{           // 设置默认字段
		FixedField: map[string]string{"region": cfg.LocalRegion, "node": ip},
		LogLevels: []logrus.Level{
			logrus.InfoLevel,
			logrus.ErrorLevel,
			logrus.WarnLevel,
			logrus.FatalLevel,
		},
	})
	if !cfg.Log.Debug {
		log.SetOutput(logf)
		log.SetLevel(logrus.InfoLevel)
	} else {
		fileAndStdoutWriter := io.MultiWriter(logf, os.Stdout)
		log.SetOutput(fileAndStdoutWriter)
		log.SetLevel(logrus.DebugLevel)
	}
}

与Go编程快闪之 logrus日志库相似的内容:

Go编程快闪之 logrus日志库

战术卧倒 golang中常见的日志包是logrus, 根据logrus的胚子和我们的生产要求,给出一个生产可用的logrus实践姿势。 #### 主谓宾定状补 logrus是一个结构化的、可插拔的、兼容golang标准log api的日志库。 快速过一下能力 - 支持对output=TTY增加关键字

关于开设go语言专题的说明

本专题写作的目的其实是分享go语言编程的使用场景,介绍go语言编程的方方面面,让大家能够用好这个由google公司发明的强力工具,提升大家在这方面的生产力,毕竟**”君子善假与物也“**嘛。 这里我先说明一下,我并不是一个对go语言的所有一切都认同的人,你会发现很多相关从业者也会吐槽go语言的“专制

Go-Zero微服务快速入门和最佳实践(一)

前言 并发编程和分布式微服务是我们Gopher升职加薪的关键。 毕竟Go基础很容易搞定,不管你是否有编程经验,都可以比较快速的入门Go语言进行简单项目的开发。 虽说好上手,但是想和别人拉开差距,提高自己的竞争力,搞懂分布式微服务和并发编程还是灰常重要的,这也是我今年更新文章的重点。 更文计划 我会更

Go的任务调度单元与并发编程

> 摘要:本文由葡萄城技术团队于博客园原创并首发。转载请注明出处:[葡萄城官网](https://www.grapecity.com.cn/),葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 # 前言 本文主要介绍Go语言、进程、线程、协程的出现背景原因以及Go 语言如何解决协程的问

go 实现ringbuffer以及ringbuffer使用场景介绍

> ringbuffer因为它能复用缓冲空间,通常用于网络通信连接的读写,虽然市面上已经有了go写的诸多版本的ringbuffer组件,虽然诸多版本,实现ringbuffer的核心逻辑却是不变的。但发现其内部提供的方法并不能满足我当下的需求,所以还是自己造一个吧。 源码已经上传到github ```

Go协程揭秘:轻量、并发与性能的完美结合

Go协程为并发编程提供了强大的工具,结合轻量级、高效的特点,为开发者带来了独特的编程体验。本文深入探讨了Go协程的基本原理、同步机制、高级用法及其性能与最佳实践,旨在为读者提供全面、深入的理解和应用指导。 关注公众号【TechLeadCloud】,分享互联网架构、云服务技术的全维度知识。作者拥有10

golang pprof 监控系列(1) —— go trace 统计原理与使用

golang pprof 监控系列(1) —— go trace 统计原理与使用 服务监控系列文章 服务监控系列视频 关于go tool trace的使用,网上有相当多的资料,但拿我之前初学golang的经验来讲,很多资料都没有把go tool trace中的相关指标究竟是统计的哪些方法,统计了哪段

通过redis学网络(1)-用go基于epoll实现最简单网络通信框架

![image.png](https://img2023.cnblogs.com/blog/1382767/202306/1382767-20230607105418219-574417823.png) > 本系列主要是为了对redis的网络模型进行学习,我会用golang实现一个reactor网络

golang trace view 视图详解

> 大家好,我是蓝胖子,在golang中可以使用go pprof的工具对golang程序进行性能分析,其中通过go trace 命令生成的trace view视图对于我们分析系统延迟十分有帮助,鉴于当前对trace view视图的介绍还是很少,在粗略的看过trace统计原理后,我将对这部分做比较详细

golang pprof 监控系列(4) —— goroutine thread 统计原理

golang pprof 监控系列(4) —— goroutine thread 统计原理 大家好,我是蓝胖子。 在之前 golang pprof监控 系列文章里我分别介绍了go trace以及go pprof工具对memory,block,mutex这些维度的统计原理,今天我们接着来介绍golan