就是记录日志的咯
日志的flag. 用于控制日志显示的格式
// 这个 1 << iota 可以学习一下. 按位控制.
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.
// 有它了就会有Ltime了... 即使不加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 or Lmicroseconds is set, use UTC rather than the local time zone
LstdFlags = Ldate | Ltime // initial values for the standard logger
)
func SetOutput(w io.Writer) // 设置output
func Output(calldepth int, s string) error // 根据calldepth 打印s, calldepth 就是调用的层级. 日志默认都是2
func SetFlags(flag int) // 设置flag
func Flags() int // 获取当前flag
func SetPrefix(prefix string) // 设置当前prefix
func Prefix() string // 获取当前prefix
关于 calldepth 的实验 -- runtime.Caller
-1: /home/work/go/src/runtime/asm.s:40: 1 // Caller底层的汇编调用
0: /home/work/go/src/log/log.go:159: 1 // 调用 runtime.Caller 的位置
1: /home/work/go/src/log/log.go:351: 1 // 调用 runtime.Caller的函数的的位置. Output函数.
2: /home/work/workspace/Go/src/shenshida.com/hello/test/test.go:25: 1 // 我们调用 Output的位置. 所以log.Print 之类的都设置为2
3: /home/work/go/src/runtime/proc.go:201: 1 // 调用main的调用了.
4: /home/work/go/src/runtime/asm_amd64.s:1333: 1 // ...
5: ???:0: 1 // 另一个次元啦...
6: ???:0: 1
// 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
// 写入的地址, 默认是 os.Stderr
buf []byte // for accumulating text to write
}
核心写入方法, 中文注释为我的注释和疑问
func (l *Logger) Output(calldepth int, s string) error {
now := time.Now() // get this early.
// 根据flag获取时间是不是更好呢?并不是都需要时间呀, 想想大多数都需要时间.好像也无所谓啦.
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] // 这是什么神仙操作... 经过下面的代码 [神仙1] 测试发现 其实就是清空了 buf 中的内容, 但是很轻量级因为只是改变了 len = 0 而已.
l.formatHeader(&l.buf, now, file, line)
l.buf = append(l.buf, s...) // 这个是append字符串的时候的 special case
if len(s) == 0 || s[len(s)-1] != '\n' {
l.buf = append(l.buf, '\n')
}
_, err := l.out.Write(l.buf)
return err
}
神仙1:
package main
import (
"fmt"
)
func main() {
a := []byte{'a', 'b', 'c'}
fmt.Print(a)
a = a[:0]
fmt.Println(a, len(a), cap(a))
b := a[:1]
fmt.Println(b[0])
}
这只是简单的日志库, 一般开发环境是不会用的, 会用到封装比较多功能的库, 包括日志等级, 日志轮转等等吧 .
常用的一个日志库 可以参考
go.uber.org/zap https://github.com/uber-go/zap
这个库速度快节省资源吧
https://github.com/natefinch/lumberjack 日志的轮转 rolling
网友评论