序
本文次要钻研一下 golang 的 log
log
flags
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
)
这几个 flags 能够用来设置 log 的 pattern
logger
type Logger struct {
mu sync.Mutex // ensures atomic writes; protects the following fields
prefix string // prefix on each line to identify the logger (but see Lmsgprefix)
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}
}
func (l *Logger) SetOutput(w io.Writer) {l.mu.Lock()
defer l.mu.Unlock()
l.out = w
}
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
}
func (l *Logger) Printf(format string, v ...interface{}) {l.Output(2, fmt.Sprintf(format, v...))
}
func (l *Logger) Print(v ...interface{}) {l.Output(2, fmt.Sprint(v...)) }
func (l *Logger) Println(v ...interface{}) {l.Output(2, fmt.Sprintln(v...)) }
func (l *Logger) Fatal(v ...interface{}) {l.Output(2, fmt.Sprint(v...))
os.Exit(1)
}
func (l *Logger) Fatalf(format string, v ...interface{}) {l.Output(2, fmt.Sprintf(format, v...))
os.Exit(1)
}
func (l *Logger) Fatalln(v ...interface{}) {l.Output(2, fmt.Sprintln(v...))
os.Exit(1)
}
func (l *Logger) Panic(v ...interface{}) {s := fmt.Sprint(v...)
l.Output(2, s)
panic(s)
}
func (l *Logger) Panicf(format string, v ...interface{}) {s := fmt.Sprintf(format, v...)
l.Output(2, s)
panic(s)
}
func (l *Logger) Panicln(v ...interface{}) {s := fmt.Sprintln(v...)
l.Output(2, s)
panic(s)
}
func (l *Logger) SetFlags(flag int) {l.mu.Lock()
defer l.mu.Unlock()
l.flag = flag
}
func (l *Logger) SetPrefix(prefix string) {l.mu.Lock()
defer l.mu.Unlock()
l.prefix = prefix
}
func (l *Logger) Writer() io.Writer {l.mu.Lock()
defer l.mu.Unlock()
return l.out
}
- log 包默认提供的 Print、Fatal、Panic 办法应用的是 std 规范输入流,它也提供了 New 办法能够自定义输入
- Fatal 相干办法会执行 os.Exit(1),Panic 相干办法会执行 panic,而 panic 执行的是 os.Exit(1)
- f 结尾的办法执行的是 fmt.Sprintf,能够应用 format,ln 结尾的办法执行的是 fmt.Sprintln,会减少换行
实例
package main
import (
"encoding/json"
"errors"
"io"
"log"
"os"
"time"
)
var logFile *os.File
func init() {log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
log.SetPrefix("demo-app")
var err error
logFile, err = os.OpenFile("/tmp/demo.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {log.Fatalln("Failed to open log file")
}
mw := io.MultiWriter(os.Stdout, logFile)
log.SetOutput(mw)
}
type Cart struct {
UserId int `json:"userId"`
SkuId int `json:"skuId"`
}
func closeLogFile() {
if logFile != nil {log.Println("closing log file")
logFile.Close()}
}
func main() {defer closeLogFile()
// log.Panicln("print and then exit with status 2")
// log.Fatalln("print and then exit with status 1")
log.Println("hello")
log.Printf("hello %s", "abc")
//print with var
str1 := "abc"
str2 := "cdf"
log.Println("print with auto space:", str1, "append", str2)
//print error
err1 := errors.New("demo error")
log.Println("error:", err1)
//print time
now1 := time.Now()
log.Println("print time:", now1)
log.Println("print time with format:", now1.Format(time.RFC3339))
//print struct
cart := Cart{
UserId: 1,
SkuId: 123,
}
log.Println("print struct:", cart)
//print json
jsonByte, err := json.Marshal(cart)
if err != nil {log.Fatalln("error:", err)
}
log.Println("print cart json:", string(jsonByte))
//log to file
}
- 这里给 log 设置了 MultiWriter,能够同时往 std 和文件输入
输入实例
demo-app 2020/12/03 23:54:27.245848 main.go:42: hello
demo-app 2020/12/03 23:54:27.245974 main.go:43: hello abc
demo-app 2020/12/03 23:54:27.245981 main.go:48: print with auto space: abc append cdf
demo-app 2020/12/03 23:54:27.245996 main.go:52: error: demo error
demo-app 2020/12/03 23:54:27.246015 main.go:56: print time: 2020-12-03 23:54:27.246001 +0800 CST m=+0.000260928
demo-app 2020/12/03 23:54:27.246022 main.go:57: print time with format: 2020-12-03T23:54:27+08:00
demo-app 2020/12/03 23:54:27.246047 main.go:64: print struct: {1 123}
demo-app 2020/12/03 23:54:27.246109 main.go:71: print cart json: {"userId":1,"skuId":123}
demo-app 2020/12/03 23:54:27.246116 main.go:33: closing log file
小结
- log 包默认提供的 Print、Fatal、Panic 办法应用的是 std 规范输入流,它也提供了 New 办法能够自定义输入
- Fatal 相干办法会执行 os.Exit(1),Panic 相干办法会执行 panic,而 panic 执行的是 os.Exit(1)
- f 结尾的办法执行的是 fmt.Sprintf,能够应用 format,ln 结尾的办法执行的是 fmt.Sprintln,会减少换行
doc
- log