在使用go写一些小程序时,我们没必要引入额外的包,直接使用fmt标准包打印即可:
import "fmt"
func main() {
fmt.Println("line1")
fmt.Print("line2")
fmt.Printf("line%d \n", 3)
str1 := fmt.Sprintln("hello", 3)
str2 := fmt.Sprint("hello ", 1, " 2")
str3 := fmt.Sprintf("hello %d", 1)
fmt.Print(str1, str2, str3)
}
line1
line2line3
hello 3
hello 1 2hello 1
那么,有些场景下,我们希望能同时打印到日志文件中要怎么办呢?
标准库提供了log组件,用法和fmt一致,有3种方式:
import “log"
func main() {
log.Println("line1")
log.Print("line2")
log.Printf("line%d \n", 3)
}
和fmt的区别就是多了时间:
2021/08/25 17:23:47 line1
2021/08/25 17:23:47 line2
2021/08/25 17:23:47 line3
我们通过SetFlag函数,可以设置打印的格式:
// For example, flags Ldate | Ltime (or LstdFlags) produce,
// 2009/01/23 01:23:23 message
// while flags Ldate | Ltime | Lmicroseconds | Llongfile produce,
// 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
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
)
比如,我们只需要时间和文件名:
import “log"
func main() {
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
log.Println("line1")
log.Print("line2")
log.Printf("line%d \n", 3)
}
此时,再次运行,则会打印文件和行号:
2021/08/25 17:27:56 mod_unread_redis.go:32: line1
2021/08/25 17:27:56 mod_unread_redis.go:33: line2
2021/08/25 17:27:56 mod_unread_redis.go:34: line3
log包使用非常简单,默认情况下,只会输出到控制台。
我们可以使用SetOutput改变输出流,比如输出到文件。
先来看一下函数原型,其接收一个io.Writer接口:
// SetOutput sets the output destination for the standard logger.
func SetOutput(w io.Writer) {
// ...
}
那么,我们就可以创建一个文件流设置一下就行了。
// 创建、追加、读写,777,所有权限
f, err := os.OpenFile("log.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
if err != nil {
return
}
defer func() {
f.Close()
}()
log.SetOutput(f)
此时,在运行,我们发现日志会输出到文件,但是控制台没有任何东西输出了。
标准库io包中,有一个MultiWriter,可以把文件流和控制台标准输出流整合到一个io.Writer上,其实现上就是一个数组,在执行写操作时,遍历数组:
// MultiWriter creates a writer that duplicates its writes to all the
// provided writers, similar to the Unix tee(1) command.
//
// Each write is written to each listed writer, one at a time.
// If a listed writer returns an error, that overall write operation
// stops and returns the error; it does not continue down the list.
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的Write函数函数,本质上就是遍历数组,比较巧妙
func (t *multiWriter) Write(p []byte) (n int, err error) {
for _, w := range t.writers {
n, err = w.Write(p)
if err != nil {
return
}
if n != len(p) {
err = ErrShortWrite
return
}
}
return len(p), nil
}
使用方式如下:
func main() {
f, err := os.OpenFile("log.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
if err != nil {
return
}
defer func() {
f.Close()
}()
// 组合一下即可,os.Stdout代表标准输出流
multiWriter := io.MultiWriter(os.Stdout, f)
log.SetOutput(multiWriter)
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
log.Println("line1")
log.Print("line2")
log.Printf("line%d \n", 3)
}
此时,再运行,则会同时输出到控制台和文件中。
2021/08/25 17:38:02 mod_unread_redis.go:42: line1
2021/08/25 17:38:02 mod_unread_redis.go:43: line2
2021/08/25 17:38:02 mod_unread_redis.go:44: line3