Skip to content

Latest commit

 

History

History
134 lines (109 loc) · 3.99 KB

log.md

File metadata and controls

134 lines (109 loc) · 3.99 KB

Table of Contents generated with DocToc

log

log 包作为 Go 标准库,仅支持日志的基本功能,不支持记录结构化日志、日志切割、Hook 等高级功能,所以更适合小型项目使用,比如一个单文件的脚本。

打印流程

// go1.22.2/src/log/log.go

func Printf(format string, v ...any) {
	std.output(0, 2, func(b []byte) []byte {
		return fmt.Appendf(b, format, v...)
	})
}

使用标准 默认值

// 标准错误输出,并且使用 日期+时间 属性格式
var std = New(os.Stderr, "", LstdFlags)


// 初始化
func New(out io.Writer, prefix string, flag int) *Logger {
	l := new(Logger)
	l.SetOutput(out)
	l.SetPrefix(prefix)
	l.SetFlags(flag)
	return l
}

log.New 函数接收三个参数,分别用来指定:日志输出位置(一个 io.Writer 对象)、日志前缀(字符串,每次打印日志都会跟随输出)、日志属性(定义好的常量)

func (l *Logger) output(pc uintptr, calldepth int, appendOutput func([]byte) []byte) error {
	// 判断是否丢弃
	if l.isDiscard.Load() {
		return nil
	}
    // 获取当前时间
	now := time.Now() 

	// Load prefix and flag once so that their value is consistent within
	// this call regardless of any concurrent changes to their value.
	prefix := l.Prefix()
	flag := l.Flags()

	var file string
	var line int
	if flag&(Lshortfile|Llongfile) != 0 { // 通过位运算来判断是否需要获取文件名和行号
		if pc == 0 {
			var ok bool
			_, file, line, ok = runtime.Caller(calldepth)
			if !ok {
				file = "???"
				line = 0
			}
		} else {
			fs := runtime.CallersFrames([]uintptr{pc})
			f, _ := fs.Next()
			file = f.File
			if file == "" {
				file = "???"
			}
			line = f.Line
		}
	}
    // 复用对象 *[]byte
	buf := getBuffer()
	defer putBuffer(buf)
	// 格式化日志头信息(如:日期时间、文件名和行号、前缀)并写入 buf
	formatHeader(buf, now, prefix, flag, file, line)
	// 追加日志内容到 buf
	*buf = appendOutput(*buf)
	if len(*buf) == 0 || (*buf)[len(*buf)-1] != '\n' {
		*buf = append(*buf, '\n')
	}

	// 加锁,保证并发安全
	l.outMu.Lock()
	defer l.outMu.Unlock()
	// 调用 Logger 对象的 out 属性的 Write 方法输出日志
	_, err := l.out.Write(*buf)
	return err
}

源码分析

结构体

type Logger struct {
	outMu sync.Mutex // 锁,保证并发情况下对其属性操作是原子性的
	out   io.Writer // destination for output

	prefix    atomic.Pointer[string] // 每行日志前缀 (but see Lmsgprefix)
	flag      atomic.Int32           // 属性 properties,用来控制日志输出格式
	isDiscard atomic.Bool // 当 out = io.Discard 是,此值为 true
}

属性 flag

const (
	Ldate         = 1 << iota     // 当前时区日期: 2009/01/23
	Ltime                         // 当前时区时间: 01:23:23
	Lmicroseconds                 // 当前时区时间,精确到微秒 microsecond resolution: 01:23:23.123123.  assumes Ltime.
	Llongfile                     // 全文件名和行号: /a/b/c/d.go:23
	Lshortfile                    // 当前文件名和行号: d.go:23. overrides Llongfile
	LUTC                          // 使用UTC而不是本地市区
	Lmsgprefix                    // move the "prefix" from the beginning of the line to before the message
	LstdFlags     = Ldate | Ltime // 标准初始值
)

参考