flyfei

调试,分析工具(pprof,日志库)

1. 语法讲解

Go语言提供了强大的调试和性能分析工具链。

2. 应用场景

3. 编程实例

场景:Web服务性能分析和调试

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "math/rand"
    "net/http"
    _ "net/http/pprof"
    "os"
    "runtime"
    "time"
    
    "github.com/sirupsen/logrus"
)

var logger = logrus.New()

// 用户结构
type User struct {
    ID       int    `json:"id"`
    Name     string `json:"name"`
    Email    string `json:"email"`
    Active   bool   `json:"active"`
    CreateAt string `json:"created_at"`
}

// 内存缓存模拟
var userCache = make(map[int]*User)

// 初始化日志
func initLogger() {
    logger.SetFormatter(&logrus.JSONFormatter{
        TimestampFormat: "2006-01-02 15:04:05",
    })
    
    // 输出到文件和控制台
    file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err == nil {
        logger.SetOutput(file)
    } else {
        logger.Info("无法创建日志文件,使用标准输出")
    }
    
    // 设置日志级别
    logger.SetLevel(logrus.DebugLevel)
}

// 模拟数据库查询
func getUserFromDB(id int) (*User, error) {
    // 模拟查询延迟
    time.Sleep(time.Millisecond * time.Duration(rand.Intn(100)))
    
    user := &User{
        ID:       id,
        Name:     fmt.Sprintf("用户%d", id),
        Email:    fmt.Sprintf("user%d@example.com", id),
        Active:   id%2 == 0,
        CreateAt: time.Now().Format("2006-01-02 15:04:05"),
    }
    
    logger.WithFields(logrus.Fields{
        "user_id": id,
        "action":  "db_query",
    }).Debug("数据库查询完成")
    
    return user, nil
}

// 获取用户信息(带缓存)
func getUserHandler(w http.ResponseWriter, r *http.Request) {
    start := time.Now()
    
    // 记录请求开始
    logger.WithFields(logrus.Fields{
        "method": r.Method,
        "path":   r.URL.Path,
        "ip":     r.RemoteAddr,
    }).Info("收到请求")
    
    // 解析用户ID
    id := 1
    if r.URL.Query().Get("id") != "" {
        _, err := fmt.Sscanf(r.URL.Query().Get("id"), "%d", &id)
        if err != nil {
            logger.WithFields(logrus.Fields{
                "error": err,
                "id":    r.URL.Query().Get("id"),
            }).Warn("无效的用户ID")
            
            http.Error(w, `{"error": "无效的用户ID"}`, http.StatusBadRequest)
            return
        }
    }
    
    // 检查缓存
    if user, exists := userCache[id]; exists {
        logger.WithFields(logrus.Fields{
            "user_id": id,
            "action":  "cache_hit",
        }).Debug("缓存命中")
        
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(user)
        return
    }
    
    // 缓存未命中,查询数据库
    logger.WithFields(logrus.Fields{
        "user_id": id,
        "action":  "cache_miss",
    }).Debug("缓存未命中")
    
    user, err := getUserFromDB(id)
    if err != nil {
        logger.WithFields(logrus.Fields{
            "error":   err,
            "user_id": id,
        }).Error("查询用户失败")
        
        http.Error(w, `{"error": "用户不存在"}`, http.StatusNotFound)
        return
    }
    
    // 写入缓存
    userCache[id] = user
    
    // 记录内存使用
    var m runtime.MemStats
    runtime.ReadMemStats(&m)
    
    // 返回响应
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(user)
    
    // 记录请求完成
    duration := time.Since(start)
    logger.WithFields(logrus.Fields{
        "user_id":      id,
        "duration_ms":  duration.Milliseconds(),
        "alloc_mb":     m.Alloc / 1024 / 1024,
        "cache_size":   len(userCache),
    }).Info("请求处理完成")
}

// 模拟内存泄漏的函数
func simulateMemoryLeak() {
    go func() {
        var leak [][]byte
        for {
            // 每秒钟泄漏1MB内存
            leak = append(leak, make([]byte, 1024*1024))
            time.Sleep(time.Second)
            
            if len(leak) > 10 { // 防止无限增长影响系统
                leak = leak[:5]
            }
        }
    }()
}

// 性能测试接口
func heavyComputation(w http.ResponseWriter, r *http.Request) {
    start := time.Now()
    
    // 模拟CPU密集型计算
    result := 0
    for i := 0; i < 1000000; i++ {
        result += i * i
    }
    
    // 模拟内存分配
    data := make([]int, 10000)
    for i := range data {
        data[i] = i * i
    }
    
    duration := time.Since(start)
    
    response := map[string]interface{}{
        "result":   result,
        "duration": duration.String(),
        "data_len": len(data),
    }
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(response)
    
    logger.WithFields(logrus.Fields{
        "action":      "heavy_computation",
        "duration_ms": duration.Milliseconds(),
    }).Info("计算完成")
}

func main() {
    // 初始化
    initLogger()
    rand.Seed(time.Now().UnixNano())
    
    logger.Info("应用程序启动")
    
    // 启动性能分析(默认在 /debug/pprof)
    logger.Info("性能分析端点: http://localhost:8080/debug/pprof")
    
    // 模拟内存泄漏(用于演示调试)
    if os.Getenv("SIMULATE_LEAK") == "1" {
        logger.Warn("模拟内存泄漏已启用")
        simulateMemoryLeak()
    }
    
    // 设置路由
    http.HandleFunc("/user", getUserHandler)
    http.HandleFunc("/compute", heavyComputation)
    http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte(`{"status": "healthy"}`))
    })
    
    // 记录启动信息
    logger.WithFields(logrus.Fields{
        "port":    8080,
        "version": "1.0.0",
    }).Info("HTTP服务器启动")
    
    // 启动服务器
    log.Fatal(http.ListenAndServe(":8080", nil))
}

4. 调试和分析工具使用

**pprof性能分析

# 生成CPU分析文件
go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30

# 生成内存分析文件  
go tool pprof http://localhost:8080/debug/pprof/heap

# 生成goroutine分析
go tool pprof http://localhost:8080/debug/pprof/goroutine

5. 课时总结