Skip to content

日志log

作者: ryan 发布于: 2025/11/28 更新于: 2025/11/28 字数: 0 字 阅读: 0 分钟

Go 标准库的 log 包以极简、稳定、并发安全著称,整个包只有约 600 行代码,却足以应对绝大多数日常日志需求。

使用示例

go
log.Println("普通日志")
log.Fatalln("致命错误")  // 打印后调用 os.Exit(1)
log.Panicln("触发 panic") // 打印后调用 panic()

输出:

go
2025/11/26 10:30:45 普通日志
2025/11/26 10:30:45 致命错误
exit status 1

注意:Fatal 系列会退出进程,Panic 系列可被 recover 捕获。

核心数据结构

go
type Logger struct {
    mu     sync.Mutex // 全局互斥锁,确保并发安全
    prefix string     // 每行日志前缀
    flag   int        // 格式控制标志位
    out    io.Writer  // 输出目标,默认 os.Stderr
    buf    []byte     // 临时缓冲区,减少内存分配
}

整个包的功能全部围绕这一个结构体展开,极致简洁。

全局默认 Logger

Logger 是所有包级函数的真正实现

go
// log/log.go 源码(简化)
var std = New(os.Stderr, "", LstdFlags)

// 所有包级函数都是 std 的薄包装
func Println(v ...any) { std.Output(2, fmt.Sprintln(v...)) }
func Printf(format string, v ...any) { std.Output(2, fmt.Sprintf(format, v...)) }
func Fatal(v ...any)  { std.Output(2, fmt.Sprintln(v...)); os.Exit(1) }
func Panic(v ...any)  { std.Output(2, fmt.Sprintln(v...)); panic(...) }

因此以下三者完全等价

go
log.Println("hello")           // 最常见
log.Default().Println("hello") // 显式获取默认 logger(Go 1.17+)
std.Println("hello")           // 直接使用包级变量(内部就是 *Logger)

工厂函数 New

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

把参数塞进结构体,返回指针。

日志格式标志位

go
const (
    Ldate         = 1 << iota // 2025/11/26
    Ltime                     // 10:30:45
    Lmicroseconds             // 微秒精度 10:30:45.123123
    Llongfile                 // 完整路径 /path/to/main.go:23
    Lshortfile                // 短路径 main.go:23
    LUTC                      // 使用 UTC 时间
)
const LstdFlags = Ldate | Ltime // 默认值

常用组合示例:

go
log.New(os.Stderr, "[INFO] ", Ldate|Ltime|Lmicroseconds|Lshortfile)
// 输出:[INFO] 2025/11/26 10:30:45.123456 main.go:23 hello

方法接收者与调用方式

所有核心方法都是指针接收者 func (l *Logger) Println(...)

std 本身就是*Logger​类型(指针)因此直接可以写为 std.Println(...)

错误示范(能编译但多余)

go
(&std).Println("hi")  // 多此一举
(*std).Println("hi")  // 同上

自定义 Logger

go
// 通常放在包级变量
var logger = log.New(
    os.Stdout,
    "[MYAPP] ",
    log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile,
)

func main() {
    logger.Println("服务器启动")
    logger.Printf("用户 %s%s 登录", user, ip)
    logger.Fatal("配置加载失败")
}

模块化日志示例:

go
// db/logger.go
var DB = log.New(os.Stderr, "[DB] ", log.LstdFlags|log.Lshortfile)

// http/logger.go
var HTTP = log.New(os.Stderr, "[HTTP] ", log.LstdFlags|log.Lshortfile)

全局只有一个 *Logger 实例 , 所有函数都是它的快捷方式