Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 实例独立维护自己的输出目标和格式

方法总览:

方法参数返回值描述
Flagsint获取日志标志
Outputcalldepth int, s stringerror输出日志
Prefixstring获取日志前缀
Printv ...interface{}打印日志
Printfformat string, v ...interface{}格式化打印
Printlnv ...interface{}打印一行
Fatalv ...interface{}打印并退出
Fatalfformat string, v ...interface{}格式化打印并退出
Fatallnv ...interface{}打印一行并退出
Panicv ...interface{}打印并 panic
Panicfformat string, v ...interface{}格式化打印并 panic
Paniclnv ...interface{}打印一行并 panic
SetFlagsflag int设置日志标志
SetOutputw io.Writer设置输出目标
SetPrefixprefix 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] '
}

Print

定义:

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 // 标准标志
)

标志详解:

常量格式示例说明
Ldate12026/04/04日期(年/月/日)
Ltime210:30:00时间(时:分:秒)
Lmicroseconds410:30:00.123456微秒精度
Llongfile8/a/b/c/d.go:23完整文件路径
Lshortfile16d.go:23简短文件名
LUTC32-使用 UTC 时间
LstdFlags3`LdateLtime`

示例 - 不同标志组合:

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)
}

七、快速参考

函数总览

函数名参数返回值描述
Flagsint获取日志标志
Outputcalldepth int, s stringerror输出日志
Prefixstring获取日志前缀
Printv ...interface{}打印日志
Printfformat string, v ...interface{}格式化打印
Printlnv ...interface{}打印一行
Fatalv ...interface{}打印并退出
Fatalfformat string, v ...interface{}格式化打印并退出
Fatallnv ...interface{}打印一行并退出
Panicv ...interface{}打印并 panic
Panicfformat string, v ...interface{}格式化打印并 panic
Paniclnv ...interface{}打印一行并 panic
SetFlagsflag int设置日志标志
SetOutputw io.Writer设置输出目标
SetPrefixprefix string设置日志前缀

常量总览

常量描述
Ldate1日期
Ltime2时间
Lmicroseconds4微秒
Llongfile8完整文件路径
Lshortfile16简短文件名
LUTC32UTC 时间
LstdFlags3标准标志

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
LUTCUTC 时间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