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方法来写入分发给它的日志 ...
随机推荐
- .NET开源全面方便的第三方登录组件集合 - MrHuo.OAuth
前言 我相信做开发的同学应该都对接过各种各样的第三方平台的登录授权,来获取用户信息(如:微信登录.支付宝登录.QQ登录.GitHub登录等等).今天给大家推荐一个.NET开源好用的.全面的.方便第三方 ...
- 不懂乐理,也能扒谱,基于openvpi将mp3转换为midi乐谱(Python3.10)
所谓"扒谱"是指通过听歌或观看演奏视频等方式,逐步分析和还原音乐作品的曲谱或乐谱的过程.它是音乐学习和演奏的一种常见方法,通常由音乐爱好者.乐手或学生使用. 在扒谱的过程中,人们会 ...
- springboot在线人数统计
在线人数统计 笔者做了一个网站,需要统计在线人数. 在线有两种: 一.如果是后台系统如果登录算在线,退出的时候或者cookie.token失效的时候就算下线 二.如果是网站前台,访问的时候就算在线 今 ...
- 飞腾E2000板载以太网适配
飞腾E2000板载以太网适配 e20005.4.18macbyt8521 1. 设备树移植 2. MACB驱动移植 3. 网络连通性调试 3-1. MACB驱动环回测试 3-2. YT8521 PHY ...
- games101-2 透视深度插值矫正与抗锯齿分析
透视深度插值矫正与抗锯齿分析 深度插值的差错原因 透视深度插值公式推导 games101中的错误 msaa与ssaa简要定义 games101中ssaa的实现 games101中msaa的实现 深度插 ...
- 写入数据或者通过EXCEl批量导入到数据库时报类型转换异常问题
报错日志如下(此处我用的是达梦,实际MySQL和oracle也会有类似的问题): Cause: org.apache.ibatis.type.TypeException: Error setting ...
- 解决报错:Java 8 date/time type `java.time.Duration` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
1.错误信息: Java 8 date/time type java.time.Duration not supported by default: add Module "com.fast ...
- Java线程池ThreadPoolExecutor源码解析
Java线程池ThreadPoolExecutor源码解析 1.ThreadPoolExecutor的构造实现 以jdk8为准,常说线程池有七大参数,通常而言,有四个参数是比较重要的 public T ...
- Spring Boot入坑-3-Maven简介
概述 Maven是一个自动化构建工具 能够将支持Maven项目的依赖包从远程仓库拉取到本地仓库,并进行依赖管理 项目中通过pom.xml来描述项目,对项目依赖进行管理与配置 给项目提供编译.打包.安装 ...
- DevOps常用工具全家桶,实现高效运维和交付
DevOps常用工具全家桶,实现高效运维和交付 1.DevOps发展 DevOps发展背景: 随着互联网技术的快速发展,软件开发和运维的挑战也日益增加.传统的软件开发和运维模式往往存在分离.效率低下. ...