日志级别和使用场景
日志级别定义
生成日志时,必须根据事件的严重程度选择正确的日志级别。
级别优先级
从低到高: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
}