先看实例
package main
import (
"log"
"os"
)
func init() {
// 配置日志输出格式
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
// 配置前缀
log.SetPrefix("order:")
// 配置输出位置
logFile, err := os.OpenFile("./test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
log.Panic("打开日志文件异常")
}
log.SetOutput(logFile)
}
func main() {
log.Println("测试")
}
输出结果
运行程序,会创建一个test.log
文件,文件内容如下:
order:2022/11/23 15:46:56 test.go:22: 测试
你可以多运行几次,修改运行main中输出的内容,观察有何效果。
标准库日志log
log包提供了简单的日志功能,该包中定义了一个结构体Logger
,该结构体有一些方法实现日志功能。
Logger结构体
type Logger struct {
mu sync.Mutex // ensures atomic writes; protects the following fields
prefix string // prefix on each line to identify the logger (but see Lmsgprefix)
flag int // properties
out io.Writer // destination for output
buf []byte // for accumulating text to write
isDiscard int32 // atomic boolean: whether out == io.Discard
}
mu
:是一个互斥锁,保证日志写操作的原子性
prefix
:日志的前缀
flag
:日志输入的格式
out
:输出的目标,例如:控制台、或者文件。
buf
:一个缓冲区,提高文本的写速度
isDiscard
:一个标记,是否out==io.Discard
日志的输出格式
日志的输出格式可以通过flag标记来控制,可以使用的常量有如下几个:
const (
Ldate = 1 << iota // the date in the local time zone: 2009/01/23
Ltime // the time in the local time zone: 01:23:23
Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.
Llongfile // full file name and line number: /a/b/c/d.go:23
Lshortfile // final file name element and line number: d.go:23. overrides Llongfile
LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone
Lmsgprefix // move the "prefix" from the beginning of the line to before the message
LstdFlags = Ldate | Ltime // initial values for the standard logger
)
例如,Ldate | Ltime
或者 LstdFlags
,输出日志格式如下:
2009/01/23 01:23:23 message
例如,Ldate | Ltime | Lmicroseconds | Llongfile
,输出日志格式如下:
2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
日志的前缀配置
func Prefix() string // 返回日志的前缀配置
func SetPrefix(prefix string) // 设置日志前缀
日志的输出位置配置
func SetOutput(w io.Writer) {
std.SetOutput(w)
}
输出位置是一个Writer
接口,这意味着实现了改接口的类都可以,可以是标准输出到控制台,也可以是文件,正如我们一开始那个实例所示。
日志堆栈调用深度设置
package main
import (
"log"
)
func main() {
logger := log.Default()
logger.SetFlags(log.Llongfile)
logger.Output(0, "0 calldepth")
logger.Output(1, "1 calldepth")
logger.Output(2, "2 calldepth")
}
运行结果
D:/Environment/go1.19.3/src/log/log.go:185: 0 calldepth
D:/Document/learn/test.go:11: 1 calldepth
D:/Environment/go1.19.3/src/runtime/proc.go:250: 2 calldepth
日志的实例化
实例化相关的语句有:
func New(out io.Writer, prefix string, flag int) *Logger {
l := &Logger{out: out, prefix: prefix, flag: flag}
if out == io.Discard {
l.isDiscard = 1
}
return l
}
var std = New(os.Stderr, "", LstdFlags)
func Default() *Logger { return std }
实例
package main
import (
"log"
"os"
)
func TestLogger() {
var (
logger = log.New(os.Stderr, "logger: ", log.Lshortfile)
)
logger.Print("我就试试不进去")
}
func main() {
TestLogger()
}
运行结果
logger: test.go:17: 我就试试不进去
日志输出方法
纯打印日志
Print[f|ln]
打印日志,抛出panic异常
Panic[f|ln]
打印日志,强制结束程序(os.Exit(1)
),defer
函数不会执行
Fatal[f|ln]
实例
package main
import (
"fmt"
"log"
)
func TestLogPrint() {
//纯粹就是打印日志而已
log.Printf("%s\n", "TestLogPrintf")
log.Println("TestLogPrintln")
}
func TestLogPanic() {
defer fmt.Println("Panic defer")
log.Panicln("TestLogPanic")
//运行后发现defer 被执行了,因此panic是等到函数执行完才结束
}
func TestLogFatal() {
defer fmt.Println("Panic Fatal")
log.Fatalln("TestLogFatal")
//运行后发现defer 没被执行了,fatal 是直接结束程序
}
func main() {
TestLogPrint()
TestLogPanic()
// TestLogFatal()
}
运行结果
执行
TestLogPrint()
TestLogPanic()
2022/11/23 16:06:11 TestLogPrintln
2022/11/23 16:06:11 TestLogPanic
Panic defer
panic: TestLogPanic
goroutine 1 [running]:
log.Panicln({0xc000109f30?, 0xc000109f60?, 0x467805?})
D:/Environment/go1.19.3/src/log/log.go:402 +0x65
main.TestLogPanic()
D:/Document/learn/test.go:17 +0x94
main.main()
D:/Document/learn/test.go:29 +0x1c
仅执行 TestLogFatal()
2022/11/23 16:06:31 TestLogFatal