Go日志系统

1.日志系统

服务器日志是服务器运行过程中记录的各种信息的集合,它们对于系统管理员和开发人员来说具有重要的意义。例如, 调试,监控,行为分析等等。 

go自带一个log库,但与java生态存在同样的窘境,就是被第三方工具盖住了锋芒。例如java日志系统一般使用的是slfj坐门面,log4j或log4j2或logback做实现。

Go的log包提供了简单的日志记录功能,但它的输出格式和功能相对固定,不支持日志级别和结构化日志。如果要使用一些高级日志功能,可以采用一些第三方日志库,例如logrus,zerolog等。本文使用logrus进行演示。

2.logrus 介绍

logrus 是一个开源的 Go 语言日志库,它提供了一个简单、灵活且功能丰富的日志记录系统,被广泛用于 Go 应用程序中。以下是 logrus 的一些主要特性:

  1. 结构化日志logrus 支持结构化日志记录,这意味着你可以以键值对的形式记录日志,使得日志更易于解析和处理。

  2. 多种日志级别:它支持多种日志级别,包括调试(debug)、信息(info)、警告(warn)、错误(error)和致命(fatal)级别。这有助于在不同的环境中控制日志的详细程度。

  3. 自定义日志格式logrus 允许你自定义日志的输出格式,包括文本格式和 JSON 格式。你可以通过实现 Formatter 接口来创建自定义格式。

  4. 钩子(Hooks)logrus 支持钩子,这些钩子可以在日志记录事件发生时执行额外的动作,比如发送警报、记录到外部系统等。

  5. 日志轮转:虽然 logrus 本身不直接支持日志轮转,但可以通过集成第三方库(如 lumberjack)来实现日志文件的自动轮转。

  6. 并发安全logrus 是并发安全的,可以在多个 goroutine 中使用而不需要额外的同步措施。

下面是一个简单的例子:

package mainimport ("github.com/sirupsen/logrus"
)func main() {// 创建一个新的Loggerlogger := logrus.New()// 设置日志输出格式为JSONlogger.Formatter = &logrus.JSONFormatter{}// 设置日志级别logger.Level = logrus.InfoLevel// 记录一条信息级别的日志logger.Info("这是一条信息级别的日志")// 记录一条错误级别的日志logger.Error("这是一条错误级别的日志")// 记录带有字段的结构化日志logger.WithFields(logrus.Fields{"animal":

如果要实现日志翻页功能(每隔一天,或者超过XX大小自动翻页),需要结合lestrrat-go/file-rotatelogs工具。

3.合理的日志分类

在生产环境,主要有两大类日志,一种是系统日志,主要用于记录程序的行为,用于排查bug,行为监控等;另外一种则是运营日志,主要用于数据分析(如果是游戏服务器,当程序出现bug,可用于补偿或者回收)。

对于系统日志,一般无需结构化输出,只有肉眼可分析即可。例如可以用下面的格式:

2024-09-08 19:46:54 [info] ----test1---
2024-09-08 19:46:54 [info] game server is starting ...
2024-09-08 19:48:21 [info] ----test2---
2024-09-08 19:48:21 [info] game server is starting ...
2024-09-08 19:50:14 [info] ----test3---
2024-09-08 19:50:14 [info] game server is starting ...

对于运营日志,如果服务器是分布式部署,需要将不同进程产生的运营日志统一采集到指定的目录,例如通过 ELK(Elasticsearch、Logstash、Kibana)或者hadoop。因此,运营日志一定是结构化日志(类似于mysql的表,有统一的格式),例如可以用下面的格式:

time|1725276165776|model|request|url|/var/queryUserGameVars|remoteIp|103.167.134.39, 172.71.214.146|localIp|127.0.0.1
time|1725276166035|model|request|url|/var/queryUserGameVars|remoteIp|103.167.134.39, 172.71.214.146|localIp|127.0.0.1
time|1725276166288|model|request|url|/array/queryUserGameVars|remoteIp|103.167.134.39, 172.71.214.146|localIp|127.0.0.1
time|1725276166541|model|request|url|/array/queryUserGameVars|remoteIp|103.167.134.39, 172.71.214.147|localIp|127.0.0.1
time|1725276188600|model|request|url|/player/getProgress|remoteIp|103.167.134.39, 172.71.214.146|localIp|127.0.0.1
time|1725276188852|model|request|url|/player/getProgress|remoteIp|103.167.134.39, 172.71.214.146|localIp|127.0.0.1
time|1725276195164|model|request|url|/player/getArchives|remoteIp|103.167.134.39, 172.71.214.146|localIp|127.0.0.1
time|1725276195421|model|request|url|/player/getArchives|remoteIp|103.167.134.39, 172.71.214.146|localIp|127.0.0.1
time|1725276197467|model|request|url|/player/getArchives|remoteIp|103.167.134.39, 172.71.214.147|localIp|127.0.0.1
time|1725276199553|model|request|url|/player/getArchives|remoteIp|103.167.134.39, 172.71.214.146|localIp|127.0.0.1
time|1725276206665|model|request|url|/template/create|remoteIp|103.167.134.39, 172.71.214.146|localIp|127.0.0.1
time|1725276206926|model|request|url|/template/create|remoteIp|103.167.134.39, 172.71.214.146|localIp|127.0.0.1

3.1.系统日志代码

func createConsoleLog() *logrus.Logger {logger := logrus.New()logger.Formatter = &consoleLogFormatter{}// 设置Logger的输出writer, _ := rotatelogs.New("logs/app/"+"app.%Y%m%d",rotatelogs.WithMaxAge(time.Duration(24)*time.Hour),)logger.Out = writer// 设置Logger的日志级别logger.Level = logrus.InfoLevelreturn logger
}

logrus 默认提供了JSONFormatter和TextFormatter两种格式化工具,但日志格式跟笔者的习惯不是很吻合,所以使用了自定义的格式工具,只需实现下面的方法即可。

type Formatter interface {Format(*Entry) ([]byte, error)
}

代码如下:

type consoleLogFormatter struct{}// Format 实现 logrus.Formatter 接口的 Format 方法。
func (f *consoleLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {var b *bytes.Bufferif entry.Buffer != nil {b = entry.Buffer} else {b = &bytes.Buffer{}}// 按照自定义格式写入日志信息_, _ = fmt.Fprintf(b, "%s [%s] %s\n", entry.Time.Format(time.DateTime), entry.Level, entry.Message)return b.Bytes(), nil
}

程序调用的时候只需如下:

// Printf 记录一条日志
func Printf(v string) {consoleLog.Info(v)
}

需要注意的是,该方法只有一个字符串参数,外部调用需要将完整的字符串内容拼完再传进来。

3.2.运营日志代码

运营日志一般需要分模块,例如游戏里的商店、任务、抽奖等等,每个模块可以使用独立文件,或者全部模块放在同一个文件,通过类型进行区分,均可。

变量定义,申明日志类型,名称,以及用一个map保存已经创建好的日志对象

type LogType int// 日志类型枚举,每一个类型对应独立的文件
const (Admin LogType = iotaAPPLICATION
)var logName = map[LogType]string{Admin:       "admin",APPLICATION: "application",
}var (logs       map[string]*logrus.Logger
)

使用自定义的格式化工具,创建logger对象

type businessLogFormatter struct{}// Format 实现 logrus.Formatter 接口的 Format 方法。
func (f *businessLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {var b *bytes.Bufferif entry.Buffer != nil {b = entry.Buffer} else {b = &bytes.Buffer{}}_, _ = fmt.Fprintf(b, "%s", entry.Message)return b.Bytes(), nil
}func createBusinessLog(name string) *logrus.Logger {logger := logrus.New()logger.Formatter = &businessLogFormatter{}writer, _ := rotatelogs.New("logs/"+name+"/"+name+".%Y%m%d",rotatelogs.WithMaxAge(time.Duration(24)*time.Hour),)logger.Out = writer// 设置Logger的日志级别logger.Level = logrus.InfoLevelreturn logger
}

在初始化时,使用map缓存名称与对应的日志对象

func init() {logs = make(map[string]*logrus.Logger)for _, logType := range logName {logger := createBusinessLog(logType)logs[logType] = logger}
}

日志打印接口

func Info(name LogType, args ...interface{}) {if len(args)%2 != 0 {panic("log arguments must be odd number")}logger := logs[logName[name]]sb := &strings.Builder{}sb.WriteString("time|")sb.WriteString(fmt.Sprintf("%d", time.Now().UnixNano()/1000000))sb.WriteString("|")for i := 0; i < len(args); i += 2 {key, ok := args[i].(string)if !ok {panic(fmt.Sprintf("key is not a string: %v", args[i]))}value := args[i+1]sb.WriteString(key)sb.WriteString("|")sb.WriteString(fmt.Sprintf("%v", value))sb.WriteString("|")}sb.WriteString("\n")logger.Info(sb.String())
}

该函数比较复杂,函数的第一个参数代表日志的类型,第二个参数是一个变长参数,因为要拼接key,value的格式,需要变长参数的数量必须是偶数。将数组的奇数项作为key,偶数项作为value。调用代码示例:

log.Info(log.APPLICATION, "key1", "value1", "key2", "value2", "key3", "value3")

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】应用层http协议

一、HTTP协议 1.1 简要介绍一下HTTP 我们在网络的应用层中可以自己定义协议&#xff0c;但是&#xff0c;已经有大佬定义了一些现成的&#xff0c;非常好用的应用层协议&#xff0c;供我们直接使用&#xff0c;HTTP&#xff08;超文本传输协议&#xff09;就是其中之一。 在互…

yolo算法小结

文章目录 yolov1工作原理限制 yolov2网络结构改进点 yolov3改进点 yolov4网络结构图改进点 yolov5改进点 参考资料 YOLO的核心思想是将物体检测视为一个回归问题&#xff0c;它不采用传统的区域提议方法&#xff0c;而是通过单一的神经网络对整个图像进行预测。这意味着YOLO只需…

C/C++两点坐标求距离以及C++保留两位小数输出,秒了

目录 1. 前言 2. 正文 2.1 问题 2.2 解决办法 2.2.1 思路 2.2.2 代码实现 3. 备注 1. 前言 依旧是带来一个练手的题目&#xff0c;目的就一个&#xff0c;方法千千万&#xff0c;通向终点的方式有很多种&#xff0c;没有谁与谁&#xff0c;我们都是为了成为更好的自己。…

使用亚马逊Bedrock的Stable Diffusion XL模型实现文本到图像生成:探索AI的无限创意

引言 什么是Amazon Bedrock&#xff1f; Amazon Bedrock是亚马逊云服务&#xff08;AWS&#xff09;推出的一项旗舰服务&#xff0c;旨在推动生成式人工智能&#xff08;AI&#xff09;在各行业的广泛应用。它的核心功能是提供由顶尖AI公司&#xff08;如AI21 Labs、Anthropic…

python中的循环结构

注意&#xff1a;range&#xff08;&#xff09;函数 累加和&#xff1a; 注意&#xff1a;if 下面如果有好几行&#xff0c;只执行一行 print必须和 for 开头相同格数 例题&#xff1a;水仙花数 注意在print语句中&#xff0c;一句好“ 。。。。。 ”后面必须有逗号然后再写变…

C++(一)----C++基础

1.C的发展史 C语言诞生后&#xff0c;很快普及使用&#xff0c;但是随着编程规模增大且越来越复杂&#xff0c;并且需要高度的抽象和建模时&#xff0c;C语言的诸多短板便表现了出来&#xff0c;为了解决软件危机&#xff0c;上世纪八十年代&#xff0c;计算机界提出了oop&…

linux top命令介绍以及使用

文章目录 介绍 top 命令1. top 的基本功能2. 如何启动 top3. top 的输出解释系统概况任务和 CPU 使用情况内存和交换空间进程信息 4. 常用操作 总结查看逻辑CPU的个数查看系统运行时间 介绍 top 命令 top 是一个在类 Unix 系统中广泛使用的命令行工具&#xff0c;用于实时显示…

WebGL系列教程二(环境搭建及初始化Shader)

目录 1 前言2 新建html页面3 着色器介绍3.1 顶点着色器、片元着色器与光栅化的概念3.2 声明顶点着色器3.3 声明片元着色器 4 坐标系(右手系)介绍5 着色器初始化5.1 给一个画布canvas5.2 获取WebGL对象5.3 创建着色器对象5.4 获取着色器对象的源5.5 绑定着色器的源5.6 编译着色器…

ChatGPT 3.5/4.0使用手册:解锁人工智能的无限潜能

1. 引言 在人工智能的浪潮中&#xff0c;ChatGPT以其卓越的语言理解和生成能力&#xff0c;成为了一个革命性的工具。它不仅仅是一个聊天机器人&#xff0c;更是一个能够协助我们日常工作、学习和创造的智能伙伴。随着ChatGPT 3.5和4.0版本的推出&#xff0c;其功能和应用范围…

windows电脑自动倒计时关机

今天聊一聊其他的。我时不时的有一个需求&#xff0c;是关于在windows电脑上定时关机。 不知道怎么地&#xff0c;我好几次都忘了这个自动定时关机的终端命令&#xff0c;于是每一次都要去网上查。 1.鼠标右击【开始菜单】选择【运行】或在键盘上按【 WinR】快捷键打开运行窗口…

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大&#xff0c;需要补充太多的知识点&#xff0c;教授讲得内容跨越较大&#xff0c;一般一节课的内容是书本上的一章节内容&#xff0c;所以看视频比较吃力&#xff0c;需要先预习课本内容后才能够很好的理解教授讲…

网络学习-eNSP配置VRRP

虚拟路由冗余协议(Virtual Router Redundancy Protocol&#xff0c;简称VRRP) VRRP广泛应用在边缘网络中&#xff0c;是一种路由冗余协议&#xff0c;它的设计目标是支持特定情况下IP数据流量失败转移不会引起混乱&#xff0c;允许主机使用单路由器&#xff0c;以及即使在实际…

模版的价值工程

我们在做什么 工作吗 最终不过是在做模版工程模版&#xff0c;最终会进化 沦为后世的参考文档。仅此而已&#xff01; 或者已经沦为了文档类别 其他&#x1f4c4; 最终我们会选择EXIT 指令 尽快它是 window桌面 我们只是图像 人字&#x1f31f;的&#x1f9a3; &#x1f631;…

leveldb源码剖析(二)——LSM Tree

LSM Tree LSM Tree&#xff1a;Log-Structured Merge Tree&#xff0c;日志结构合并树。是一种频繁写性能很高的数据结构。 LSM Tree将写入操作与合并操作分离&#xff0c;数据首先写入磁盘中的日志文件&#xff08;WAL&#xff09;&#xff0c;随后写入内存缓存&#xff0c;…

Adobe After Effects的插件--------CC Particle World

CC Particle World是一个粒子效果器,用于在三维空间中生成和模拟各种粒子系统,包括火焰、雨、雪、爆炸、烟雾等等。它会自动随时间变化发射粒子。 本文部分参照 https://www.163.com/dy/article/IEJVDN760536FE6V.html 使用条件 使用该插件的图层需是2D图层。 我们新建一个…

Matlab simulink建模与仿真 第十一章(端口及子系统库)【上】

参考视频&#xff1a;simulink1.1simulink简介_哔哩哔哩_bilibili 一、端口及子系统库中的模块概览 注&#xff1a;In模块、Out模块和Subsystem模块在第二章中均有介绍&#xff0c;本章不再赘述&#xff1b;Subsystem Examples子系统实例模块也不进行介绍。 二、使能及其子模…

camtasia2024破解版本安装包网盘下载 附带永久激活码秘钥

Camtasia 2024 &#x1f31f; 新功能大揭秘&#xff0c;让你轻松成为视频制作达人&#xff01; 嘿&#xff0c;亲爱的小红薯们&#xff01;&#x1f44b; 今天我要给大家介绍一款超实用的视频编辑软件——Camtasia 2024。这款软件可是让我的视频制作技能瞬间提升了不止一个档次…

《数字信号处理》学习05-单位冲击响应与系统响应

目录 一&#xff0c;单位冲激响应 二&#xff0c;LTI系统对任意序列的系统响应 三&#xff0c;LTI系统的性质 通过上一篇文章《数字信号处理》学习04-离散时间系统中的线性时不变系统-CSDN博客的学习&#xff0c;我已经知道了离散时间线性时不变系统&#xff08;LTI&#x…

Linux系统本地化部署Dify并安装Ollama运行llava大语言模型详细教程

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Nginx解析:入门笔记

&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》《MYSQL》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 ✨欢迎加入探索nginx之旅✨ &#x1f44b; 大家好&#xff01;文本学习和探索Nginx配置。…