一、log包基础:默认日志器
Go 标准库 log 提供了简单的日志功能,默认日志器(std)可直接使用,无需额外配置。
1. 核心函数
|
函数 |
功能 |
输出示例 |
|
log.Print(v…) |
输出日志(无换行符自动添加) |
2024/11/18 15:00:00 这是一条日志 |
|
log.Println(v…) |
输出日志并换行 |
2024/11/18 15:00:00 这是一条日志 |
|
log.Printf(format, v…) |
格式化输出日志 |
2024/11/18 15:00:00 数值:100 |
|
log.Fatal(v…) |
输出日志后调用 os.Exit(1)(终止程序) |
2024/11/18 15:00:00 致命错误 |
|
log.Panic(v…) |
输出日志后触发 panic(可被 recover 捕获) |
2024/11/18 15:00:00 恐慌错误 |
2. 基础用法示例
package main
import "log"
func main() {
// 基本日志输出
log.Print("这是一条普通日志")
log.Println("这是一条带换行的日志")
log.Printf("这是一条格式化日志,数值:%d", 123)
// 致命错误(程序终止)
// log.Fatal("发生致命错误,程序退出")
// 恐慌错误(触发panic)
// log.Panic("发生恐慌错误")
}
3. 默认日志特性
- 输出格式:日期 时间 日志内容(如 2024/11/18 15:00:00 日志内容);
- 输出目标:默认输出到标准错误流(os.Stderr);
- 线程安全:支持多 goroutine 并发写入,内部有锁保护。
二、日志配置:自定义输出格式与目标
通过 log 包的配置函数,可自定义日志的前缀、输出目标、时间格式等。
1. 核心配置函数
|
函数 |
功能 |
|
log.SetPrefix(prefix string) |
设置日志前缀(会添加到每条日志前) |
|
log.SetFlags(flags int) |
设置日志标志(控制输出内容,如是否显示时间、文件名等) |
|
log.SetOutput(w io.Writer) |
设置日志输出目标(默认是 os.Stderr) |
2. 常用日志标志(flags参数)
|
标志常量 |
含义 |
|
|
log.Ldate |
显示日期(如 2024/11/18) |
|
|
log.Ltime |
显示时间(如 15:00:00) |
|
|
log.Lmicroseconds |
显示微秒级时间(如 15:00:00.123456) |
|
|
log.Llongfile |
显示完整文件名和行号(如 /a/b/c.go:23) |
|
|
log.Lshortfile |
显示短文件名和行号(如 c.go:23) |
|
|
log.LstdFlags |
默认标志(Ldate,Ltime) |
3. 自定义配置示例
package main
import (
"io"
"log"
"os"
)
func main() {
// 1. 设置日志前缀
log.SetPrefix("[INFO] ")
// 2. 设置日志标志(显示日期、时间、微秒、短文件名)
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.Lshortfile)
// 3. 设置日志输出到文件(同时输出到控制台)
logFile, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
log.Fatal("创建日志文件失败:", err)
}
defer logFile.Close() // 确保程序结束时关闭文件
// log.SetOutput(logFile) // 仅输出到文件
// 同时输出到文件和标准输出(io.MultiWriter 合并多个输出目标)
log.SetOutput(io.MultiWriter(logFile, os.Stdout)) // 同时输出到文件和控制台
// 测试日志
log.Println("程序启动")
log.Printf("用户%s登录成功", "admin")
log.Println("日志系统初始化完成")
}
三、创建自定义日志器(log.New)
除了默认日志器,还可通过 log.New 创建多个独立配置的日志器(如区分错误日志、访问日志)。
1.log.New函数定义
func New(out io.Writer, prefix string, flag int) *log.Logger
- 参数:out:日志输出目标(io.Writer 接口,如文件、网络连接);prefix:日志前缀;flag:日志标志(同 SetFlags 参数);
- 返回:自定义日志器指针(*log.Logger),可调用 Print/Println 等方法。
2. 多日志器示例(区分 info 和 error 日志)
四、日志轮转与进阶(标准库局限与扩展)
1. 标准库log的局限
- 不支持日志级别(如 DEBUG/INFO/WARN/ERROR);
- 不支持日志轮转(按大小 / 时间分割日志文件);
- 不支持结构化日志(如 JSON 格式)。
2. 日志轮转简易实现(按文件大小)
package main
import (
"log"
"os"
"time"
)
// rotateLog 检查日志文件大小,超过阈值则轮转
func rotateLog(filePath string, maxSize int64) (*os.File, error) {
// 检查文件是否存在
fileInfo, err := os.Stat(filePath)
if err != nil {
// 如果文件不存在,直接创建新文件
if os.IsNotExist(err) {
return os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
}
// 其他错误返回
return nil, err
}
// 若文件存在且超过最大大小,重命名为备份文件
if fileInfo.Size() >= maxSize {
backupPath := filePath + "." + time.Now().Format("20060102150405")
if err := os.Rename(filePath, backupPath); err != nil {
return nil, err
}
}
// 创建新日志文件
return os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
}
func main() {
logPath := "app.log"
maxSize := int64(1024 * 20) // 最大20kb
logFile, err := rotateLog(logPath, maxSize)
if err != nil {
log.Fatal("日志轮转失败:", err)
}
defer logFile.Close() // 确保程序结束时关闭文件
// 设置日志输出目标
log.SetOutput(logFile)
// 设置日志格式
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.Lshortfile)
log.SetPrefix("[INFO] ")
log.Println("带轮转功能的日志系统已启动")
log.Printf("日志文件路径:%s,最大大小:%d bytes", logPath, maxSize)
// 获取当前日志文件大小
for i:=100; i>=0; i-- {
fileInfo, err := os.Stat(logPath)
log.Println(i)
if err != nil {
log.Printf("获取文件信息失败:%v", err)
} else {
log.Printf("日志文件路径:%s,当前文件大小:%d bytes", logPath, fileInfo.Size())
}
}
}
3. 推荐第三方日志库(解决标准库局限)
- zap:Uber 开源,高性能、结构化日志,支持级别和轮转;
- logrus:类 log4j 风格,支持插件和钩子,易用性好;
- zerolog:极致性能,专注结构化日志,API 简洁。
五、最佳实践
- 生产环境日志输出到文件:避免仅依赖控制台输出,确保日志可持久化;
- 添加必要上下文:日志中包含时间、文件名、行号、模块名等,便于问题定位;
- 区分日志级别:通过第三方库实现 DEBUG/INFO/ERROR 等级别,便于筛选;
- 日志轮转:防止单一日志文件过大(如超过 100MB 轮转),并定期清理旧日志;
- 敏感信息脱敏:日志中避免明文输出密码、Token 等敏感数据;
- 并发安全:多 goroutine 写日志时,确保日志器线程安全(标准库 log 已支持)。
通过 log 包的基础功能和自定义配置,可满足简单日志需求;复杂场景(如级别、轮转)则提议使用第三方库。