Go log 包详解
概述
log 包实现了一个简单的日志记录包,提供带时间戳和可选的文件/行号信息的日志输出功能。它定义了一个 Logger 类型,支持多个输出目标,并提供了包级别的全局默认 logger。该包广泛用于应用程序的日志记录、调试和错误跟踪。
包导入
import "log"
基本使用
1. 使用全局 Logger
package main
import (
"log"
)
func main() {
// 基本日志输出
log.Println("这是一条日志信息")
// 带格式的日志
name := "张三"
age := 25
log.Printf("姓名:%s, 年龄:%d\n", name, age)
// 输出:2026/04/04 10:30:00 这是一条日志信息
// 输出:2026/04/04 10:30:00 姓名:张三,年龄:25
}
2. 创建自定义 Logger
package main
import (
"log"
"os"
)
func main() {
// 创建自定义 logger
logger := log.New(os.Stdout, "自定义前缀:", log.Ldate|log.Ltime)
logger.Println("这是一条自定义日志")
// 输出:2026/04/04 10:30:00 自定义前缀:这是一条自定义日志
}
3. 设置日志选项
package main
import (
"log"
"os"
)
func main() {
// 设置日志前缀和选项
log.SetPrefix("[APP] ")
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
log.Println("带完整信息的日志")
// 输出:[APP] 2026/04/04 10:30:00 log_test.go:15: 带完整信息的日志
}
一、核心类型
Logger
定义:
type Logger struct {
// 包含未导出的字段
}
说明:
- 功能:日志记录器类型
- 字段:所有字段均为未导出(内部实现)
- 用途:提供线程安全的日志输出功能
- 特点:每个 Logger 实例独立维护自己的输出目标和格式
方法总览:
| 方法 | 参数 | 返回值 | 描述 |
|---|---|---|---|
Flags | 无 | int | 获取日志标志 |
Output | calldepth int, s string | error | 输出日志 |
Prefix | 无 | string | 获取日志前缀 |
Print | v ...interface{} | 无 | 打印日志 |
Printf | format string, v ...interface{} | 无 | 格式化打印 |
Println | v ...interface{} | 无 | 打印一行 |
Fatal | v ...interface{} | 无 | 打印并退出 |
Fatalf | format string, v ...interface{} | 无 | 格式化打印并退出 |
Fatalln | v ...interface{} | 无 | 打印一行并退出 |
Panic | v ...interface{} | 无 | 打印并 panic |
Panicf | format string, v ...interface{} | 无 | 格式化打印并 panic |
Panicln | v ...interface{} | 无 | 打印一行并 panic |
SetFlags | flag int | 无 | 设置日志标志 |
SetOutput | w io.Writer | 无 | 设置输出目标 |
SetPrefix | prefix string | 无 | 设置日志前缀 |
示例 - 完整使用:
package main
import (
"bytes"
"fmt"
"log"
"os"
)
func main() {
// 示例 1:创建自定义 logger
var buf bytes.Buffer
logger := log.New(&buf, "DEBUG: ", log.Ldate|log.Ltime)
logger.Println("调试信息")
fmt.Print(buf.String())
// 示例 2:同时输出到多个目标
file, _ := os.Create("app.log")
defer file.Close()
multiWriter := io.MultiWriter(os.Stdout, file)
logger.SetOutput(multiWriter)
logger.Println("同时输出到控制台和文件")
// 示例 3:获取和设置标志
flags := logger.Flags()
fmt.Printf("当前标志:%d\n", flags)
logger.SetFlags(flags | log.Lmicroseconds)
logger.Println("带微秒的日志")
// 示例 4:获取和设置前缀
prefix := logger.Prefix()
fmt.Printf("当前前缀:%s\n", prefix)
logger.SetPrefix("[INFO] ")
logger.Println("修改前缀后的日志")
}
二、包级别函数
Flags
定义:
func Flags() int
说明:
- 功能:获取全局 logger 的标志
- 返回值:
int- 当前标志值 - 用途:查看当前日志格式设置
示例:
package main
import (
"fmt"
"log"
)
func main() {
flags := log.Flags()
fmt.Printf("默认标志:%d\n", flags)
// 输出:默认标志:3 (Ldate|Ltime)
}
Output
定义:
func Output(calldepth int, s string) error
说明:
- 功能:输出一条日志记录
- 参数:
calldepth- 调用栈深度(用于获取正确的文件名和行号)s- 要输出的日志字符串
- 返回值:
error- 输出错误(如果有) - 用途:底层日志输出,通常不直接使用
示例:
package main
import (
"log"
)
func customLog() {
// calldepth=2 表示跳过当前函数和 Output 本身
log.Output(2, "自定义日志")
}
func main() {
customLog()
}
Prefix
定义:
func Prefix() string
说明:
- 功能:获取全局 logger 的前缀
- 返回值:
string- 当前前缀字符串 - 用途:查看当前日志前缀设置
示例:
package main
import (
"fmt"
"log"
)
func main() {
prefix := log.Prefix()
fmt.Printf("默认前缀:'%s'\n", prefix)
// 输出:默认前缀:''
log.SetPrefix("[APP] ")
prefix = log.Prefix()
fmt.Printf("设置后前缀:'%s'\n", prefix)
// 输出:设置后前缀:'[APP] '
}
定义:
func Print(v ...interface{})
说明:
- 功能:打印日志(类似 fmt.Print)
- 参数:
v ...interface{}- 要打印的值 - 返回值:无
- 特点:自动添加时间戳和前缀
示例:
package main
import (
"log"
)
func main() {
log.Print("普通日志")
log.Print("多", "个", "值")
log.Print(123, " ", 45.67)
// 输出:
// 2026/04/04 10:30:00 普通日志
// 2026/04/04 10:30:00 多个值
// 2026/04/04 10:30:00 123 45.67
}
Printf
定义:
func Printf(format string, v ...interface{})
说明:
- 功能:格式化打印日志(类似 fmt.Printf)
- 参数:
format- 格式字符串v ...interface{}- 格式化参数
- 返回值:无
- 特点:自动添加时间戳和前缀
示例:
package main
import (
"log"
)
func main() {
name := "张三"
age := 25
score := 95.5
log.Printf("姓名:%s", name)
log.Printf("年龄:%d", age)
log.Printf("姓名:%s, 年龄:%d, 分数:%.1f", name, age, score)
// 输出:
// 2026/04/04 10:30:00 姓名:张三
// 2026/04/04 10:30:00 年龄:25
// 2026/04/04 10:30:00 姓名:张三,年龄:25, 分数:95.5
}
Println
定义:
func Println(v ...interface{})
说明:
- 功能:打印一行日志(类似 fmt.Println)
- 参数:
v ...interface{}- 要打印的值 - 返回值:无
- 特点:自动在末尾添加换行符
示例:
package main
import (
"log"
)
func main() {
log.Println("第一行日志")
log.Println("第二行日志")
// 输出:
// 2026/04/04 10:30:00 第一行日志
// 2026/04/04 10:30:00 第二行日志
}
Fatal
定义:
func Fatal(v ...interface{})
说明:
- 功能:打印日志并调用
os.Exit(1) - 参数:
v ...interface{}- 要打印的值 - 返回值:无(程序会退出)
- 用途:记录致命错误并终止程序
示例:
package main
import (
"log"
)
func main() {
// 模拟错误情况
err := loadConfig()
if err != nil {
log.Fatal("无法加载配置文件:", err)
// 程序在这里终止,后续代码不会执行
}
log.Println("配置文件加载成功")
}
func loadConfig() error {
return fmt.Errorf("文件不存在")
}
Fatalf
定义:
func Fatalf(format string, v ...interface{})
说明:
- 功能:格式化打印日志并调用
os.Exit(1) - 参数:
format- 格式字符串v ...interface{}- 格式化参数
- 返回值:无(程序会退出)
- 用途:记录致命错误并终止程序
示例:
package main
import (
"log"
)
func main() {
config := "config.json"
err := fmt.Errorf("文件不存在")
if err != nil {
log.Fatalf("加载配置文件 %s 失败:%v", config, err)
// 程序在这里终止
}
log.Println("配置加载成功")
}
Fatalln
定义:
func Fatalln(v ...interface{})
说明:
- 功能:打印一行日志并调用
os.Exit(1) - 参数:
v ...interface{}- 要打印的值 - 返回值:无(程序会退出)
- 用途:记录致命错误并终止程序
示例:
package main
import (
"log"
)
func main() {
err := initialize()
if err != nil {
log.Fatalln("初始化失败:", err)
// 程序在这里终止
}
}
func initialize() error {
return fmt.Errorf("初始化错误")
}
Panic
定义:
func Panic(v ...interface{})
说明:
- 功能:打印日志并调用
panic - 参数:
v ...interface{}- 要打印的值 - 返回值:无(会触发 panic)
- 用途:记录错误并触发 panic 恢复机制
示例:
package main
import (
"log"
)
func main() {
defer func() {
if r := recover(); r != nil {
log.Println("捕获到 panic:", r)
}
}()
// 触发 panic
log.Panic("发生严重错误")
// 这行代码不会执行
log.Println("不会输出")
}
Panicf
定义:
func Panicf(format string, v ...interface{})
说明:
- 功能:格式化打印日志并调用
panic - 参数:
format- 格式字符串v ...interface{}- 格式化参数
- 返回值:无(会触发 panic)
- 用途:记录错误并触发 panic 恢复机制
示例:
package main
import (
"log"
)
func main() {
defer func() {
if r := recover(); r != nil {
log.Println("恢复:", r)
}
}()
code := 500
log.Panicf("服务器错误,状态码:%d", code)
}
Panicln
定义:
func Panicln(v ...interface{})
说明:
- 功能:打印一行日志并调用
panic - 参数:
v ...interface{}- 要打印的值 - 返回值:无(会触发 panic)
- 用途:记录错误并触发 panic 恢复机制
示例:
package main
import (
"log"
)
func main() {
defer func() {
if r := recover(); r != nil {
log.Println("捕获 panic")
}
}()
log.Panicln("发生不可恢复的错误")
}
SetFlags
定义:
func SetFlags(flag int)
说明:
- 功能:设置全局 logger 的标志
- 参数:
flag- 标志值(可使用位运算组合) - 返回值:无
- 用途:自定义日志输出格式
示例:
package main
import (
"log"
)
func main() {
// 设置多种标志
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
log.Println("带完整信息的日志")
// 输出:2026/04/04 10:30:00 log_test.go:12: 带完整信息的日志
// 添加微秒
log.SetFlags(log.Flags() | log.Lmicroseconds)
log.Println("带微秒的日志")
// 输出:2026/04/04 10:30:00.123456 log_test.go:16: 带微秒的日志
}
SetOutput
定义:
func SetOutput(w io.Writer)
说明:
- 功能:设置全局 logger 的输出目标
- 参数:
w- io.Writer 接口实现 - 返回值:无
- 用途:将日志输出到文件、网络等
- 默认:标准错误输出(os.Stderr)
示例:
package main
import (
"log"
"os"
)
func main() {
// 输出到文件
file, err := os.Create("app.log")
if err != nil {
log.Fatal("创建日志文件失败:", err)
}
defer file.Close()
log.SetOutput(file)
log.Println("这条日志会写入文件")
// 输出到多个目标
log.SetOutput(io.MultiWriter(os.Stdout, file))
log.Println("这条日志同时输出到控制台和文件")
}
SetPrefix
定义:
func SetPrefix(prefix string)
说明:
- 功能:设置全局 logger 的前缀
- 参数:
prefix- 前缀字符串 - 返回值:无
- 用途:为日志添加统一标识
示例:
package main
import (
"log"
)
func main() {
log.SetPrefix("[APP] ")
log.Println("应用启动")
// 输出:[APP] 2026/04/04 10:30:00 应用启动
log.SetPrefix("[ERROR] ")
log.Println("发生错误")
// 输出:[ERROR] 2026/04/04 10:30:00 发生错误
}
三、常量
日志标志常量
定义:
const (
Ldate = 1 << iota // 日期:2009/01/23
Ltime // 时间:01:23:23
Lmicroseconds // 微秒:01:23:23.123123
Llongfile // 完整文件路径和行号:/a/b/c/d.go:23
Lshortfile // 简短文件名和行号:d.go:23
LUTC // 使用 UTC 时间
LstdFlags = Ldate | Ltime // 标准标志
)
标志详解:
| 常量 | 值 | 格式示例 | 说明 |
|---|---|---|---|
Ldate | 1 | 2026/04/04 | 日期(年/月/日) |
Ltime | 2 | 10:30:00 | 时间(时:分:秒) |
Lmicroseconds | 4 | 10:30:00.123456 | 微秒精度 |
Llongfile | 8 | /a/b/c/d.go:23 | 完整文件路径 |
Lshortfile | 16 | d.go:23 | 简短文件名 |
LUTC | 32 | - | 使用 UTC 时间 |
LstdFlags | 3 | `Ldate | Ltime` |
示例 - 不同标志组合:
package main
import (
"log"
)
func main() {
// 示例 1:只显示日期
log.SetFlags(log.Ldate)
log.Println("只显示日期")
// 输出:2026/04/04 只显示日期
// 示例 2:只显示时间
log.SetFlags(log.Ltime)
log.Println("只显示时间")
// 输出:10:30:00 只显示时间
// 示例 3:日期 + 时间(默认)
log.SetFlags(log.Ldate | log.Ltime)
log.Println("日期 + 时间")
// 输出:2026/04/04 10:30:00 日期 + 时间
// 示例 4:带微秒
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
log.Println("带微秒")
// 输出:2026/04/04 10:30:00.123456 带微秒
// 示例 5:带简短文件名
log.SetFlags(log.Lshortfile)
log.Println("带文件名")
// 输出:log_test.go:23 带文件名
// 示例 6:带完整路径
log.SetFlags(log.Llongfile)
log.Println("带完整路径")
// 输出:/home/user/project/log_test.go:27 带完整路径
// 示例 7:UTC 时间
log.SetFlags(log.Ltime | log.LUTC)
log.Println("UTC 时间")
// 输出:02:30:00 UTC 时间
}
四、典型示例
示例 1:日志输出到文件
package main
import (
"log"
"os"
)
func main() {
// 创建日志文件
file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
log.Fatal("创建日志文件失败:", err)
}
defer file.Close()
// 设置输出到文件
log.SetOutput(file)
// 设置日志格式
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
// 记录日志
log.Println("应用启动")
log.Println("处理请求...")
log.Println("应用关闭")
}
示例 2:多级别日志
package main
import (
"io"
"log"
"os"
)
// 创建不同级别的 logger
var (
Debug *log.Logger
Info *log.Logger
Warning *log.Logger
Error *log.Logger
)
func init() {
// Debug 级别
Debug = log.New(os.Stdout, "[DEBUG] ", log.Ldate|log.Ltime|log.Lshortfile)
// Info 级别
Info = log.New(os.Stdout, "[INFO] ", log.Ldate|log.Ltime|log.Lshortfile)
// Warning 级别
Warning = log.New(os.Stdout, "[WARN] ", log.Ldate|log.Ltime|log.Lshortfile)
// Error 级别
Error = log.New(os.Stderr, "[ERROR] ", log.Ldate|log.Ltime|log.Lshortfile)
}
func main() {
Debug.Println("调试信息")
Info.Println("普通信息")
Warning.Println("警告信息")
Error.Println("错误信息")
}
示例 3:同时输出到多个目标
package main
import (
"io"
"log"
"os"
)
func main() {
// 创建日志文件
file, _ := os.Create("app.log")
defer file.Close()
// 同时输出到控制台和文件
multiWriter := io.MultiWriter(os.Stdout, file)
log.SetOutput(multiWriter)
log.SetFlags(log.Ldate | log.Ltime)
log.Println("这条日志会同时输出到控制台和文件")
}
示例 4:条件日志
package main
import (
"log"
"os"
)
var debugMode bool
func init() {
// 根据环境变量决定是否开启调试
debugMode = os.Getenv("DEBUG") == "true"
if !debugMode {
// 关闭调试日志(输出到 /dev/null)
log.SetOutput(io.Discard)
}
}
func debug(format string, v ...interface{}) {
if debugMode {
log.Printf("DEBUG: "+format, v...)
}
}
func main() {
debug("这是调试信息,只有 DEBUG=true 时才输出")
log.Println("这是普通日志,总是输出")
}
示例 5:带时间戳的日志轮转
package main
import (
"fmt"
"io"
"log"
"os"
"time"
)
// RotatingWriter 日志轮转写入器
type RotatingWriter struct {
dir string
current *os.File
createdAt time.Time
}
func NewRotatingWriter(dir string) (*RotatingWriter, error) {
rw := &RotatingWriter{
dir: dir,
createdAt: time.Now(),
}
return rw, rw.rotate()
}
func (rw *RotatingWriter) rotate() error {
if rw.current != nil {
rw.current.Close()
}
filename := fmt.Sprintf("%s/%s.log", rw.dir, time.Now().Format("2006-01-02_15-04-05"))
file, err := os.Create(filename)
if err != nil {
return err
}
rw.current = file
return nil
}
func (rw *RotatingWriter) Write(p []byte) (n int, err error) {
// 每小时轮转一次
if time.Since(rw.createdAt) > time.Hour {
if err := rw.rotate(); err != nil {
return 0, err
}
rw.createdAt = time.Now()
}
return rw.current.Write(p)
}
func main() {
os.MkdirAll("logs", 0755)
writer, _ := NewRotatingWriter("logs")
log.SetOutput(writer)
log.SetFlags(log.Ldate | log.Ltime)
// 模拟日志写入
for i := 0; i < 10; i++ {
log.Printf("日志记录 %d", i)
time.Sleep(time.Second)
}
}
示例 6:自定义日志格式
package main
import (
"fmt"
"io"
"log"
"os"
"time"
)
// CustomFormatter 自定义日志格式器
type CustomFormatter struct {
prefix string
out io.Writer
}
func (f *CustomFormatter) Write(p []byte) (n int, err error) {
timestamp := time.Now().Format("2006-01-02 15:04:05.000")
_, err = fmt.Fprintf(f.out, "%s %s %s", timestamp, f.prefix, string(p))
if err != nil {
return 0, err
}
return len(p), nil
}
func main() {
// 创建自定义格式器
formatter := &CustomFormatter{
prefix: "[APP]",
out: os.Stdout,
}
log.SetOutput(formatter)
log.Println("自定义格式的日志")
// 输出:2026-04-04 10:30:00.123 [APP] 自定义格式的日志
}
五、最佳实践
1. 选择合适的日志级别
// 使用不同的 logger 实例处理不同级别
var (
DebugLogger = log.New(io.Discard, "[DEBUG] ", log.LstdFlags)
InfoLogger = log.New(os.Stdout, "[INFO] ", log.LstdFlags)
ErrorLogger = log.New(os.Stderr, "[ERROR] ", log.LstdFlags)
)
// 根据环境启用不同级别
if os.Getenv("DEBUG") == "true" {
DebugLogger.SetOutput(os.Stdout)
}
2. 使用文件日志
func setupFileLogger(filename string) error {
file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return err
}
log.SetOutput(file)
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
return nil
}
3. 避免性能问题
// 不好的做法:总是构造日志字符串
log.Printf("处理请求 ID=%s, 用户=%s, 时间=%d", id, user, time.Now().Unix())
// 好的做法:使用条件判断
if logEnabled {
log.Printf("处理请求 ID=%s, 用户=%s, 时间=%d", id, user, time.Now().Unix())
}
4. 错误处理
// 使用 Fatal 处理不可恢复的错误
file, err := os.Open("config.json")
if err != nil {
log.Fatalf("无法打开配置文件:%v", err)
}
// 使用 Panic 处理需要 recover 的情况
if criticalErr != nil {
log.Panicf("严重错误:%v", criticalErr)
}
5. 日志脱敏
func logRequest(password, token string) {
// 脱敏敏感信息
maskedPassword := maskString(password)
maskedToken := maskString(token)
log.Printf("请求:password=%s, token=%s", maskedPassword, maskedToken)
}
func maskString(s string) string {
if len(s) <= 4 {
return "****"
}
return s[:2] + "****" + s[len(s)-2:]
}
六、与其他包配合
1. 与 os 包配合
package main
import (
"log"
"os"
)
func main() {
// 输出到标准错误
log.SetOutput(os.Stderr)
// 输出到标准输出
log.SetOutput(os.Stdout)
// 输出到文件
file, _ := os.Create("app.log")
defer file.Close()
log.SetOutput(file)
}
2. 与 io 包配合
package main
import (
"io"
"log"
"os"
)
func main() {
// 多路输出
log.SetOutput(io.MultiWriter(os.Stdout, os.Stderr))
// 丢弃输出(用于禁用日志)
log.SetOutput(io.Discard)
// 缓冲输出
buffered := bufio.NewWriter(os.Stdout)
log.SetOutput(buffered)
buffered.Flush()
}
3. 与 fmt 包配合
package main
import (
"fmt"
"log"
)
func main() {
// 组合使用
fmt.Println("普通输出")
log.Println("日志输出")
// 在日志中使用 fmt 格式化
msg := fmt.Sprintf("用户 %s 登录", "张三")
log.Println(msg)
}
七、快速参考
函数总览
| 函数名 | 参数 | 返回值 | 描述 |
|---|---|---|---|
Flags | 无 | int | 获取日志标志 |
Output | calldepth int, s string | error | 输出日志 |
Prefix | 无 | string | 获取日志前缀 |
Print | v ...interface{} | 无 | 打印日志 |
Printf | format string, v ...interface{} | 无 | 格式化打印 |
Println | v ...interface{} | 无 | 打印一行 |
Fatal | v ...interface{} | 无 | 打印并退出 |
Fatalf | format string, v ...interface{} | 无 | 格式化打印并退出 |
Fatalln | v ...interface{} | 无 | 打印一行并退出 |
Panic | v ...interface{} | 无 | 打印并 panic |
Panicf | format string, v ...interface{} | 无 | 格式化打印并 panic |
Panicln | v ...interface{} | 无 | 打印一行并 panic |
SetFlags | flag int | 无 | 设置日志标志 |
SetOutput | w io.Writer | 无 | 设置输出目标 |
SetPrefix | prefix string | 无 | 设置日志前缀 |
常量总览
| 常量 | 值 | 描述 |
|---|---|---|
Ldate | 1 | 日期 |
Ltime | 2 | 时间 |
Lmicroseconds | 4 | 微秒 |
Llongfile | 8 | 完整文件路径 |
Lshortfile | 16 | 简短文件名 |
LUTC | 32 | UTC 时间 |
LstdFlags | 3 | 标准标志 |
Logger 方法总览
| 方法 | 描述 |
|---|---|
Flags() | 获取标志 |
Output() | 输出日志 |
Prefix() | 获取前缀 |
Print/Printf/Println | 打印日志 |
Fatal/Fatalf/Fatalln | 打印并退出 |
Panic/Panicf/Panicln | 打印并 panic |
SetFlags() | 设置标志 |
SetOutput() | 设置输出 |
SetPrefix() | 设置前缀 |
日志级别对比
| 方法 | 行为 | 使用场景 |
|---|---|---|
Print 系列 | 仅输出日志 | 普通信息记录 |
Fatal 系列 | 输出日志 + os.Exit(1) | 不可恢复的错误 |
Panic 系列 | 输出日志 + panic() | 需要 recover 的严重错误 |
标志组合示例
| 组合 | 效果 | 示例输出 |
|---|---|---|
Ldate | Ltime | 日期 + 时间 | 2026/04/04 10:30:00 |
Lshortfile | 文件名 + 行号 | main.go:23 |
Ldate | Ltime | Lshortfile | 完整信息 | 2026/04/04 10:30:00 main.go:23 |
Lmicroseconds | 微秒精度 | 10:30:00.123456 |
LUTC | UTC 时间 | 10:30:00 UTC |
八、注意事项
1. Fatal 和 Panic 的区别
// Fatal: 调用 os.Exit(1),不执行 defer
defer fmt.Println("不会执行")
log.Fatal("程序退出")
// Panic: 触发 panic,执行 defer 中的 recover
defer func() {
if r := recover(); r != nil {
fmt.Println("捕获到 panic")
}
}()
log.Panic("触发 panic")
2. 并发安全
// log 包是并发安全的
// 多个 goroutine 可以同时使用同一个 logger
go log.Println("goroutine 1")
go log.Println("goroutine 2")
3. 性能考虑
// 避免在日志中执行耗时操作
log.Printf("处理完成,耗时:%d", expensiveOperation())
// 应该先判断是否需要日志
if debugEnabled {
log.Printf("调试信息:%s", debugInfo)
}
4. 输出缓冲
// 使用缓冲输出时,记得 flush
buffered := bufio.NewWriter(os.Stdout)
log.SetOutput(buffered)
// 程序结束前 flush
buffered.Flush()
5. 全局 logger 的限制
// 全局 logger 不适合多模块使用
// 应该为每个模块创建独立的 logger
// 不好的做法
func module1() {
log.Println("模块 1") // 使用全局 logger
}
// 好的做法
var module1Logger = log.New(os.Stdout, "[模块 1] ", log.LstdFlags)
func module1() {
module1Logger.Println("模块 1")
}
九、完整示例:日志系统
package main
import (
"fmt"
"io"
"log"
"os"
"time"
)
// Logger 日志系统
type Logger struct {
debug *log.Logger
info *log.Logger
warn *log.Logger
error *log.Logger
enabled map[string]bool
}
// NewLogger 创建日志系统
func NewLogger(output io.Writer, enableDebug bool) *Logger {
flags := log.Ldate | log.Ltime | log.Lshortfile
l := &Logger{
enabled: make(map[string]bool),
}
// Debug
l.enabled["debug"] = enableDebug
if enableDebug {
l.debug = log.New(output, "[DEBUG] ", flags)
} else {
l.debug = log.New(io.Discard, "[DEBUG] ", flags)
}
// Info
l.enabled["info"] = true
l.info = log.New(output, "[INFO] ", flags)
// Warning
l.enabled["warn"] = true
l.warn = log.New(output, "[WARN] ", flags)
// Error
l.enabled["error"] = true
l.error = log.New(os.Stderr, "[ERROR] ", flags)
return l
}
// Debug 调试日志
func (l *Logger) Debug(format string, v ...interface{}) {
if l.enabled["debug"] {
l.debug.Printf(format, v...)
}
}
// Info 信息日志
func (l *Logger) Info(format string, v ...interface{}) {
l.info.Printf(format, v...)
}
// Warn 警告日志
func (l *Logger) Warn(format string, v ...interface{}) {
l.warn.Printf(format, v...)
}
// Error 错误日志
func (l *Logger) Error(format string, v ...interface{}) {
l.error.Printf(format, v...)
}
func main() {
// 创建日志系统
logger := NewLogger(os.Stdout, true)
// 使用示例
logger.Debug("调试信息:%s", "test")
logger.Info("应用启动")
logger.Warn("资源不足")
logger.Error("连接失败:%v", fmt.Errorf("network error"))
// 模拟业务逻辑
for i := 0; i < 5; i++ {
logger.Debug("处理请求 %d", i)
time.Sleep(100 * time.Millisecond)
}
logger.Info("应用关闭")
}
最后更新: 2026-04-04
Go 版本: 1.21+
包文档: https://pkg.go.dev/log