在程序中,我们日常需要记录日志,来了解程序的工作状态

为此,标准库提供了log包,进行日志的跟踪记录,传统的CLI程序写到了名为stdout的设备上

作为标准输出

但是我们还需要记录程序的执行细节,为此UNIX还提供了stderr的设备,方便作为日志的记录

这样我们可以分别配置两者的记录,

如果程序没有输出,还可以将一般的日志信息输出到stdout,错误或者警告信息写到stderr

那么在Go中,管理对应输出的,就是log包

我们从log最基础的功能开始,学习如何创建定制日志记录器,记录日志的目的是追踪程序在什么位置做了什么,这就需要通过配置在每个日志项上写的一些嘻嘻

一般的日志记录如下

图片

包含了前缀 时间戳 日志具体由哪个源文件记录的,源文件记录日志所在行,最后是日志消息

这就需要先配置log包,从而完成这种日志输出项

//首先是初始化执行函数,在main函数执行之前

func init() {

//日志设置前缀,前缀建议全大写

log.SetPrefix(“TRACE: “)

//设置日志要显示的信息,有日期,时间,路径和行号,除此外的可选项有

log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile)

}

func main() {

//Println写到标准日志记录

log.Println(“message”)

// Fatalln调用println.然后调用os.exit(1)

log.Fatalln(“fatal message”)

// Panicln 调用println()之后,调用panic()

log.Panicln(“panic message”)

}

上面的代码中,首先我们配置了log的配置

上面的配置中我们使用三个常量

常量的代码如下

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

)

上面的常量中,有一个关键字

iota在常量声明区有一个作用,就是iota可以为每个常量复制相同的表达式直到声明区结束

iota同时会设置自己的初始值为0,然后每次处理常量后,自增1,那么下面的关键字就是

图片

上面的常量表达式,就是每次都是右移一位,计算后的结果如上

然后我们实际使用的时候,就是如下

图片

利用最后数来配置对应的日志输出信息

然后是main函数

里面是如何写消息的

里面有三个函数,Println,Fatalln和Panicln来写日志,函数中有可以格式化消息的版本,只需要使用f替换ln就可以了

Fata;系列函数用于写日志消息,然后使用os.Exit(1)终止函数

Panic系列用来写日志消息,然后触发一个panic,除非程序执行recover函数,不然就是进行终止

log还保证了多goroutine安全的,可以同时的调用同一个日志记录器这个函数,不会有彼此的些冲突

那么,接下来的流程,就是不同等级的日志写到不同的目的地

为了定制这样的一个日志记录器,我们需要创建一个Logger类型的值,给每个日志配置一个单独的目的地和独立的前缀和标志,我们来看下面的代码

// 为了创建定制的日志记录器

package main

import (

“io”

“io/ioutil”

“log”

“os”

)

var (

Trace *log.Logger // 所有

Info *log.Logger // 重要信息

Warning *log.Logger // 告警

Error *log.Logger // 错误

)

func init() {

file, err := os.OpenFile(“errors.txt”,

os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)

if err != nil {

log.Fatalln(“Failed to open error log file:”, err)

}

Trace = log.New(ioutil.Discard,

“TRACE: “,

log.Ldate|log.Ltime|log.Lshortfile)

Info = log.New(os.Stdout,

“INFO: “,

log.Ldate|log.Ltime|log.Lshortfile)

Warning = log.New(os.Stdout,

“WARNING: “,

log.Ldate|log.Ltime|log.Lshortfile)

Error = log.New(io.MultiWriter(file, os.Stderr),

“ERROR: “,

log.Ldate|log.Ltime|log.Lshortfile)

}

func main() {

Trace.Println(“I have something standard to say”)

Info.Println(“Special Information”)

Warning.Println(“There is something you need to know about”)

Error.Println(“Something has failed”)

}

我们首先创建了4种不同的Logger类型的指针,分别命名为了Trace Info Warning Error

我们看下变量名很简短,,但是含义是有的

init函数代码创建了每个Logger类型的值

利用的是Logger类中端的New函数

func New(out io.Writer, prefix string, flag int) *Logger {

return &Logger{out: out, prefix: prefix, flag: flag}

}

分别传入了日志的目的地地址.要求是实现了io.Wirter的目的地,

然后是prefix的前缀,最后则是日志的标志

我们在创建Trace的时候,使用了

ioutil.Discard

这是个独特的属性,对于Linux,是写到了devNull中,因此对其写入不会有任何的动作,但是会成功返回

然后info和warning级别都使用了stdout作为日志的输出\

当然,除了stdout 还有 stdin stderr这三个标准的输入输出级别

最后是关于Error的级别,利用了一个特殊的函数,io包中的MultiWriter函数

func MultiWriter(writers …Writer) Writer {

allWriters := make([]Writer, 0, len(writers))

for _, w := range writers {

if mw, ok := w.(*multiWriter); ok {

allWriters = append(allWriters, mw.writers…)

} else {

allWriters = append(allWriters, w)

}

}

return &multiWriter{allWriters}

}

传入多个实现了io.Writer接口的值,返回一个io.Wirter,这样在返回的Writer中写入的时候,就会给所有的Writer做一个输出,我们就可以同时写到文件和stderr中了

总结一下

log包的实现,基于对记录日志的需求长时间的实践和记录得出的,标准包的log就给出了记录日志的所有功能,我们也推荐使用这个包

发表评论

邮箱地址不会被公开。 必填项已用*标注