一文搞懂Go语言标准库,log

2021-12-27 19:08:28 浏览数 (1)

Go 语言的标准库中提供了一个简单的 log 日志包,它不仅提供了很多函数,还定义了一个包含很多方法的类型 Logger。Logger 会打印每条日志信息的日期、时间,默认输出到标准错误。Fatal 系列函数会在写入日志信息后调用 os.Exit(1)。Panic 系列函数会在写入日志信息后 panic。下面详细介绍下标准库log的基本使用。

函数

Golang 的 log 包主要提供了以下几个具备输出功能的函数:

代码语言:javascript复制
func Fatal(v ...interface{})
func Fatalf(format string, v ...interface{})
func Fatalln(v ...interface{})
func Panic(v ...interface{})
func Panicf(format string, v ...interface{})
func Panicln(v ...interface{})
func Print(v ...interface{})
func Printf(format string, v ...interface{})
func Println(v ...interface{})

示例

代码语言:javascript复制
//l.Output(2, fmt.Sprintf(format, v...))
 v := "普通"
 log.Printf("一条%s日志。n", v)
 
 //l.Output(2, fmt.Sprintln(v...))
 log.Println("一条一条普通日志。")
 
 //l.Output(2, fmt.Sprintln(v...))
 //os.Exit(1)
 log.Fatalln("一条触发fatal的日志。")
 
 //s := fmt.Sprintln(v...)
 //l.Output(2, s)
 //panic(s)
 log.Panicln("一条触发panic的日志。")

运行结果

代码语言:javascript复制
021/12/22 15:01:01 一条普通日志。
2021/12/22 15:01:01 一条普通日志。
2021/12/22 15:01:01 一条触发fatal的日志。

以上这些函数的使用方法和 fmt 包完全相同,具体 fmt 包的介绍上一篇已经介绍过了,>>传送门。通过查看源码可以发现:

  • log.Fatal [ln|f] 实际上是调用的 Printf [ln|f] 之后,又调用了 os.Exit(1) 退出程序。
  • log.Panic [ln|f] 实际上是调用的 Printf [ln|f] 之后,又调用了 panic()函数,抛出一个恐慌。
  • 而 Print[ln|f] 实际上是调用的 Output() 函数。

函数 Output() 的源码:

代码语言:javascript复制
func (l *Logger) Output(calldepth int, s string) error {
 now := time.Now() // get this early.
 var file string
 var line int
 l.mu.Lock()
 defer l.mu.Unlock()
 if l.flag&(Lshortfile|Llongfile) != 0 {
  // Release lock while getting caller info - it's expensive.
  l.mu.Unlock()
  var ok bool
  _, file, line, ok = runtime.Caller(calldepth)
  if !ok {
   file = "???"
   line = 0
  }
  l.mu.Lock()
 }
 l.buf = l.buf[:0]
 l.formatHeader(&l.buf, now, file, line)
 l.buf = append(l.buf, s...)
 if len(s) == 0 || s[len(s)-1] != 'n' {
  l.buf = append(l.buf, 'n')
 }
 _, err := l.out.Write(l.buf)
 return err
}

可以发现:

  1. 函数使用互斥锁来保证多个 goroutine 写日志的安全,且在调用 runtime.Caller() 之前,先释放了互斥锁,等获取到信息后再加锁来保证安全。
  2. 使用 formatHeader() 函数来格式化日志的信息,然后保存到 buf 中,然后再把日志信息追加到 buf 的末尾,然后再通过判断,查看日志是否为空或末尾不是 n,如果是就再把 n 追加到 buf 的末尾,最后将日志信息输出。

配置logger

默认情况下的 logger 只提供了日志的时间信息,log 标准库还提供了一些定制的方法。可以得到更多信息,例如记录该日志的文件名和行号等。

Flags

log标准库中的 Flags 函数会返回标准 logger 的输出配置,而 SetFlags 函数用来设置标准 logger 的输出配置。

代码语言:javascript复制
func Flags() int
func SetFlags(flag int)

log 标准库提供了如下的 flag 选项,它们是一系列定义好的常量。

代码语言:javascript复制
const (
    // 控制输出日志信息的细节,不能控制输出的顺序和格式。
    // 输出的日志在每一项后会有一个冒号分隔:例如2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
    Ldate         = 1 << iota     // 日期:2009/01/23
    Ltime                         // 时间:01:23:23
    Lmicroseconds                 // 微秒级别的时间:01:23:23.123123(用于增强Ltime位)
    Llongfile                     // 文件全路径名 行号: /a/b/c/d.go:23
    Lshortfile                    // 文件名 行号:d.go:23(会覆盖掉Llongfile)
    LUTC                          // 使用UTC时间
    LstdFlags     = Ldate | Ltime // 标准logger的初始值
)

示例

代码语言:javascript复制
func main() {
    log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)
    log.Println("一条很普通的日志。")
}

//输出
//2021/12/22 15:09:08.290930 /box/main.go:7: 一条很普通的日志。

日志前缀 Prefix

Prefix 函数查看标准 logger 的输出前缀,SetPrefix 函数用来设置输出前缀。

代码语言:javascript复制
func Prefix() string
func SetPrefix(prefix string)

示例

代码语言:javascript复制
func main() {
 log.SetPrefix("[info]")
 log.Println("一条普通的日志")
 fmt.Println(log.Prefix())
}

//输出
//[info]2021/12/22 15:15:28 一条普通的日志
//[info]

输出位置 Output

SetOutput 函数用来设置标准 logger 的输出目的地,默认是标准错误输出。

代码语言:javascript复制
func SetOutput(w io.Writer)

示例

代码语言:javascript复制
func main() {
 logFile, err := os.OpenFile("./test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
 if err != nil {
  fmt.Println("open log file failed, err:", err)
  return
 }
 log.SetOutput(logFile)
 log.SetFlags(log.Lshortfile | log.Lmicroseconds | log.Ldate)
 log.SetPrefix("[info]")
 log.Println("一条很普通的日志")
}

自定义logger

lo g标准库中还提供了一个创建新 logger 对象的构造函数 New,支持我们创建自己的logger。

代码语言:javascript复制
func New(out io.Writer, prefix string, flag int) *Logger {
 return &Logger{out: out, prefix: prefix, flag: flag}
}
  • New 创建一个 Logger 对象
  • 参数 out 设置日志信息写入的目的地
  • 参数 prefix 日志前缀,会添加到生成的每一条日志前面
  • 参数 flag 定义日志的属性(时间、文件等等)

示例

代码语言:javascript复制
func main() {
 logger := log.New(os.Stdout, "<info>", log.Lshortfile|log.Lmicroseconds|log.Ldate)
 logger.Println("自定义的 logger 日志")
}

//输出
//<info>2021/12/22 23:30:16.283401 main.go:10: 自定义的 logger 日志

由于 G o内置的 log 库功能有限,在实际的项目中可以根据自己的需要选择使用第三方的日志库,如logrus、zap等。

图片及部分相关技术知识点来源于网络搜索,侵权删!

参考资料:

https://www.jianshu.com/p/66c75589d6b5

https://www.cnblogs.com/flippedxyy/p/15558771.html

0 人点赞