一般而言日志可分为事件日志和消息日志两种
- 事件日志记录发生在系统运行过程中的事件,用来审计操作、诊断问题等。对理解复杂系统的运行非常关键。
- 消息日志被应用在比如即时通信中,用来记录来往的消息。
标准的日志定义在IETF中,会记录时间、地点、参与者、起因、简要经过等信息。
日志记录一般格式:[时间][日志级别][地点][参与者][事件][起因]
对于系统日志一般还会定义严重性等级(Severity Level)用来标识该条日志记录的紧要程度
紧要程度 | 日志级别 |
---|---|
0 | EMERGENCY |
1 | ALERT |
2 | CREITICAL |
3 | ERROR |
4 | WARNING |
5 | NOTICE |
6 | INFORMATION |
7 | DEBUG |
log
Golang内置了标准的log
日志库
package main
import "log"
type User struct {
Id int
Name string
}
func main() {
user := User{Id: 1, Name: "admin"}
log.Printf("id = %v, name = %v", user.Id, user.Name)
}
2021/03/31 22:18:33 id = 1, name = admin
Golang默认的日志记录分为两部分,前面是事件发生的时间,后面是事件记录。
log
日志记录器默认是多goroutine
安全的,多个goroutine
可以同时调用来自一个日志记录器的输出函数,而不会发生写冲突。
日志输出
-
log
日志库默认会输出日志到标准错误stderr
-
log
日志库会将每条日志前自动添加上日期和时间 - 若日志不是以换行符为结尾,则会自动补上换行符。
log
日志库提供了三组函数用于输出日志
输出 | 格式化 | 换行 | 描述 |
---|---|---|---|
log.Print | log.Printf | log.Println | 正常输出日志 |
log.Panic | log.Panicf | log.Panicln | 输出日志与相关调用信息后退出程序 |
log.Fatal | log.Fatalf | log.Fatalln | 输出日志后退出程序os.Exit(1)
|
设置前缀
- 调用
log.SetPrefix
会为每条日志文本前添加一个前缀
user := User{Id: 1, Name: "admin"}
log.SetPrefix("[ERROR] ")
log.Printf("id = %v, name = %v", user.Id, user.Name)
[ERROR] 2021/03/31 22:32:31 id = 1, name = admin
设置选项
- 调用
log.SetFlags
为输出的日志添加选项,可同时设置多个,多个|
分割。 - 设置选项可为每条日志输出的文本前增加额外信息
选项 | 描述 |
---|---|
Ldate | 输出当地时区日期 |
Ltime | 输出当地时区时间 |
Lmicroseconds | 输出时间精确到微秒 |
Llongfile | 输出全文件名和行号,包含包名。 |
Lshortfile | 输出当前文件名加行号,不包含报名。 |
LUTC | 若已设置Ldate或Ltime则输出UTC时间,而非当地时区。 |
user := User{Id: 1, Name: "admin"}
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.Llongfile | log.LUTC)
log.SetPrefix("[ERROR] ")
log.Printf("id = %v, name = %v", user.Id, user.Name)
[ERROR] 2021/03/31 14:31:21.368463 D:/go/project/example/main.go:14: id = 1, name = admin
日志记录器
-
logger
是对log
的简单封装,使用logger
可以使日志记录更加便捷。
func log.New(out io.Writer, prefix string, flag int) *Logger
参数 | 类型 | 描述 |
---|---|---|
out | io.Writer | 设置日志数据接入的目的地 |
prefix | string | 每行日志前缀字符串 |
flag | int | 设置日志记录选项 |
user := User{Id: 1, Name: "admin"}
logger := log.New(os.Stdout, "[DEBUG] ", log.Ldate|log.Ltime|log.Lshortfile)
logger.Printf("id = %v, name = %v", user.Id, user.Name)
[DEBUG] 2021/03/31 23:00:03 main.go:17: id = 1, name = admin
例如:将错误记录到日志同时打印到文件
package main
import (
"io"
"log"
"os"
)
var (
Error *log.Logger
)
func init() {
filename := "error.log"
file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
log.Fatalln(err)
}
out := io.MultiWriter(file, os.Stderr)
Error = log.New(out, "[ERROR] ", log.Ldate|log.Ltime|log.Llongfile)
}
func main() {
user := &struct {
Id int
Name string
}{
Id: 1,
Name: "admin",
}
Error.Printf("id = %v, name = %v", user.Id, user.Name)
}
创建文件
file,err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
选项 | 描述 |
---|---|
os.O_CREATE | 若文件不存在则创建新文件 |
os.O_WRONLY | 打开文件的读写权限 |
os.O_APPEND | 写入数据时采用追加而非覆盖的方式 |
例如:
- 指定日志文件保存路径,若目录不存在则创建。
- 向指定目录的日志文件内写入日志,同时打印到标准输出。
package main
import (
"io"
"log"
"os"
)
var (
Error *log.Logger
)
//MakeDir 创建目录
func MakeDir(dir string) (bool, error) {
//判断文件是否存在
_, err := os.Stat(dir)
if err == nil {
return true, nil
}
//递归创建文件夹
err = os.MkdirAll(dir, 0755)
if err != nil {
return false, err
}
return true, nil
}
func init() {
dir := "./runtime/log/"
filename := "error.log"
filepath := dir + filename
ok, err := MakeDir(dir)
if err != nil && !ok {
panic(err)
}
file, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
log.Fatalln(err)
}
out := io.MultiWriter(file, os.Stderr)
Error = log.New(out, "[ERROR] ", log.Ldate|log.Ltime|log.Llongfile)
}
func main() {
user := &struct {
Id int
Name string
}{
Id: 1,
Name: "admin",
}
Error.Printf("id = %v, name = %v", user.Id, user.Name)
}
日志级别
紧急程度 | 级别名称 | 级别方法 | 日志前缀 | 颜色 |
---|---|---|---|---|
0 | fatal | Fatal/Fatalf | [FATAL] | 红色背景色 |
1 | error | Error/Errorf | [ERROR] | 红色前景色 |
2 | warn | Warn/Warnf/Warningf | [WARN] | 杨红背景色 |
3 | info | Info/Infof | [INFO] | 请求前景色 |
4 | debug | Debug/Debugf | [DEBUG] | 黄色前景色 |
有疑问加站长微信联系(非本文作者)