Go--日志
一、Logger
go语言默认提供的日志功能,包为ttps://golang.org/pkg/log/
优势:
- 使用非常简单,可以设置任何io.Writer作为日志记录输出并向其发送要写入的日志
劣势:
- 仅限基本的日志级别
- 只有一个Print选项。不支持INFO/DEBUG等多个级别
- 对于错误日志,它有Fatal和Panic
- Fatal日志通过调用os.Exit(1)来结束程序
- Panic日志在写入日志消息之后抛出一个panic
- 但是它缺少一个ERROR日志级别,这个级别可以在不抛出panic或退出程序的情况下记录错误
- 缺乏日志格式化的能力——例如记录调用者的函数名和行号,格式化日期和时间格式等
- 不提供日志切割的能力
实例:
package main import (
"fmt"
"log"
"os"
"time"
) func main() {
//创建输出日志文件
logFile, err := os.Create("./" + time.Now().Format("20060102") + ".txt")
if err != nil {
fmt.Println(err)
} //创建一个Logger
//参数1:日志写入目的地
//参数2:每条日志的前缀
//参数3:日志属性
loger := log.New(logFile, "test_", log.Ldate|log.Ltime|log.Lshortfile)
//Flags返回Logger的输出选项
fmt.Println(loger.Flags()) //SetFlags设置输出选项
loger.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) //返回输出前缀
fmt.Println(loger.Prefix()) //设置输出前缀
loger.SetPrefix("test_") //输出一条日志
loger.Output(2, "打印一条日志信息") //格式化输出日志
// loger.Printf("第%d行 内容:%s", 11, "我是错误") //等价于print();os.Exit(1);
// loger.Fatal("我是错误") //等价于print();panic();
// loger.Panic("我是错误33333333") //log的导出函数
//导出函数基于std,std是标准错误输出
//var std = New(os.Stderr, "", LstdFlags) //获取输出项
fmt.Println(log.Flags())
//获取前缀
fmt.Printf(log.Prefix())
}
二、Zap
2.1 安装
go get -u go.uber.org/zap
Zap提供了两种类型的日志记录器—Sugared Logger和Logger。
在性能很好但不是很关键的上下文中,使用SugaredLogger。它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录。
在每一微秒和每一次内存分配都很重要的上下文中,使用Logger。它甚至比SugaredLogger更快,内存分配次数也更少,但它只支持强类型的结构化日志记录
2.2 创建一个logger
创建logger,使用zap.NewDevelopment()、zap.NewExample()、zap.NewProduction(),各自会有一些预定义的设置,适合不同的场景,依次为开发,测试,生产环境
或者用zap.New(),可定制化
//设置全局变量
var logger *zap.Logger
var sugarLogger *zap.SugaredLogger func InitLogger() {
logger, _ = zap.NewProduction()
} func InitSugarLogger() {
logger, _ = zap.NewProduction()
sugarLogger = logger.Sugar()
}
2.3 字段类型
zap.Type(Type为bool/int/uint/float64/complex64/time.Time/time.Duration/error等)就表示该类型的字段,zap.Typep以p结尾表示该类型指针的字段,zap.Types以s结尾表示该类型切片的字段,如:
- zap.Bool(key string, val bool) Field:bool字段
- zap.Boolp(key string, val *bool) Field:bool指针字段
- zap.Bools(key string, val []bool) Field:bool切片字段
- zap.Any(key string, value interface{}) Field:任意类型的字段
- zap.Binary(key string, val []byte) Field:二进制串的字段
实例:
if err != nil {
logger.Error( //打印的日志等级,如Info / Error/ Debug / Panic
"Error fetching url..",
zap.String("url", url), //字段类型
zap.Error(err))
}
2.4 使用方法
logger
func simpleHttpGet(url string) {
resp, err := http.Get(url)
if err != nil {
logger.Error(
"Error fetching url..",
zap.String("url", url),
zap.Error(err))
} else {
logger.Info("Success..",
zap.String("statusCode", resp.Status),
zap.String("url", url))
resp.Body.Close()
}
}
Sugared Logger
func simpleHttpGet(url string) {
sugarLogger.Debugf("Trying to hit GET request for %s", url)
resp, err := http.Get(url)
if err != nil {
sugarLogger.Errorf("Error fetching URL %s : Error = %s", url, err)
} else {
sugarLogger.Infof("Success! statusCode = %s for URL %s", resp.Status, url)
resp.Body.Close()
}
}
2.5 将日志写入文件
使用New()来定制化一个logger:zapcore.Core需要三个配置——Encoder,WriteSyncer,LogLevel
func New(core zapcore.Core, options ...Option) *Logger
- Encoder: 编码器(如何写入日志) 这里使用默认的NewJSONEncoder() 及预设置好的 ProductionEncoderConfig()
- WriterSyncer :指定日志将写到哪里去 使用zapcore.AddSync()函数并且将打开的文件句柄传进去
- Log Level: 哪种级别的日志将被写入
实例:
func InitNewSugarLogger() {
//以json格式写入
encoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
//创建写入的日志文件
file, _ := os.Create("./test.log")
writeSyncer := zapcore.AddSync(file)
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
logger := zap.New(core)
sugarLogger = logger.Sugar()
}
2.6 输出格式
2.6.1 数据格式
logger默认输出JSON结构格式,如:
{"level":"info","msg":"Failed to fetch URL: http://example.org/api"}
将JSON Encoder更改为普通的Log Encoder,将2.4中实例的NewJSONEncoder()改为NewConsoleEncoder()即可
encoder := zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig())
输出为:
1.5731818118626883e+09 debug Trying to hit GET request for http://www.google.com
2.6.2 时间格式
默认输出时间并不适合阅读,如:
1.5731818329012108e+09 error Error fetching URL http://www.google.com
需要更改2.4实例中的zapcore函数
func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
return zapcore.NewConsoleEncoder(encoderConfig)
}
//在创建时添加zap.AddCaller()
logger := zap.New(core, zap.AddCaller())
三、日志切割
Zap本身不支持切割归档日志文件,将使用第三方库Lumberjack来实现
安装:
go get -u github.com/natefinch/lumberjack
zap logger中加入Lumberjack
//写入的文件,并切割日志
func getLogWriter() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "./test1.log", //日志文件的位置
MaxSize: 1, //在进行切割之前,日志文件的最大大小(以MB为单位)
MaxBackups: 5, //保留旧文件的最大个
MaxAge: 30, //保留旧文件的最大天数
Compress: false, //是否压缩/归档旧文件
}
//将打开的文件句柄传进去
return zapcore.AddSync(lumberJackLogger)
}
四、完整案例
package main import (
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"net/http"
) //设置全局变量,使用sugarLogger
var sugarLogger *zap.SugaredLogger //以json格式写入,并修改时间编码
func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
return zapcore.NewConsoleEncoder(encoderConfig)
} //写入的文件,并切割日志
func getLogWriter() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "./test1.log", //日志文件的位置
MaxSize: 1, //在进行切割之前,日志文件的最大大小(以MB为单位)
MaxBackups: 5, //保留旧文件的最大个
MaxAge: 30, //保留旧文件的最大天数
Compress: false, //是否压缩/归档旧文件
}
//将打开的文件句柄传进去
return zapcore.AddSync(lumberJackLogger)
} // InitLogger 自定义logger
func InitLogger() {
writeSyncer := getLogWriter()
encoder := getEncoder()
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel) //创建时添加将调用函数信息记录到日志中的功能
logger := zap.New(core, zap.AddCaller())
sugarLogger = logger.Sugar()
} //测试访问url
func simpleHttpGet(url string) {
sugarLogger.Debugf("Trying to hit GET request for %s", url)
resp, err := http.Get(url)
if err != nil {
sugarLogger.Errorf("Error fetching URL %s : Error = %s", url, err)
} else {
sugarLogger.Infof("Success! statusCode = %s for URL %s", resp.Status, url)
resp.Body.Close()
}
} func main() {
InitLogger()
//zap底层 API 可以设置缓存,所以一般使用defer logger.Sync()将缓存同步到文件中
defer sugarLogger.Sync() simpleHttpGet("www.xxx.com")
simpleHttpGet("https://www.google.com")
}
文件里的日志为:
2023-04-27T17:23:48.749+0800 DEBUG test/logs.go:47 Trying to hit GET request for www.xxx.com
2023-04-27T17:23:48.760+0800 ERROR test/logs.go:50 Error fetching URL www.xxx.com : Error = Get "www.xxx.com": unsupported protocol scheme ""
2023-04-27T17:23:48.760+0800 DEBUG test/logs.go:47 Trying to hit GET request for https://www.google.com
2023-04-27T17:23:49.344+0800 INFO test/logs.go:52 Success! statusCode = 429 Too Many Requests for URL https://www.google.com
logger:
package main import (
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"net/http"
"os"
) // 设置全局变量,使用Logger
var logger *zap.Logger // 以json格式写入,并修改时间编码
func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
//将JSON Encode改为普通的log Encoder,使用NewConsoleEncoder()
return zapcore.NewConsoleEncoder(encoderConfig)
} // 写入的文件,并切割日志
func getLogWriter() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "./err.log", //日志文件的位置
MaxSize: 1, //在进行切割之前,日志文件的最大大小(以MB为单位)
MaxBackups: 5, //保留旧文件的最大个
MaxAge: 30, //保留旧文件的最大天数
Compress: false, //是否压缩/归档旧文件
}
//将打开的文件句柄传进去
return zapcore.AddSync(lumberJackLogger)
} // 写入的文件,并切割日志
func getInfoWriter() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "./info.log", //日志文件的位置
MaxSize: 100, //在进行切割之前,日志文件的最大大小(以MB为单位)
MaxBackups: 5, //保留旧文件的最大个
MaxAge: 15, //保留旧文件的最大天数
Compress: false, //是否压缩/归档旧文件
}
//将打开的文件句柄传进去
return zapcore.AddSync(lumberJackLogger)
} // Init 自定义logger
func init() {
writeSyncer := getLogWriter()
encoder := getEncoder() //设置不同的日志等级;支持>= <= > < ==
infoLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl == zapcore.InfoLevel
})
errorLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl == zapcore.ErrorLevel
}) core := zapcore.NewTee(
//开发测试时输出到控制台,便于查看
zapcore.NewCore(encoder, zapcore.AddSync(os.Stdout), zapcore.InfoLevel),
//生产环境将不同等级日志写入相应的日志文件
zapcore.NewCore(encoder, writeSyncer, errorLevel),
zapcore.NewCore(encoder, getInfoWriter(), infoLevel),
)
logger = zap.New(core)
//zap底层 API 可以设置缓存,所以一般使用defer logger.Sync()将缓存同步到文件中
defer logger.Sync()
} // 测试访问url
func simpleHttpGet(url string) {
resp, err := http.Get(url)
if err != nil {
logger.Error("Error fetching url..")
} else {
logger.Info("Success..",
zap.String("statusCode", resp.Status),
zap.String("url", url))
resp.Body.Close()
}
} func main() {
simpleHttpGet("www.xxx.com")
simpleHttpGet("https://www.google.com")
}
日志文件:
023-05-18T11:38:42.311+0800 ERROR test/logs.go:54 Error fetching url..
2023-05-18T11:38:42.746+0800 INFO test/logs.go:56 Success.. {"statusCode": "429 Too Many Requests", "url": "https://www.google.com"}
2023-05-18T11:40:38.340+0800 ERROR test/logs.go:51 Error fetching url..
2023-05-18T11:40:38.754+0800 INFO test/logs.go:53 Success.. {"statusCode": "429 Too Many Requests", "url": "https://www.google.com"}
参考:https://www.topgoer.com/%E9%A1%B9%E7%9B%AE/log/Logger.html
五、补充
package logs import (
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
) // 设置全局变量,使用Logger
var Logger *zap.Logger // 以json格式写入,并修改时间编码
func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
//将JSON Encode改为普通的log Encoder,使用NewConsoleEncoder()
return zapcore.NewConsoleEncoder(encoderConfig)
} // 写入的文件,并切割日志
func getLogWriter() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "./err.log", //日志文件的位置
MaxSize: 1, //在进行切割之前,日志文件的最大大小(以MB为单位)
MaxBackups: 5, //保留旧文件的最大个
MaxAge: 30, //保留旧文件的最大天数
Compress: false, //是否压缩/归档旧文件
}
//将打开的文件句柄传进去
return zapcore.AddSync(lumberJackLogger)
} // 写入的文件,并切割日志
func getInfoWriter() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "./info.log", //日志文件的位置
MaxSize: 100, //在进行切割之前,日志文件的最大大小(以MB为单位)
MaxBackups: 5, //保留旧文件的最大个
MaxAge: 15, //保留旧文件的最大天数
Compress: false, //是否压缩/归档旧文件
}
//将打开的文件句柄传进去
return zapcore.AddSync(lumberJackLogger)
} // Init 自定义logger
func init() {
writeSyncer := getLogWriter()
encoder := getEncoder() //设置不同的日志等级;支持>= <= > < ==
infoLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl == zapcore.InfoLevel
})
errorLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl == zapcore.ErrorLevel
}) core := zapcore.NewTee(
//开发测试时输出到控制台,便于查看
zapcore.NewCore(encoder, zapcore.AddSync(os.Stdout), zapcore.InfoLevel),
//生产环境将不同等级日志写入相应的日志文件
zapcore.NewCore(encoder, writeSyncer, errorLevel),
zapcore.NewCore(encoder, getInfoWriter(), infoLevel),
)
Logger = zap.New(core)
//zap底层 API 可以设置缓存,所以一般使用defer logger.Sync()将缓存同步到文件中
defer Logger.Sync()
}
Go--日志的更多相关文章
- .NetCore中的日志(2)集成第三方日志工具
.NetCore中的日志(2)集成第三方日志工具 0x00 在.NetCore的Logging组件中集成NLog 上一篇讨论了.NetCore中日志框架的结构,这一篇讨论一下.NetCore的Logg ...
- .NetCore中的日志(1)日志组件解析
.NetCore中的日志(1)日志组件解析 0x00 问题的产生 日志记录功能在开发中很常用,可以记录程序运行的细节,也可以记录用户的行为.在之前开发时我一般都是用自己写的小工具来记录日志,输出目标包 ...
- Logstash实践: 分布式系统的日志监控
文/赵杰 2015.11.04 1. 前言 服务端日志你有多重视? 我们没有日志 有日志,但基本不去控制需要输出的内容 经常微调日志,只输出我们想看和有用的 经常监控日志,一方面帮助日志微调,一方面及 ...
- SQLServer事务同步下如何收缩日志
事务同步是SQLServer做读写分离的一种常用的方式. 随着业务数据的不断增长,数据库积攒了大量的日志,为了腾出硬盘空间,需要对数据库日志进行清理 订阅数据库的日志清理 因为订阅数据库所有的数据都来 ...
- 如何正确使用日志Log
title: 如何正确使用日志Log date: 2015-01-08 12:54:46 categories: [Python] tags: [Python,log] --- 文章首发地址:http ...
- 前端学HTTP之日志记录
前面的话 几乎所有的服务器和代理都会记录下它们所处理的HTTP事务摘要.这么做出于一系列的原因:跟踪使用情况.安全性.计费.错误检测等等.本文将谥介绍日志记录 记录内容 大多数情况下,日志的记录出于两 ...
- ASP.NET Core应用中如何记录和查看日志
日志记录不仅对于我们开发的应用,还是对于ASP.NET Core框架功能都是一项非常重要的功能特性.我们知道ASP.NET Core使用的是一个极具扩展性的日志系统,该系统由Logger.Logger ...
- .NET Core的日志[5]:利用TraceSource写日志
从微软推出第一个版本的.NET Framework的时候,就在“System.Diagnostics”命名空间中提供了Debug和Trace两个类帮助我们完成针对调试和跟踪信息的日志记录.在.NET ...
- .NET Core的日志[4]:将日志写入EventLog
面向Windows的编程人员应该不会对Event Log感到陌生,以至于很多人提到日志,首先想到的就是EventLog.EventLog不仅仅记录了Windows系统自身针对各种事件的日志,我们的应用 ...
- .NET Core的日志[3]:将日志写入Debug窗口
定义在NuGet包"Microsoft.Extensions.Logging.Debug"中的DebugLogger会直接调用Debug的WriteLine方法来写入分发给它的日志 ...
随机推荐
- 增长实验室-ab分流的流量保护功能介绍
介绍ab分流的流量保护功能之前,先普及一下ab分流的一些概念和术语 名词解释: 实验:用来验证某个决定请求处理方式的功能或策略的一部分流量,通常用来验证某个功能或策略对系统指标(如PV/UV,CRT, ...
- .NET8:快速集成Rapid.NET三维控件
.NET8正式版本发布了,AnyCAD Rapid.NET针对.NET8进行了升级和优化.本文以WPF项目为例介绍在.NET8中使用AnyCAD Rapid.NET三维控件. 1 从.NET6升级 若 ...
- freeswitch设置多个execute_on_media
概述 freeswitch是一款简单好用的VOIP开源软交换平台. fs中有非常多的接口和通道变量,使用方式多变. 官方文档有时候也仅仅是介绍了最基本的使用方法和格式. 环境 centos:CentO ...
- 【Javaweb】implements Serializable是什么意思?反序列化是什么意思?
为了保证数据传输的可靠 性,常常要implements Serializable,为什么? 对象本质上是虚无缥缈的,只是内存中的一个地址,如果想要让对象持久化,让对象在网络上传输,总不可能传送一个内存 ...
- Echarts 柱形图最全详解
Echarts 是一款基于 JavaScript 的开源可视化图表库,被广泛应用于数据可视化领域.它提供了丰富的图表类型和交互功能,其中柱形图是最常用和重要的一种图表类型之一.下面是对 Echarts ...
- Quartz核心原理之架构及基本元素介绍
1 什么是Quartz Quartz是一个作业调度框架,它可以与J2EE和J2SE应用相结合,也可以单独使用.它能够创建多个甚至数万个jobs这样复杂的程序,jobs可以做成标准的java组件或EJB ...
- 解决GET http://192.168.41.103:9528/sockjs-node/info?t=1678639328658 net::ERR_CONNECTION_TIMED_OUT
问题现象 解决办法 找到依赖/node_modules/sockjs-client/dist/sockjs.js注释掉下面的一行代码
- 2021AI量化投资训练营重磅升级,自带编程的优势显而易见
量化交易规模突破万亿大关 国内量化交易规模快速发展,今年量化基金已突破万亿大关,并且量化私募的整体业绩十分亮眼,过去5年一线量化私募的超额收益基本在20%~30%,量化交易的占比已达到20%-30%( ...
- 01的token的年度总结
大家好,我是token,一个热爱.NET的普通人,同样我来自湖南衡阳,再次之前我已经遇到非常多的湖南衡阳的老乡,比如李哥. 在这里一年中,我的成长也是非常迅速的,每一年的的每一天,感觉自己的知识点 ...
- CMU DLSys 课程笔记 2 - ML Refresher / Softmax Regression
CMU DLSys 课程笔记 2 - ML Refresher / Softmax Regression 本节 Slides | 本节课程视频 这一节课是对机器学习内容的一个复习,以 Softmax ...