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方法来写入分发给它的日志 ...
随机推荐
- 使用js写一个音乐音谱图
我们经常看到在听乐音的时候,会有音谱图随着音乐的节奏不断变化给人视觉上的享受,那么我们通过js来实现以下这个效果,下面是简单的效果图 首先我们需要有一个绘制音频的函数 function draw() ...
- 代替gets()的新操作
1 scanf("%[^\n]", s); ^是"非" \n是换行 所以这就是一直读到\n才结束
- Cannot resolve symbol ‘c:forEach‘;Cannot resolve taglib with uri http://java.sun.com/jsp/jstl/corede
#### Cannot resolve taglib with uri http://java.sun.com/jsp/jstl/core:等类似,都是因为 在jsp页面中加入<%@ tagli ...
- 渐进式web全栈:blazor web app
前言 本文要说的这种开发模式,这种模式并不是只有blazor支持,js中有一样的方案next.js nuxt.js:blazor还有很多其它内容,本文近关注渐进式开发模式. 是的,前后端是主流,不过以 ...
- Hive数据库数据表元数据导出脚本
表结构导出 ## @Title Hive库表元数据导出脚本 ## @Author changxy #!/bin/bash ##############注意修改Hive连接信息############# ...
- 使用IDEA2022.3创建web工程~
为什么突然记录这么一篇博客呢? 以前都是用2019IDEA的,突然换成了IDEA2022懵逼了,所以记录一下~ 具体步骤 1.创建一个新的Project 2.注意选择BuildSystem 3.在当前 ...
- npm 发布流程
登录npm 查看本地是否登录 # 全局配置源 npm who am i # 官方源 npm who am i --registry https://registry.npmjs.org 注: npm源 ...
- 【电影推荐系统】Spring Boot + Vue3 前后端分离项目
目录 0 前言 1 项目前端介绍 1.1 项目启动和编译 1.1.1 项目启动 1.1.2 项目编译 1.2 前端技术栈 1.3 功能模块前端界面展示 1.3.1 基础功能模块 1.3.2 用户模块 ...
- Python——第五章:json模块
什么是json: json 模块是用于处理 JSON(JavaScript Object Notation)数据的模块,翻译过来叫js对象简谱.JSON是一种轻量级的数据交换格式,常用于将数据在不同语 ...
- BUUCTF 加固题 Ezsql WriteUp
文章目录 想直接要加固代码的点这里 题目 一.查看 二.进入目标机器加固 修改前的文件: 添加如下代码: 修改后的文件 三.Check 想直接要加固代码的点这里 题目 靶机地址解释: 第一行:目标机器 ...