Go日志处理

摘要

Go中日志处理的一些包的用法

log

log模块主要提供了三类接口

  • Print
  • Panic
  • Fatal

Print

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"log"
)

func main(){
arr := []int {2,3}
log.Print("Print array ",arr,"\n")
log.Println("Println array",arr)
log.Printf("Printf array with item [%d,%d]\n",arr[0],arr[1])
}

// 输出
2018/11/14 10:52:16 Print array [2 3]
2018/11/14 10:52:16 Println array [2 3]
2018/11/14 10:52:16 Printf array with item [2,3]

Fatal

log.Fatal接口会先将日志内容打印到标准输出,接着调用系统的 os.exit(1)接口,退出程序并返回状态 1 。

但是有一点需要注意,由于是直接调用系统接口退出,defer函数不会被调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"fmt"
"log"
)

func test_deferfatal(){
defer func() {
fmt.Println("--first--")
}()
log.Fatalln("test for defer Fatal")
}

func main() {
test_deferfatal()
}
// 输出
2018/11/14 10:55:23 test for defer Fatal
exit status 1

Panic

log.Panic接口会把日志内容刷到标准错误后调用panic函数

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
package main

import (
"fmt"
"log"
)

func test_deferpanic(){
defer func() {
fmt.Println("--first--")
if err := recover(); err != nil {
fmt.Println(err)
}
}()
log.Panicln("test for defer Panic")
defer func() {
fmt.Println("--second--")
}()
}

func main() {
test_deferpanic()
}
// 输出
2018/11/14 10:57:11 test for defer Panic
--first--
test for defer Panic
  • 首先输出了test for defer Panic
  • 然后第一个defer函数被调用了并输出了--first--
  • 但是第二个defer函数并没有输出

可见在Panic之后声明的defer是不会执行的。

自定义Logger类型

log.Logger提供了一个New方法用来创建对象

1
2
3
4
5
6
7
8
//定义logger, 传入参数 文件,前缀字符串,flag标记
func New(out io.Writer, prefix string, flag int) *Logger

//设置flag格式
func SetFlags(flag int)

//配置log的输出格式
func SetPrefix(prefix string)

该函数一共有三个参数:

  • 输出位置out,是一个io.Writer对象,该对象可以是一个文件也可以是实现了该接口的对象。通常我们可以用这个来指定日志输出到哪个文件。
  • prefix我们在前面已经看到,就是在日志内容前面的东西。我们可以将其置为[Info][Warning]等来帮助区分日志级别。
  • flags是一个选项,显示日志开头的东西,可选的值有:
1
2
3
4
5
6
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
LstdFlags = Ldate | Ltime // 日期和时间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"log"
"os"
)
func main(){
// 定义一个文件
fileName := "ll.log"
logFile,err := os.Create(fileName)
defer logFile.Close()
if err != nil {
log.Fatalln("open file error !")
}
// 创建一个日志对象
debugLog := log.New(logFile,"[Debug]",log.LstdFlags)
debugLog.Println("A debug message here")
//配置一个日志格式的前缀
debugLog.SetPrefix("[Info]")
debugLog.Println("A Info Message here ")
//配置log的Flag参数
debugLog.SetFlags(debugLog.Flags() | log.LstdFlags)
debugLog.Println("A different prefix")
}

github.com/wonderivan/logger

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

package main

import "github.com/wonderivan/logger"

func main() {
// 配置logger,如果不配置时默认为控制台输出,等级为DEBG
logger.SetLogger(`{"Console": {"level": "DEBG"}`)
// 配置详细见 下文

// 设置完成后,即可在控制台和日志文件app.log中看到如下输出
// 由于默认输出,只会在控制台输出Debug及其以上日志,所以该条不会输出
logger.Trace("this is Trace")
logger.Debug("this is Debug")
logger.Info("this is Info")
logger.Warn("this is Warn")
logger.Error("this is Error")
logger.Crit("this is Critical")
logger.Alert("this is Alert")
logger.Emer("this is Emergency")

}

日志等级

当前日志输出等级共8种,从0-7对应的等级由高到底,当配置为某个输出等级时,只有大于等于该等级的日志才会输出。不同的输出适配器支持不同的日志等级配置:

等级 配置 释义 控制台颜色
0 EMER 系统级紧急,比如磁盘出错,内存异常,网络不可用等 红色底
1 ALRT 系统级警告,比如数据库访问异常,配置文件出错等 紫色
2 CRIT 系统级危险,比如权限出错,访问异常等 蓝色
3 EROR 用户级错误 红色
4 WARN 用户级警告 黄色
5 INFO 用户级重要 天蓝色
6 DEBG 用户级调试 绿色
7 TRAC 用户级基本输出 绿色

配置说明

logger当前支持控制台、文件、网络3种方式适配器输出,可以通过各自的参数进行设置,该logger支持多个方式同时输出,如果未配置某项适配器时,则不初始化也不会输出到该适配器。

通过调用logger.SetLogger(config string)方法设置参数,config支持json配置,也支持指定内容为json配置的文件路径,例如:

1
2
3
4
// 通过配置参数直接配置
logger.SetLogger(`{"Console": {"level": "DEBG"}`)
// 通过配置文件配置
logger.SetLogger("/home/log.json")

配置参数说明

默认配置为只控制台输出DEBG及其以上日志。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"TimeFormat":"2006-01-02 15:04:05", // 输出日志开头时间格式
"Console": { // 控制台日志配置
"level": "TRAC", // 控制台日志输出等级
"color": true // 控制台日志颜色开关
},
"File": { // 文件日志配置
"filename": "app.log", // 初始日志文件名
"level": "TRAC", // 日志文件日志输出等级
"daily": true, // 跨天后是否创建新日志文件,当append=true时有效
"maxlines": 1000000, // 日志文件最大行数,当append=true时有效
"maxsize": 1, // 日志文件最大大小,当append=true时有效
"maxdays": -1, // 日志文件有效期
"append": true, // 是否支持日志追加
"permit": "0660" // 新创建的日志文件权限属性
},
"Conn": { // 网络日志配置
"net":"tcp", // 日志传输模式
"addr":"10.1.55.10:1024", // 日志接收服务器
"level": "Warn", // 网络日志输出等级
"reconnect":true, // 网络断开后是否重连
"reconnectOnMsg":false, // 发送完每条消息后是否断开网络
}
}

时间格式

时间类型 时间格式
ANSIC “Mon Jan _2 15:04:05 2006”
UnixDate “Mon Jan _2 15:04:05 MST 2006”
RubyDate “Mon Jan 02 15:04:05 -0700 2006”
RFC822 “02 Jan 06 15:04 MST”
RFC822Z “02 Jan 06 15:04 -0700”
RFC850 “Monday, 02-Jan-06 15:04:05 MST”
RFC1123 “Mon, 02 Jan 2006 15:04:05 MST”
RFC1123Z “Mon, 02 Jan 2006 15:04:05 -0700”
RFC3339 “2006-01-02T15:04:05Z07:00”
RFC3339Nano “2006-01-02T15:04:05.999999999Z07:00”
Kitchen “3:04PM”
Stamp “Jan _2 15:04:05”
StampMilli “Jan _2 15:04:05.000”
StampMicro “Jan _2 15:04:05.000000”
StampNano “Jan _2 15:04:05.000000000”
RFC3339Nano1 “2006-01-02 15:04:05.999999999 -0700 MST”
DEFAULT “2006-01-02 15:04:05”

时间格式打印

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
========RFC1123Z time format========
Thu, 02 Aug 2018 18:48:04 +0800 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug RFC1123Z
========Stamp time format========
Aug 2 18:48:04 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug Stamp
========StampMilli time format========
Aug 2 18:48:04.489 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug StampMilli
========StampNano time format========
Aug 2 18:48:04.490002155 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug StampNano
========RubyDate time format========
Thu Aug 02 18:48:04 +0800 2018 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug RubyDate
========RFC822 time format========
02 Aug 18 18:48 CST [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug RFC822
========RFC822Z time format========
02 Aug 18 18:48 +0800 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug RFC822Z
========RFC1123 time format========
Thu, 02 Aug 2018 18:48:04 CST [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug RFC1123
========RFC3339 time format========
2018-08-02T18:48:04+08:00 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug RFC3339
========RFC3339Nano time format========
2018-08-02T18:48:04.490377325+08:00 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug RFC3339Nano
========ANSIC time format========
Thu Aug 2 18:48:04 2018 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug ANSIC
========UnixDate time format========
Thu Aug 2 18:48:04 CST 2018 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug UnixDate
========RFC850 time format========
Thursday, 02-Aug-18 18:48:04 CST [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug RFC850
========Kitchen time format========
6:48PM [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug Kitchen
========StampMicro time format========
Aug 2 18:48:04.490662 [DEBG] [github.com/wonderivan/logger/log_test.go:115] Debug StampMicro

其他

  • 当日志文件配置项append为true时,如果当前写入的日志发生跨天(daily为true)或超过最大限制时,会创建一个新文件,原有文件格式被重命名为: ****.xxxx-xx-xx.xxx.xxx 格式,例如:
    • 当向app.log写入日志时,触发了创建新文件操作,则将app.log重命名为 app.2018-01-01.001.log, 如果此时app.2018-01-01.001.log已经存在,则将刚才的app.log重命名为 app.2018-01-01.002.log,以此类推。
  • logger package默认初始化了全局的defaultLogger,所以直接调用logger.SetLogger的其实是给该对象设置输出参数,如果想要使用自定义的logger对象,需要通过logger.NewLogger()进行初始化。
  • 网络配置中的reconnectOnMsgtrue时,每条发送一条消息都会重连一次网络日志中心,适用于写日志频率极低的情况下的调用,可以减少长时间网络连接而占用资源。但强烈不建议平常使用时设置为true,这将会导致调用方反复的网络重连,极大增加资源消耗和延迟。
  • conn网络输出适配器经过ELK集成环境的测试验证,通过该方式发送的日志,能够正常通过ElecsearchKibana检索和分析

github.com/op/go-logging

https://godoc.org/github.com/op/go-logging

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package main

import (
"fmt"
"github.com/op/go-logging"
"os"
)

var log = logging.MustGetLogger("example")

// General:
// %{id} Sequence number for log message (uint64).
// %{pid} Process id (int)
// %{time} Time when log occurred (time.Time)
// %{level} Log level (Level)
// %{module} Module (string)
// %{program} Basename of os.Args[0] (string)
// %{message} Message (string)
// %{longfile} Full file name and line number: /a/b/c/d.go:23
// %{shortfile} Final file name element and line number: d.go:23
// %{callpath} Callpath like main.a.b.c...c "..." meaning recursive call ~. meaning truncated path
// %{color} ANSI color based on log level
// Experimental:
// %{longpkg} Full package path, eg. github.com/go-logging
// %{shortpkg} Base package path, eg. go-logging
// %{longfunc} Full function name, eg. littleEndian.PutUint32
// %{shortfunc} Base function name, eg. PutUint32
// %{callpath} Call function path, eg. main.a.b.c
var format = logging.MustStringFormatter(
`%{color}%{time:15:04:05.000} %{longfunc} > [%{level:.4s}] %{id:03x}%{color:reset} %{message}`,
)
var format_noc = logging.MustStringFormatter(
`%{time:15:04:05.000} %{longfunc} > [%{level:.4s}] %{id:03x} %{message}`,
)

type Password string

func (p Password) Redacted() interface{} {
return logging.Redact(string(p))
}

func main() {
// 打开日志文件
// 第二个参数为打开文件的模式,可选如下:
/*
O_RDONLY // 只读模式打开文件
O_WRONLY // 只写模式打开文件
O_RDWR // 读写模式打开文件
O_APPEND // 写操作时将数据附加到文件尾部
O_CREATE // 如果不存在将创建一个新文件
O_EXCL // 和O_CREATE配合使用,文件必须不存在
O_SYNC // 打开文件用于同步I/O
O_TRUNC // 如果可能,打开时清空文件
*/
// 第三个参数为文件权限,请参考linux文件权限,664在这里为八进制,代表:rw-rw-r--
logFile, err := os.OpenFile("./log.txt", os.O_WRONLY | os.O_CREATE | os.O_APPEND,0664)
if err != nil{
fmt.Println(err)
}
backend1 := logging.NewLogBackend(logFile, "", 0)
backend2 := logging.NewLogBackend(os.Stderr, "", 0)

backend2Formatter := logging.NewBackendFormatter(backend2, format)
backend1Formatter := logging.NewBackendFormatter(backend1, format_noc)
//backend1Leveled := logging.AddModuleLevel(backend1)
//backend1Leveled.SetLevel(logging.INFO, "")

logging.SetBackend(backend1Formatter, backend2Formatter)

log.Debugf("debug %s", Password("secret"))
log.Info("info")
log.Notice("notice")
log.Warning("warning")
log.Error("error")
log.Critical("critical")
}

github.com/sirupsen/logrus

https://www.cnblogs.com/xuange306/p/9507760.html

https://blog.csdn.net/wangjunsheng/article/details/80754156

https://www.jianshu.com/p/5fac8bed4505