Go中标准库(log)记录日志

253 阅读2分钟

1. 前言

日志是开发人员的眼睛和耳朵,可以用来跟踪、调试和分析代码。基于此,标准库提供了log包,可以对日志做一些最基本的配置。根据 特殊需要,开发人员还可以自己定制日志记录器。

2. Log包


package test

import (
	"fmt"
	"log"
	"testing"
)

func init() {

	log.SetPrefix("Gopher:")
	log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile)

}

func TestLog(t *testing.T) {
	// Println 写到标准日志记录器
	log.Println("message")

	// Fatalln 在调用 Println()之后会接着调用 os.Exit(1)
	log.Fatalln("fatal message")

	// Panicln 在调用 Println()之后会接着调用 panic()
	log.Panicln("panic message")

	fmt.Println("hello!") //这句代码是不会执行到的,因为程序已经退出或报panic了

}


输出结果:

=== RUN   TestLog
Go-serve:2023/03/27 11:02:34.333911 d:/learnspace/gogo/test/log_test.go:18: message
Go-serve:2023/03/27 11:02:34.355644 d:/learnspace/gogo/test/log_test.go:19: fatal message
FAIL    gogo/test       1.056s

log.Fatalln()log.Panicln() 都会使得测试结果 FAIL

log有三个方法PrintlnFatallnPanicln 来写日志消息。 这些函数也有可以格式化消息的版本,只需要用 f 替换结尾的 lnFatal 系列函数用来写日志 消息,然后使用 os.Exit(1)终止程序。Panic 系列函数用来写日志消息,然后触发一个 panic。 除非程序执行 recover 函数,否则会导致程序打印调用栈后终止。

2.1 设置日志输出格式


func init() {

	log.SetPrefix("Gopher:")
	log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile)

}

通过init()进行日志输出的格式设定

2.1.1 SetFlags参数


const ( 
	// 将下面的位使用或运算符连接在一起,可以控制要输出的信息。没有
	// 办法控制这些信息出现的顺序(下面会给出顺序)或者打印的格式
	// (格式在注释里描述)。这些项后面会有一个冒号:
	// 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
	// 日期: 2009/01/23
	Ldate = 1 << iota
	// 时间: 01:23:23
	Ltime
	// 毫秒级时间: 01:23:23.123123。该设置会覆盖 Ltime 标志
	Lmicroseconds
	// 完整路径的文件名和行号: /a/b/c/d.go:23
	Llongfile
	// 最终的文件名元素和行号: d.go:23
	// 覆盖 Llongfile
	Lshortfile
	// 标准日志记录器的初始值
	LstdFlags = Ldate | Ltime
	) 

log 包有一个很方便的地方就是,这些日志记录器是多 goroutine 安全的。这意味着在多个 goroutine 可以同时调用来自同一个日志记录器的这些函数,而不会有彼此间的写冲突。

2.1.2 SetOutput 指定输出文件

package log

import (
	"log"
	"os"
	"path/filepath"
	"testing"
)

var strWorkDir string
var separator string

func init() {

	strWorkDir, _ = os.Getwd()             //获取文件所在路径
	separator = string(filepath.Separator) //文件地址分隔符

	logFileName := "log.txt"

	logFilePath := filepath.Join(strWorkDir, logFileName)

	var file *os.File

	_, err := os.Stat(logFilePath) // 检查文件是否存在
	if os.IsNotExist(err) {        // 文件不存在则创建
		file, err = os.Create(logFilePath)
		if err != nil {
			panic(err)
		}
	} else if err != nil {
		panic(err)
	} else { // 文件存在则打开
		file, err = os.OpenFile(logFilePath, os.O_APPEND|os.O_WRONLY, 0644)
		if err != nil {
			panic(err)
		}
	}

	log.SetOutput(file)
	log.SetPrefix("Gopher:")
	log.SetFlags(log.Ldate | log.Ltime | log.Llongfile)

}

func TestLog(t *testing.T) {

	// Println 写到标准日志记录器
	log.Println("message...")
	log.Fatalln("fatal...")
	log.Panicln("panicln...")

}

log.txt

Gopher:2023/03/27 17:07:36 d:/learnspace/gogo/test/log/log_test.go:50: message...
Gopher:2023/03/27 17:07:36 d:/learnspace/gogo/test/log/log_test.go:51: fatal...

2.2 定制的日志记录器

要想创建一个定制的日志记录器,需要创建一个 Logger 类型值。可以给每个日志记录器配 置一个单独的目的地,并独立设置其前缀和标志


 logger := log.New(os.Stdout, "Gopher:", log.Lshortfile|log.Ldate|log.Ltime)
 logger.Println("logger message....")
 
Gopher:2023/03/27 17:14:34 d:\learnspace\gogo\test\log\log_test.go:53: logger message....

参考书籍《Go语言实战》