Skip to content

日志级别和使用场景

日志级别定义

生成日志时,必须根据事件的严重程度选择正确的日志级别。

级别优先级

从低到高:DEBUG < INFO < WARN < ERROR < FATAL

DEBUG 级别

使用场景

  • 开发和调试阶段的详细信息
  • 变量值、函数参数、返回值
  • 详细的执行流程跟踪

使用规则

  • 在开发环境启用
  • 禁止在生产环境记录 DEBUG 日志
  • 避免记录大量数据或循环内的日志

示例

go
// ✅ 正确 - 记录函数参数和中间变量
func ProcessOrder(orderID int64, userID int64) error {
    log.Debug("processing order",
        "order_id", orderID,
        "user_id", userID)

    price := calculatePrice(orderID)
    log.Debug("calculated price",
        "order_id", orderID,
        "price", price)

    return nil
}

// ❌ 错误 - 循环内记录 DEBUG
for _, item := range items {
    log.Debug("processing item", "item", item) // 过度记录
}
typescript
// ✅ 正确 - 记录组件状态变化
function UserProfile({ userId }: Props) {
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    console.debug('Fetching user profile', { userId });
    fetchUser(userId).then(data => {
      console.debug('User profile loaded', { userId, username: data.username });
      setUser(data);
    });
  }, [userId]);
}

// ❌ 错误 - 记录敏感信息
console.debug('User data', { password: user.password }); // 禁止

INFO 级别

使用场景

  • 应用正常运行的关键节点
  • 重要业务操作的成功执行
  • HTTP 请求/响应记录
  • 定时任务执行

使用规则

  • 必须记录业务操作的成功完成
  • 应该包含操作的关键参数
  • 避免过于频繁的 INFO 日志

示例

go
// ✅ 正确 - HTTP 请求日志
func LogHTTPRequest(c echo.Context) {
    log.Info("http request",
        "method", c.Request().Method,
        "path", c.Request().URL.Path,
        "remote_ip", c.RealIP(),
        "user_agent", c.Request().UserAgent())
}

// ✅ 正确 - 业务操作成功
func CreateUser(req *CreateUserRequest) (*User, error) {
    user := &User{
        Username: req.Username,
        Email:    req.Email,
    }

    if err := db.Create(user).Error; err != nil {
        return nil, err
    }

    log.Info("user created",
        "user_id", user.ID,
        "username", user.Username)

    return user, nil
}

// ❌ 错误 - 过度记录
for i := 0; i < 1000; i++ {
    log.Info("processing", "index", i) // 避免循环记录
}
typescript
// ✅ 正确 - 记录重要操作
async function submitOrder(order: Order) {
  console.info('Submitting order', {
    orderId: order.id,
    itemCount: order.items.length,
    totalAmount: order.total,
  });

  const result = await api.createOrder(order);

  console.info('Order submitted successfully', {
    orderId: result.id,
    status: result.status,
  });

  return result;
}

// ❌ 错误 - 记录用户输入的每次变化
<input onChange={e => console.info('Input changed', e.target.value)} />

WARN 级别

使用场景

  • 潜在的问题或异常情况
  • 使用了已废弃的功能
  • 配置缺失或不合理但有默认值
  • 重试操作

使用规则

  • 必须记录可能影响业务的警告
  • 应该说明警告的原因和影响
  • 建议提供解决方案或后续操作

示例

go
// ✅ 正确 - 配置缺失警告
func LoadConfig() *Config {
    cfg := &Config{}

    if os.Getenv("REDIS_HOST") == "" {
        log.Warn("REDIS_HOST not set, using default",
            "default_host", "localhost")
        cfg.RedisHost = "localhost"
    }

    return cfg
}

// ✅ 正确 - 重试警告
func FetchExternalAPI(url string) (*Response, error) {
    var resp *Response
    var err error

    for i := 0; i < 3; i++ {
        resp, err = http.Get(url)
        if err == nil {
            return resp, nil
        }

        log.Warn("api request failed, retrying",
            "url", url,
            "attempt", i+1,
            "error", err.Error())

        time.Sleep(time.Second * time.Duration(i+1))
    }

    return nil, err
}

// ✅ 正确 - 数据异常但可处理
func ProcessData(data *Data) error {
    if data.Value < 0 {
        log.Warn("negative value detected, using absolute",
            "original_value", data.Value,
            "corrected_value", math.Abs(data.Value))
        data.Value = math.Abs(data.Value)
    }

    return nil
}
typescript
// ✅ 正确 - API 降级警告
async function fetchUserData(userId: string) {
  try {
    return await api.getUserFromCache(userId);
  } catch (error) {
    console.warn('Cache unavailable, falling back to database', {
      userId,
      error: error.message,
    });
    return await api.getUserFromDB(userId);
  }
}

// ✅ 正确 - 废弃功能警告
function legacyAPI() {
  console.warn('This API is deprecated, use newAPI instead');
  // ... legacy logic
}

ERROR 级别

使用场景

  • 业务操作失败
  • 数据库操作失败
  • 外部服务调用失败
  • 数据验证失败

使用规则

  • 必须记录错误原因和上下文
  • 必须包含堆栈跟踪(后端)
  • 应该包含请求 ID 或事务 ID
  • 避免重复记录同一错误

示例

go
// ✅ 正确 - 完整的错误日志
func CreateOrder(ctx context.Context, req *CreateOrderRequest) (*Order, error) {
    requestID := ctx.Value("request_id").(string)

    order := &Order{
        UserID: req.UserID,
        Total:  req.Total,
    }

    if err := db.WithContext(ctx).Create(order).Error; err != nil {
        log.Error("failed to create order",
            "request_id", requestID,
            "user_id", req.UserID,
            "error", err.Error(),
            "stack", string(debug.Stack()))
        return nil, fmt.Errorf("创建订单失败: %w", err)
    }

    return order, nil
}

// ✅ 正确 - 外部服务调用失败
func SendEmail(to string, subject string, body string) error {
    err := emailClient.Send(to, subject, body)
    if err != nil {
        log.Error("failed to send email",
            "to", to,
            "subject", subject,
            "error", err.Error())
        return fmt.Errorf("发送邮件失败: %w", err)
    }

    return nil
}

// ❌ 错误 - 重复记录
func Handler(c echo.Context) error {
    err := service.DoSomething()
    if err != nil {
        log.Error("error in service", "error", err) // 第一次记录
        log.Error("handler failed", "error", err)    // 重复记录,应该避免
        return err
    }
    return nil
}

// ✅ 正确 - 只在最外层记录
func Handler(c echo.Context) error {
    err := service.DoSomething() // service 内部已记录
    if err != nil {
        return c.JSON(500, Response{Message: "操作失败"})
    }
    return nil
}
typescript
// ✅ 正确 - 记录 API 调用失败
async function deleteUser(userId: string) {
  try {
    await api.delete(`/users/${userId}`);
    console.info('User deleted', { userId });
  } catch (error) {
    console.error('Failed to delete user', {
      userId,
      error: error.message,
      stack: error.stack,
    });
    throw new Error('删除用户失败');
  }
}

// ✅ 正确 - 记录表单验证错误
function validateForm(data: FormData) {
  const errors = [];

  if (!data.email.includes('@')) {
    errors.push('Invalid email format');
  }

  if (errors.length > 0) {
    console.error('Form validation failed', {
      errors,
      formData: { email: data.email }, // 不包含敏感字段
    });
  }

  return errors;
}

FATAL 级别

使用场景

  • 应用无法继续运行的严重错误
  • 启动时的致命错误(配置错误、数据库连接失败)
  • 系统资源耗尽

使用规则

  • 必须记录详细的错误信息
  • 通常记录后应用会退出
  • 应该包含环境信息和配置

示例

go
// ✅ 正确 - 数据库连接失败
func InitDB(config *Config) *gorm.DB {
    dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True",
        config.DBUser,
        config.DBPassword,
        config.DBHost,
        config.DBPort,
        config.DBName)

    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        log.Fatal("failed to connect database",
            "host", config.DBHost,
            "port", config.DBPort,
            "database", config.DBName,
            "error", err.Error())
    }

    return db
}

// ✅ 正确 - 必需配置缺失
func main() {
    jwtSecret := os.Getenv("JWT_SECRET")
    if jwtSecret == "" {
        log.Fatal("JWT_SECRET environment variable is required")
    }

    // 启动应用...
}

// ❌ 错误 - 滥用 FATAL
func ProcessRequest(req *Request) error {
    if req.ID == 0 {
        log.Fatal("invalid request ID") // 不应该用 FATAL,用 ERROR
    }
    return nil
}

日志级别选择决策树

相关文档