Go--日志

一、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()
}

 

posted @ 2023-04-27 17:27  心恩惠动  阅读(86)  评论(0编辑  收藏  举报