Go标准库日志打印及同时输出到控制台与文件
作者:Go和分布式IM
打印
在使用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包
标准库提供了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
附:日志切割(按文件大小切割、按日期切割)
其实就是每次记录文件的大小,超过了就重新写一个文件。
通过Stat()函数拿到文件的一些信息
open, _:= os.Open("文件名") stat, _, := open.Stat() stat.Size()//拿到文件大小
日期切割:
拿到文件的名称或者检查下有没有当天的日志文件,没有就创建新增。
总结
到此这篇关于Go标准库日志打印及同时输出到控制台与文件的文章就介绍到这了,更多相关Go标准库日志打印及输出内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!