一、Logger

  go语言默认提供的日志功能,包为ttps://golang.org/pkg/log/

优势:

  1. 使用非常简单,可以设置任何io.Writer作为日志记录输出并向其发送要写入的日志

劣势:

  1. 仅限基本的日志级别

    • 只有一个Print选项。不支持INFO/DEBUG等多个级别
  2. 对于错误日志,它有Fatal和Panic
    • Fatal日志通过调用os.Exit(1)来结束程序
    • Panic日志在写入日志消息之后抛出一个panic
    • 但是它缺少一个ERROR日志级别,这个级别可以在不抛出panic或退出程序的情况下记录错误
  3. 缺乏日志格式化的能力——例如记录调用者的函数名和行号,格式化日期和时间格式等
  4. 不提供日志切割的能力

实例:

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--日志的更多相关文章

  1. .NetCore中的日志(2)集成第三方日志工具

    .NetCore中的日志(2)集成第三方日志工具 0x00 在.NetCore的Logging组件中集成NLog 上一篇讨论了.NetCore中日志框架的结构,这一篇讨论一下.NetCore的Logg ...

  2. .NetCore中的日志(1)日志组件解析

    .NetCore中的日志(1)日志组件解析 0x00 问题的产生 日志记录功能在开发中很常用,可以记录程序运行的细节,也可以记录用户的行为.在之前开发时我一般都是用自己写的小工具来记录日志,输出目标包 ...

  3. Logstash实践: 分布式系统的日志监控

    文/赵杰 2015.11.04 1. 前言 服务端日志你有多重视? 我们没有日志 有日志,但基本不去控制需要输出的内容 经常微调日志,只输出我们想看和有用的 经常监控日志,一方面帮助日志微调,一方面及 ...

  4. SQLServer事务同步下如何收缩日志

    事务同步是SQLServer做读写分离的一种常用的方式. 随着业务数据的不断增长,数据库积攒了大量的日志,为了腾出硬盘空间,需要对数据库日志进行清理 订阅数据库的日志清理 因为订阅数据库所有的数据都来 ...

  5. 如何正确使用日志Log

    title: 如何正确使用日志Log date: 2015-01-08 12:54:46 categories: [Python] tags: [Python,log] --- 文章首发地址:http ...

  6. 前端学HTTP之日志记录

    前面的话 几乎所有的服务器和代理都会记录下它们所处理的HTTP事务摘要.这么做出于一系列的原因:跟踪使用情况.安全性.计费.错误检测等等.本文将谥介绍日志记录 记录内容 大多数情况下,日志的记录出于两 ...

  7. ASP.NET Core应用中如何记录和查看日志

    日志记录不仅对于我们开发的应用,还是对于ASP.NET Core框架功能都是一项非常重要的功能特性.我们知道ASP.NET Core使用的是一个极具扩展性的日志系统,该系统由Logger.Logger ...

  8. .NET Core的日志[5]:利用TraceSource写日志

    从微软推出第一个版本的.NET Framework的时候,就在“System.Diagnostics”命名空间中提供了Debug和Trace两个类帮助我们完成针对调试和跟踪信息的日志记录.在.NET ...

  9. .NET Core的日志[4]:将日志写入EventLog

    面向Windows的编程人员应该不会对Event Log感到陌生,以至于很多人提到日志,首先想到的就是EventLog.EventLog不仅仅记录了Windows系统自身针对各种事件的日志,我们的应用 ...

  10. .NET Core的日志[3]:将日志写入Debug窗口

    定义在NuGet包"Microsoft.Extensions.Logging.Debug"中的DebugLogger会直接调用Debug的WriteLine方法来写入分发给它的日志 ...

随机推荐

  1. 将强化学习引入NLP:原理、技术和代码实现

    本文深入探讨了强化学习在自然语言处理(NLP)中的应用,涵盖了强化学习的基础概念.与NLP的结合方式.技术细节以及实际的应用案例.通过详细的解释和Python.PyTorch的实现代码,读者将了解如何 ...

  2. Codeforces Round #696 (Div. 2) (A~C题解)

    写在前边 链接:Codeforces Round #696 (Div. 2) A. Puzzle From the Future 链接:A题链接 题目大意: 给定一个\(a\),\(b\),\(d = ...

  3. Excel 使用 VLOOKUP 函数匹配特定列

    前言 工作有一项内容,是根据新的表格的某一列的内容一对一匹配,生成一列新的表格.这就用到了 Excel 的 VLOOKUP 函数. 函数使用 函数体: =VLOOKUP(lookup_value,ta ...

  4. excel怎么固定前几行前几列不滚动?

    在Excel中,如果你想固定前几行或前几列不滚动,可以通过以下几种方法来实现.详细的介绍如下: **固定前几行不滚动:** 1. 选择需要固定的行数.例如,如果你想要固定前3行,应该选中第4行的单元格 ...

  5. MacOS|matplotlib 无法显示中文 解决办法

    matplotlib 无法显示中文 解决办法 画图时,中文无法正常显示,如图 下载字体 点击这里获取字体 提取码: wnby 查看字体路径 在 python 环境中执行以下指令 import matp ...

  6. MySQL笔记01: MySQL入门_1.3 MySQL启动停止与登录

    1.3 MySQL启动停止与登录 1.3.1 MySQL启动与停止 MySQL数据库分为客户端和服务器端,只有服务器端服务开启以后,才可以通过客户端登录MySQL服务端. 首先,以管理员身份运行&qu ...

  7. MCube动态化与原生工程结合最佳实践

    跨端动态化开发方案重要性日益凸显,本文对我们团队MCube动态化实践做了总结,为大家提供经验和借鉴. 接入背景 随着我们工程的需求迭代,暴露出了业务需求量大,分端开发和发版更新成本高等痛点,使用H5页 ...

  8. [ABC282E] Choose Two and Eat One

    Problem Statement A box contains $N$ balls, each with an integer between $1$ and $M-1$ written on it ...

  9. [ABC262B] Triangle (Easier)

    Problem Statement You are given a simple undirected graph with $N$ vertices and $M$ edges. The verti ...

  10. 一招MAX降低10倍,现在它是我的了

    一.背景 性能优化是一场永无止境的旅程. 到家门店系统,作为到家核心基础服务之一,门店C端接口有着调用量高,性能要求高的特点. C端服务经过演进,核心接口先查询本地缓存,如果本地缓存没有命中,再查询R ...