Day14-fmt包与日志log包 | 青训营笔记

104 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 14 天

1.fmt包格式化I/O

在fmt包,有关格式化输入输出的方法就两大类:Scan Print ,分别在scan.go print.go 文件中。

print.go文件中定义了如下函数:

func Printf(format string, a ...interface{}) (n int, err error)
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
func Sprintf(format string, a ...interface{}) string

func Print(a ...interface{}) (n int, err error)
func Fprint(w io.Writer, a ...interface{}) (n int, err error)
func Sprint(a ...interface{}) string

func Println(a ...interface{}) (n int, err error)
func Fprintln(w io.Writer, a ...interface{}) (n int, err error)
func Sprintln(a ...interface{}) string

我个人为了记忆而将这9个函数按照两个维度来说明,基本上可以说明白了。

如果把”Print”理解为核心关键字,那么后面跟的后缀有”f”和”ln”以及””,着重的是输出内容的最终结果;

  • 如果后缀是”f”, 则指定了format
  • 如果后缀是”ln”, 则有换行符
Println、Fprintln、Sprintln 输出内容时会加上换行符;
Print、Fprint、Sprint 输出内容时不加上换行符;
Printf、Fprintf、Sprintf 按照指定格式化文本输出内容

如果把”Print”理解为核心关键字,那么前面的前缀有”F”和”S”以及””,着重的是输出内容的目标(终端)

  • 如果前缀是”F”, 则指定了io.Writer
  • 如果前缀是”S”, 则是输出到字符串
Print、Printf、Println 输出内容到标准输出os.Stdout;
Fprint、Fprintf、Fprintln 输出内容到指定的io.Writer;
Sprint、Sprintf、Sprintln 输出内容到字符串。

scan.go文件中定义了如下函数:

func Scanf(format string, a ...interface{}) (n int, err error)
func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error)
func Sscanf(str string, format string, a ...interface{}) (n int, err error)

func Scan(a ...interface{}) (n int, err error)
func Fscan(r io.Reader, a ...interface{}) (n int, err error)
func Sscan(str string, a ...interface{}) (n int, err error)

func Scanln(a ...interface{}) (n int, err error)
func Fscanln(r io.Reader, a ...interface{}) (n int, err error)
func Sscanln(str string, a ...interface{}) (n int, err error)

这9个函数可以扫描格式化文本以生成值。同样也可以按照两个维度来说明。

如果把”Scan”理解为核心关键字,那么后面跟的后缀有”f”和”ln”以及””,着重的是输入内容的结果;

  • 如果后缀是”f”, 则指定了format
  • 如果后缀是”ln”, 则有换行符
Scanln、Fscanln、Sscanln 读取到换行时停止,并要求一次提供一行所有条目;
Scan、Fscan、Sscan 读取内容时不关注换行;
Scanf、Fscanf、Sscanf 根据格式化文本读取。

如果把”Scan”理解为核心关键字,那么前面的前缀有”F”和”S”以及””,着重的是输入内容的来源(终端);

  • 如果前缀是”F”, 则指定了io.Reader
  • 如果前缀是”S”, 则是从字符串读取
Scan、Scanf、Scanln 从标准输入os.Stdin读取文本;
Fscan、Fscanf、Fscanln 从指定的io.Reader接口读取文本;
Sscan、Sscanf、Sscanln 从一个参数字符串读取文本。

2.日志log包

Go语言标准包中有日志功能,对应在log包中。主要结构体是:

type Logger struct {
    mu sync.Mutex // ensures atomic writes; protects the following fields
    prefix string // prefix to write at beginning of each line
    flag int // properties
    out io.Writer // destination for output
    buf []byte // for accumulating text to write
}

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

在log包中通过New函数得到一个Logger结构体指针,这个函数的三个参数分别是out,prefifix,flflagpfefifix可以指定日志信息的前缀,比如“[Debug]”等,一般根据实际需要定义,可根据情况随时通过SetPrefifix()函数修改。flflag是日志的前缀信息(在prefifix之后),包括可配置的时间格式等,一般默认为LstdFlags就可以了。out是日志输出的目标,只要实现了io.Writer接口就可以作为out,log包中默认指定stderr为out,所以log包默认都是输出到标准设备。

var std = New(os.Stderr, "", LstdFlags)
func Println(v ...interface{}) {
    std.Output(2, fmt.Sprintln(v...))
}

也可以按照上面的思路,把日志信息写入到文件。

logfile, err := os.OpenFile("my.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
    log.Fatalln("fail to create log file!")
}
defer logfile.Close()

l:=log.New(logfile, "", log.LstdFlags)
l.Println("test")
num:=5
l.Println("test %d",num)

因为logfifile已经实现了io.Writer,所以这里用做out,日志信息被写入到文件。log的方法Printf()可以把信息按照一定格式来写入。另外,在写入日志信息时都有加入并发锁,这是musync.Mutex的作用。

最后,log包的日志功能基本上能满足一般的开发需要,但相对还是比较简单,缺少日志分层控制,缺少对json格式的支持等,所以如果有需要灵活定制或大并发、大吞吐量的日志开发需求,建议考虑使用其他方法或途径来实现。