go语言日志

Go日志

一、log包

log日志包会默认为输出带上时间,我们也可以自定义我们的日志抬头信息

1
2
3
4
// 定义抬头信息为:时间+文件名+源代码所在行号
func init(){
log.SetFlags(log.Ldate | log.Lshortfile)
}

log包提供的可定义的选项常量

1
2
3
4
5
6
7
8
9
const (
Ldate = 1 << iota //日期示例: 2009/01/23
Ltime //时间示例: 01:23:23
Lmicroseconds //毫秒示例: 01:23:23.123123.
Llongfile //绝对路径和行号: /a/b/c/d.go:23
Lshortfile //文件和行号: d.go:23.
LUTC //日期时间转为0时区的
LstdFlags = Ldate | Ltime //Go提供的标准抬头信息
)

也可以设置前缀

1
2
3
func init(){
log.SetPrefix(" [userCenter] ")
}

log包除了 Print 系列的函数,还有Fatal、Panic 系列的函数。

Fatal 系列函数表示程序遇到了致命的错误,需要退出,这时候使用 Fatal 记录日志后,然后程序退出

Panic 系列函数表示先记录日志,然后调用 panic 函数抛出一个 panic,这时候除了使用 recover 函数,否则程序就会打印错误堆栈信息,然后程序终止。

设置输出位置,实现一个 io.Writer 接口的类型即可

定制自己的日志
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var (
Debug *log.Logger
Info *log.Logger
Warning *log.Logger
Error *log.Logger
)

func init(){
errFile,err := os.OpenFile("./log/log/errors.log",os.O_CREATE | os.O_WRONLY | os.O_APPEND,0666)
if err != nil {
log.Fatalln("打开日志文件失败",err)
}
Info = log.New(os.Stdout,"Info:",log.Ldate | log.Ltime | log.Lshortfile)
Warning = log.New(os.Stdout,"Warning:",log.Ldate | log.Ltime | log.Lshortfile)
Error = log.New(io.MultiWriter(os.Stderr,errFile),"Error:",log.Ldate | log.Ltime | log.Lshortfile)
}

func main(){
Info.Println("hello:","world")
Warning.Println("hello:","world")
Error.Println("nihao")
}

io.MultiWriter 函数可以包装多个 io.Writer 为一个 io.Writer,这样就可以达到同时对多个 io.Writer 输出日志的目的

logger使用起来非常简单,但是不支持 INFO/DEBUG等多个级别;没有切割日志的功能等等。

二、zap包

zap 是非常快、结构化、分日志级别的Go日志库
安装:go get -u go.uber.org/zap

配置 zap Logger

Zap提供了两种类型的日志记录器—Sugared LoggerLogger

在性能很好但不是很关键的上下文中,使用SugaredLogger。它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录。

在每一微秒和每一次内存分配都很重要的上下文中,使用Logger。它甚至比SugaredLogger更快,内存分配次数也更少,但它只支持强类型的结构化日志记录。

  1. Logger
  • 通过调用zap.NewProduction()/zap.NewDevelopment()或者zap.Example()创建一个Logger。
  • 上面的每一个函数都将创建一个logger。唯一的区别在于它将记录的信息不同。例如production logger默认记录调用函数信息、日期和时间等。
  • 通过Logger调用Info/Error等。
  • 默认情况下日志都会打印到应用程序的console界面。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var logger *zap.Logger

func main() {
InitLogger()
defer logger.Sync()
simpleHttpGet("www.google.com")
simpleHttpGet("http://www.google.com")
}

func InitLogger() {
logger, _ = zap.NewProduction()
}

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()
}
}
  1. Sugared Logger

现在让我们使用Sugared Logger来实现相同的功能。

  • 大部分的实现基本都相同。
  • 惟一的区别是,我们通过调用主logger的. Sugar()方法来获取一个SugaredLogger
  • 然后使用SugaredLoggerprintf格式记录语句

下面是修改过后使用SugaredLogger代替Logger的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var sugarLogger *zap.SugaredLogger

func main() {
InitLogger()
defer sugarLogger.Sync()
simpleHttpGet("www.google.com")
simpleHttpGet("http://www.google.com")
}

func InitLogger() {
logger, _ := zap.NewProduction()
sugarLogger = logger.Sugar()
}

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

定制 Logger

  1. 将日志写入文件而不是终端

    使用 zap.New(…) 方法来手动传递所有配置。

    1
    func New(core zapcore.Core, options ...Option) *Logger

    zapcore.Core 需要三个配置— Encoder、WriteSyncer、LogLevel

    • Encoder:编码器 (如何写入日志),使用 NewJSONEncoder() ,并使用预先设置的 ProductionEncoderConfig()。

      1
      zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
    • WriterSyncer:指定日志将写到哪里去,使用 zapcore.AddSync() 函数并且将新打开的文件句柄传进去

      1
      2
      file,_ := os.Create("./test.log")
      writeSyncer := zapcore.AddSync(file)
    • Log Level:那种级别的日志将被写入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    var sugarLogger *zap.SugaredLogger
    func InitLogger() {
    writeSyncer := getLogWriter()
    encoder := getEncoder()
    core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)

    logger := zap.New(core)
    sugarLogger = logger.Sugar()
    }

    func getEncoder() zapcore.Encoder {
    return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
    // 将JSON Encoder 更改为普通 Encoder
    return zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig())
    }

    func getLogWriter() zapcore.WriteSyncer {
    file, _ := os.Create("./test.log")
    return zapcore.AddSync(file)
    }
    • 接下来修改时间编码器
    • 在日志文件中使用大写字母记录日志级别
    1
    2
    3
    4
    5
    6
    func getEncoder() zapcore.Encoder {
    encoderConfig := zap.NewProductionEncoderConfig()
    encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
    encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
    return zapcore.NewConsoleEncoder(encoderConfig)
    }
    • 添加将调用函数信息记录到日志中的功能
    1
    logger := zap.New(core,zap.AddCaller())

    最终

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    var sugarLogger *zap.SugaredLogger

    func InitLogger2(){
    writeSyncer := getLogwriter()
    encoder := getEncoder()
    core := zapcore.NewCore(encoder,writeSyncer,zapcore.DebugLevel)

    logger := zap.New(core, zap.AddCaller())
    sugarLogger = logger.Sugar()
    }

    func getEncoder() zapcore.Encoder {
    encoderConfig := zap.NewProductionEncoderConfig()
    encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
    encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
    return zapcore.NewConsoleEncoder(encoderConfig)
    }
    func getLogwriter()zapcore.WriteSyncer{
    file,_ := os.Create("./log/zap/test.log")
    return zapcore.AddSync(file)
    }

    func simpleHttpGet2(url string) {
    resp, err := http.Get(url)
    if err != nil {
    sugarLogger.Error(
    "Error fetching url..",
    zap.String("url", url),
    zap.Error(err))
    } else {
    sugarLogger.Info(
    "Success..",
    zap.String("statusCode", resp.Status),
    zap.String("url", url))
    resp.Body.Close()
    }
    }

    func main(){
    InitLogger2()
    defer sugarLogger.Sync()
    simpleHttpGet2("http://www.google.com")
    }
  2. 使用 Lumberjack 进行日志切割归档

    zap本书不提供日志切割,可以使用 Lumberjack

    安装:go get -u github.com/natefinch/lumberjack

    修改 getLogWriter() 函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    func getLogWriter() zapcore.WriteSyncer {
    lumberJackLogger := &lumberjack.Logger{
    Filename: "./test.log",
    MaxSize: 10,
    MaxBackups: 5,
    MaxAge: 30,
    Compress: false,
    }
    return zapcore.AddSync(lumberJackLogger)
    }

    Lumberjack Logger采用以下属性作为输入:

    • Filename: 日志文件的位置
    • MaxSize:在进行切割之前,日志文件的最大大小(以MB为单位)
    • MaxBackups:保留旧文件的最大个数
    • MaxAges:保留旧文件的最大天数
    • Compress:是否压缩/归档旧文件

测试最终结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package main

import (
"net/http"

"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

var sugarLogger *zap.SugaredLogger

func main() {
InitLogger()
defer sugarLogger.Sync()
simpleHttpGet("www.sogo.com")
simpleHttpGet("http://www.sogo.com")
}

func InitLogger() {
writeSyncer := getLogWriter()
encoder := getEncoder()
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)

logger := zap.New(core, zap.AddCaller())
sugarLogger = logger.Sugar()
}

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: "./test.log",
MaxSize: 1,
MaxBackups: 5,
MaxAge: 30,
Compress: false,
}
return zapcore.AddSync(lumberJackLogger)
}

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

参考:https://www.liwenzhou.com/posts/Go/zap/