第17节:错误处理与异常管理

2 阅读6分钟

1. 课程概述

本节深入分析 Claude Code 的错误处理与异常管理机制,探讨其全局错误处理策略、API 错误处理、工具执行错误处理以及网络错误处理等方面。通过了解 Claude Code 的错误处理设计,我们可以学习如何构建健壮、可靠的 AI 辅助开发工具。

2. 错误处理架构

2.1 错误处理层次

应用层
├── 全局错误处理
├── API 错误处理
├── 工具执行错误处理
├── 网络错误处理
└── 用户输入错误处理

2.2 错误类型分类

错误类型描述处理策略
API 错误与 Anthropic API 交互时的错误重试机制、错误转换
工具错误工具执行失败错误捕获、用户提示
网络错误网络连接问题重试、超时处理
输入错误用户输入无效输入验证、错误提示
系统错误内部系统故障错误日志、紧急处理

3. 全局错误处理

3.1 错误捕获机制

实现原理

  • 使用 try-catch 捕获同步错误
  • 使用 Promise.catch 捕获异步错误
  • 全局错误监听器

关键代码

// src/main.tsx
import { registerErrorHandler } from './utils/errorHandler';

// 注册全局错误处理
registerErrorHandler();

// 应用主逻辑
try {
  // 应用启动
} catch (error) {
  console.error('Application startup error:', error);
  process.exit(1);
}

3.2 错误上报系统

实现方式

  • 错误日志记录
  • 错误分类和统计
  • 可选的错误上报(用户可选择)

关键代码

// src/services/api/logging.ts
export const logError = (error: Error, context?: string) => {
  const errorData = {
    timestamp: new Date().toISOString(),
    error: error.message,
    stack: error.stack,
    context
  };
  
  // 本地日志记录
  console.error('Error:', errorData);
  
  // 可选的远程上报
  if (config.enableErrorReporting) {
    sendErrorReport(errorData);
  }
};

4. API 错误处理

4.1 API 错误类型

常见错误

  • 400 Bad Request - 请求参数错误
  • 401 Unauthorized - 认证失败
  • 403 Forbidden - 权限不足
  • 429 Too Many Requests - 请求频率过高
  • 500 Internal Server Error - 服务器内部错误

4.2 重试机制

实现原理

  • 指数退避策略
  • 最大重试次数
  • 可重试错误类型判断

关键代码

// src/services/api/withRetry.ts
import axios from 'axios';

export const withRetry = async <T>(
  fn: () => Promise<T>,
  maxRetries = 3,
  retryDelay = 1000
): Promise<T> => {
  let lastError: Error;
  
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error as Error;
      
      // 判断是否可重试
      if (!isRetryableError(error)) {
        throw error;
      }
      
      // 指数退避
      const delay = retryDelay * Math.pow(2, i);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
  
  throw lastError;
};

const isRetryableError = (error: any): boolean => {
  // 判断是否是网络错误或可重试的 HTTP 错误
  return (
    axios.isAxiosError(error) &&
    (error.code === 'ECONNABORTED' ||
     error.response?.status === 429 ||
     error.response?.status >= 500)
  );
};

4.3 错误转换

实现方式

  • 将 API 错误转换为用户友好的错误消息
  • 错误分类和处理
  • 错误代码映射

关键代码

// src/services/api/errors.ts
export const handleApiError = (error: any): string => {
  if (axios.isAxiosError(error)) {
    switch (error.response?.status) {
      case 401:
        return 'Authentication failed. Please check your API key.';
      case 403:
        return 'Access denied. You don\'t have permission to perform this action.';
      case 429:
        return 'Rate limit exceeded. Please try again later.';
      case 400:
        return 'Invalid request. Please check your input.';
      case 500:
        return 'Server error. Please try again later.';
      default:
        return 'API error. Please try again.';
    }
  }
  
  return error.message || 'Unknown error occurred.';
};

5. 工具执行错误处理

5.1 工具错误捕获

实现原理

  • 工具执行时的错误捕获
  • 错误信息格式化
  • 用户友好的错误提示

关键代码

// src/tools.ts
export const executeTool = async (tool: Tool, input: any) => {
  try {
    return await tool.execute(input);
  } catch (error) {
    // 捕获工具执行错误
    const errorMessage = error instanceof Error ? error.message : 'Unknown error';
    
    // 格式化错误信息
    return {
      error: true,
      message: `Tool execution failed: ${errorMessage}`,
      details: error instanceof Error ? error.stack : undefined
    };
  }
};

5.2 工具错误恢复

实现策略

  • 错误回退机制
  • 替代工具推荐
  • 错误信息分析和建议

6. 网络错误处理

6.1 网络状态监测

实现方式

  • 网络连接状态检测
  • 网络延迟监测
  • 网络错误分类

关键代码

// src/utils/network.ts
export const checkNetworkStatus = async (): Promise<boolean> => {
  try {
    const response = await fetch('https://api.anthropic.com/v1/health', {
      method: 'HEAD',
      timeout: 5000
    });
    return response.ok;
  } catch {
    return false;
  }
};

export const measureNetworkLatency = async (): Promise<number> => {
  const start = Date.now();
  try {
    await fetch('https://api.anthropic.com/v1/health', {
      method: 'HEAD',
      timeout: 10000
    });
    return Date.now() - start;
  } catch {
    return -1;
  }
};

6.2 超时处理

实现原理

  • 请求超时设置
  • 超时错误捕获和处理
  • 超时重试策略

关键代码

// src/services/api/client.ts
import axios from 'axios';

const apiClient = axios.create({
  baseURL: 'https://api.anthropic.com/v1',
  timeout: 30000, // 30秒超时
  headers: {
    'Content-Type': 'application/json'
  }
});

// 超时拦截器
apiClient.interceptors.response.use(
  response => response,
  error => {
    if (error.code === 'ECONNABORTED') {
      // 处理超时错误
      console.error('Request timed out');
    }
    return Promise.reject(error);
  }
);

7. 用户输入错误处理

7.1 输入验证

实现方式

  • 输入格式验证
  • 输入长度限制
  • 输入内容检查

关键代码

// src/utils/inputValidation.ts
export const validateInput = (input: string): { valid: boolean; error?: string } => {
  // 检查输入长度
  if (input.length > 10000) {
    return {
      valid: false,
      error: 'Input too long. Please keep it under 10000 characters.'
    };
  }
  
  // 检查特殊字符
  if (input.includes('\x00')) {
    return {
      valid: false,
      error: 'Input contains invalid characters.'
    };
  }
  
  return { valid: true };
};

7.2 错误提示

实现策略

  • 实时输入验证
  • 错误消息本地化
  • 错误修复建议

8. 系统错误处理

8.1 系统故障处理

实现方式

  • 系统状态监测
  • 故障自动恢复
  • 紧急安全措施

关键代码

// src/utils/systemHealth.ts
export const checkSystemHealth = (): boolean => {
  // 检查内存使用
  const memoryUsage = process.memoryUsage();
  const memoryThreshold = 0.8; // 80% 内存使用
  const memoryUsed = memoryUsage.heapUsed / memoryUsage.heapTotal;
  
  if (memoryUsed > memoryThreshold) {
    console.warn('High memory usage detected');
    // 触发内存清理
    gc?.();
  }
  
  // 检查 CPU 使用
  // 检查磁盘空间
  // 检查其他系统资源
  
  return true;
};

// 定期检查系统健康状态
setInterval(checkSystemHealth, 60000); // 每分钟检查一次

8.2 紧急错误处理

实现策略

  • 紧急开关(参考第13节)
  • 安全模式
  • 数据备份和恢复

9. 错误日志系统

9.1 日志级别

日志级别

  • DEBUG - 详细调试信息
  • INFO - 一般信息
  • WARN - 警告信息
  • ERROR - 错误信息
  • FATAL - 致命错误

实现方式

// src/utils/logger.ts
export enum LogLevel {
  DEBUG = 'debug',
  INFO = 'info',
  WARN = 'warn',
  ERROR = 'error',
  FATAL = 'fatal'
}

export class Logger {
  private level: LogLevel;
  
  constructor(level: LogLevel = LogLevel.INFO) {
    this.level = level;
  }
  
  debug(message: string, data?: any) {
    if (this.shouldLog(LogLevel.DEBUG)) {
      console.debug(`[DEBUG] ${message}`, data);
    }
  }
  
  info(message: string, data?: any) {
    if (this.shouldLog(LogLevel.INFO)) {
      console.info(`[INFO] ${message}`, data);
    }
  }
  
  warn(message: string, data?: any) {
    if (this.shouldLog(LogLevel.WARN)) {
      console.warn(`[WARN] ${message}`, data);
    }
  }
  
  error(message: string, error?: Error, data?: any) {
    if (this.shouldLog(LogLevel.ERROR)) {
      console.error(`[ERROR] ${message}`, error, data);
    }
  }
  
  fatal(message: string, error?: Error, data?: any) {
    if (this.shouldLog(LogLevel.FATAL)) {
      console.error(`[FATAL] ${message}`, error, data);
      process.exit(1);
    }
  }
  
  private shouldLog(level: LogLevel): boolean {
    const levels = [LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR, LogLevel.FATAL];
    return levels.indexOf(level) >= levels.indexOf(this.level);
  }
}

export const logger = new Logger();

9.2 日志存储

实现方式

  • 本地文件日志
  • 日志轮换
  • 日志压缩

关键代码

// src/utils/fileLogger.ts
import fs from 'fs';
import path from 'path';
import { Logger, LogLevel } from './logger';

export class FileLogger extends Logger {
  private logDir: string;
  
  constructor(logDir: string, level: LogLevel = LogLevel.INFO) {
    super(level);
    this.logDir = logDir;
    
    // 确保日志目录存在
    if (!fs.existsSync(this.logDir)) {
      fs.mkdirSync(this.logDir, { recursive: true });
    }
    
    // 检查日志文件大小,进行轮换
    this.checkLogRotation();
  }
  
  private getLogFilePath(): string {
    const date = new Date().toISOString().split('T')[0];
    return path.join(this.logDir, `claude-code-${date}.log`);
  }
  
  private checkLogRotation() {
    const logFile = this.getLogFilePath();
    if (fs.existsSync(logFile)) {
      const stats = fs.statSync(logFile);
      const maxSize = 10 * 1024 * 1024; // 10MB
      
      if (stats.size > maxSize) {
        // 轮换日志文件
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        const rotatedFile = path.join(this.logDir, `claude-code-${timestamp}.log`);
        fs.renameSync(logFile, rotatedFile);
      }
    }
  }
  
  private writeToLog(level: string, message: string, data?: any) {
    const logFile = this.getLogFilePath();
    const timestamp = new Date().toISOString();
    const logEntry = `[${timestamp}] [${level}] ${message}`;
    
    fs.appendFileSync(logFile, logEntry + '\n');
    
    if (data) {
      fs.appendFileSync(logFile, JSON.stringify(data, null, 2) + '\n');
    }
  }
  
  // 重写父类方法
  debug(message: string, data?: any) {
    super.debug(message, data);
    this.writeToLog('DEBUG', message, data);
  }
  
  info(message: string, data?: any) {
    super.info(message, data);
    this.writeToLog('INFO', message, data);
  }
  
  warn(message: string, data?: any) {
    super.warn(message, data);
    this.writeToLog('WARN', message, data);
  }
  
  error(message: string, error?: Error, data?: any) {
    super.error(message, error, data);
    this.writeToLog('ERROR', message, { error: error?.stack, ...data });
  }
  
  fatal(message: string, error?: Error, data?: any) {
    super.fatal(message, error, data);
    this.writeToLog('FATAL', message, { error: error?.stack, ...data });
  }
}

export const fileLogger = new FileLogger(path.join(process.env.HOME || '', '.claude', 'logs'));

10. 代码优化建议

10.1 错误处理一致性

现状:不同模块的错误处理方式不一致

建议

  • 统一错误处理接口
  • 建立错误处理中间件
  • 标准化错误响应格式

10.2 错误恢复能力

现状:部分错误没有有效的恢复机制

建议

  • 为所有关键操作添加回退策略
  • 实现自动恢复机制
  • 提供用户可操作的错误恢复选项

10.3 错误信息可读性

现状:部分错误信息不够清晰和用户友好

建议

  • 改进错误消息的表达
  • 添加错误原因和解决建议
  • 实现错误消息本地化

10.4 错误监控

现状:缺乏全面的错误监控和分析

建议

  • 实现错误监控仪表板
  • 建立错误趋势分析
  • 设置错误告警机制

11. 实践演练

11.1 错误处理测试

步骤

  1. 模拟 API 错误
  2. 测试网络错误处理
  3. 测试工具执行错误处理
  4. 验证错误恢复机制

命令

# 模拟 API 错误(使用无效 API 密钥)
ANTHROPIC_API_KEY=invalid_key claude run "Hello"

# 模拟网络错误(断开网络连接后)
claude run "Hello"

# 测试工具执行错误
claude run "使用不存在的工具"

11.2 自定义错误处理

步骤

  1. 创建自定义错误类
  2. 实现错误处理中间件
  3. 测试自定义错误处理

代码示例

// custom-error.ts
export class CustomError extends Error {
  readonly code: string;
  readonly status: number;
  readonly details?: any;
  
  constructor(message: string, code: string, status: number = 500, details?: any) {
    super(message);
    this.name = 'CustomError';
    this.code = code;
    this.status = status;
    this.details = details;
  }
}

// error-middleware.ts
export const errorMiddleware = (error: any, req: any, res: any, next: any) => {
  if (error instanceof CustomError) {
    res.status(error.status).json({
      error: true,
      code: error.code,
      message: error.message,
      details: error.details
    });
  } else {
    // 处理其他错误
    res.status(500).json({
      error: true,
      code: 'INTERNAL_ERROR',
      message: 'Internal server error'
    });
  }
};

12. 总结与展望

Claude Code 的错误处理与异常管理机制体现了现代应用的最佳实践,通过多层次的错误处理策略,确保了应用的稳定性和可靠性。

关键要点

  • 多层次的错误处理架构,覆盖从 API 到用户输入的各个层面
  • 完善的错误捕获、处理和恢复机制
  • 详细的错误日志和监控系统
  • 用户友好的错误提示和处理建议

未来发展方向:

  • 更智能的错误预测和预防
  • 自动化的错误修复建议
  • 更全面的错误分析和报告
  • 与 AI 模型集成的错误处理

通过学习 Claude Code 的错误处理设计,我们可以掌握构建健壮、可靠的 AI 辅助开发工具的核心技术,提高应用的用户体验和稳定性。