一、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. 【主流技术】详解 Spring Boot 2.7.x 集成 ElasticSearch7.x 全过程(二)

    目录 前言 一.添加依赖 二. yml 配置 三.注入依赖 四.CRUD 常用 API ES 实体类 documents 操作 常见条件查询(重点) 分页查询 排序 构造查询 测试调用 五.文章小结 ...

  2. 文心一言 VS 讯飞星火 VS chatgpt (141)-- 算法导论12.1 1题

    一.用go语言,对于关键字集合(1,4,5,10,16,17,21),分别画出高度为 2.3.4.5和6的二又搜索树. 文心一言: 以下是用Go语言为给定的关键字集合(1, 4, 5, 10, 16, ...

  3. ERP大作业进度(一)

    ERP和进销存的区别 ERP(企业资源计划)和进销存(进货.销售和库存管理)是两个不同的概念,尽管它们在企业管理中通常存在交集.以下是它们之间的主要区别: 范围: ERP:ERP系统是一个综合性的.集 ...

  4. ThreadPoolExecutor线程池内部处理浅析

    我们知道如果程序中并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束时,会因为频繁创建线程而大大降低系统的效率,因此出现了线程池的使用方式,它可以提前创建好线程来执行任务.本文主要通过j ...

  5. 【驱动】串口驱动分析(二)-tty core

    前言 tty这个名称源于电传打字节的简称,在linux表示各种终端,终端通常都跟硬件相对应.比如对应于输入设备键盘鼠标,输出设备显示器的控制终端和串口终端.也有对应于不存在设备的pty驱动.在如此众多 ...

  6. Typora的安装与使用教程

    一.安装 1.下载 下载地址:Typora 官方中文站 二.安装 1.无脑下一步即可. 三.使用教程 1.Typora系统设置 一般导出使用PDF文件比较好,性价比比较高. 2.其他设置 以下所有设置 ...

  7. CTT2023 邮寄

    从广州被邮寄到了苏州.还有点感冒有点咳嗽,体温 37 度.还是来了. Day 0 清早坐 xp 的车,早上坐飞机,中午坐高铁,下午坐大巴,风尘仆仆地赶到了苏州. 飞机上有一套省选题要验,看了两眼,T1 ...

  8. 【问题解决】unable to do port forwarding: socat not found

    问题复现 前阵子应公司要求做华为云平台的调研,写了一篇文档包含将华为云CCE下载kuberctl配置及使用kubectl转发流量到本地的操作. 今天一早上同事就发来一个错误界面,说是Java远程调试转 ...

  9. Python实现贪吃蛇大作战

    贪吃蛇 初始版本 初始版本,只存在基本数据结构--双向队列. 游戏思路 贪吃蛇通过不断得吃食物来增长自身,如果贪吃蛇碰到边界或者自身则游戏失败. 食物是绿色矩形来模拟,坐标为随机数生成,定义一个蛇长变 ...

  10. Redis全文搜索教程之创建索引并关联源数据

    Redis 全文搜索是依赖于 Redis 官方提供的 RediSearch 来实现的.RediSearch 提供了一种简单快速的方法对 hash 或者 json 类型数据的任何字段建立二级索引,然后就 ...