Sentry 错误捕获基础 #

错误捕获方式 #

Sentry 提供多种错误捕获方式:

text
┌─────────────────────────────────────────────────────────────┐
│                    错误捕获方式                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 自动捕获                                                │
│     - 未捕获的异常                                          │
│     - Promise 拒绝                                         │
│     - 控制台错误                                            │
│                                                             │
│  2. 手动捕获                                                │
│     - captureException()                                   │
│     - captureMessage()                                     │
│     - captureEvent()                                       │
│                                                             │
│  3. 集成捕获                                                │
│     - 框架集成                                              │
│     - 库集成                                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

自动捕获 #

JavaScript 自动捕获 #

Sentry SDK 自动捕获以下错误:

javascript
// 1. 未捕获的异常
throw new Error("Uncaught error");

// 2. 未处理的 Promise 拒绝
Promise.reject("Unhandled rejection");

// 3. 全局错误
window.onerror = function(message, source, lineno, colno, error) {
  // Sentry 自动捕获
};

// 4. console.error
console.error("This will be captured as breadcrumb");

配置自动捕获 #

javascript
Sentry.init({
  dsn: "https://xxxxxxxx@o123456.ingest.sentry.io/1234567",
  
  integrations: [
    new Sentry.Integrations.GlobalHandlers({
      onerror: true,           // 捕获全局错误
      onunhandledrejection: true,  // 捕获未处理的 Promise
    }),
    new Sentry.Integrations.CaptureConsole({
      levels: ["error", "warn"],  // 捕获 console.error 和 console.warn
    }),
  ],
});

Python 自动捕获 #

python
import sentry_sdk

# 自动捕获未处理的异常
def main():
    raise Exception("This will be captured automatically")

if __name__ == "__main__":
    main()

手动捕获 #

captureException #

捕获异常对象:

javascript
try {
  const result = JSON.parse(invalidJson);
} catch (error) {
  Sentry.captureException(error);
}
python
try:
    result = json.loads(invalid_json)
except json.JSONDecodeError as error:
    sentry_sdk.capture_exception(error)
go
if err != nil {
    sentry.CaptureException(err)
}

captureMessage #

捕获消息字符串:

javascript
// 简单消息
Sentry.captureMessage("Something went wrong");

// 带级别的消息
Sentry.captureMessage("User login failed", "warning");

// 使用 Sentry.Severity 枚举
Sentry.captureMessage("Critical error", Sentry.Severity.Critical);
python
import sentry_sdk
from sentry_sdk import set_level

# 简单消息
sentry_sdk.capture_message("Something went wrong")

# 带级别的消息
sentry_sdk.capture_message("User login failed", level="warning")

captureEvent #

捕获自定义事件:

javascript
Sentry.captureEvent({
  message: "Custom event message",
  level: "error",
  tags: {
    feature: "checkout",
  },
  extra: {
    orderId: "12345",
    amount: 99.99,
  },
});
python
sentry_sdk.capture_event({
    "message": "Custom event message",
    "level": "error",
    "tags": {
        "feature": "checkout",
    },
    "extra": {
        "order_id": "12345",
        "amount": 99.99,
    },
})

错误级别 #

级别定义 #

Sentry 定义了以下错误级别(从低到高):

text
┌─────────────────────────────────────────────────────────────┐
│                    错误级别                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  fatal    - 致命错误,应用崩溃                              │
│  error    - 错误,功能无法正常工作                          │
│  warning  - 警告,可能存在问题                              │
│  info     - 信息,一般性事件                                │
│  debug    - 调试信息                                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

设置错误级别 #

javascript
// captureException
Sentry.captureException(new Error("Error message"), {
  level: "error",
});

// captureMessage
Sentry.captureMessage("Warning message", {
  level: "warning",
});

// 使用 Scope 设置默认级别
Sentry.withScope((scope) => {
  scope.setLevel("warning");
  Sentry.captureMessage("This is a warning");
});
python
# capture_exception
sentry_sdk.capture_exception(error, level="error")

# capture_message
sentry_sdk.capture_message("Warning message", level="warning")

# 使用上下文设置级别
with sentry_sdk.push_scope() as scope:
    scope.set_level("warning")
    sentry_sdk.capture_message("This is a warning")

级别与告警 #

不同级别可以配置不同的告警策略:

yaml
# 告警规则示例
rules:
  - name: "Fatal Error Alert"
    conditions:
      - level: fatal
    actions:
      - type: slack
        channel: "#critical-alerts"
      - type: pagerduty

  - name: "Error Rate Alert"
    conditions:
      - level: error
      - threshold: 10 minutes
      - count: 100
    actions:
      - type: email
        targets: ["dev-team@example.com"]

错误过滤 #

beforeSend 过滤 #

javascript
Sentry.init({
  dsn: "https://xxxxxxxx@o123456.ingest.sentry.io/1234567",
  
  beforeSend(event, hint) {
    // 过滤本地开发环境的错误
    if (event.request?.url?.includes("localhost")) {
      return null;
    }
    
    // 过滤特定错误
    if (event.exception?.values?.[0]?.type === "NetworkError") {
      return null;
    }
    
    // 过滤特定用户
    if (event.user?.id === "test-user") {
      return null;
    }
    
    return event;
  },
});

ignoreErrors 过滤 #

javascript
Sentry.init({
  dsn: "https://xxxxxxxx@o123456.ingest.sentry.io/1234567",
  
  ignoreErrors: [
    // 字符串匹配
    "NetworkError",
    "Failed to fetch",
    
    // 正则表达式
    /ResizeObserver loop/,
    /Non-Error promise rejection/,
    
    // 特定错误类型
    "TypeError: Load failed",
  ],
});

denyUrls 和 allowUrls #

javascript
Sentry.init({
  dsn: "https://xxxxxxxx@o123456.ingest.sentry.io/1234567",
  
  // 排除特定 URL 的错误
  denyUrls: [
    /\/api\/health/,
    /\/static\//,
    /google-analytics\.com/,
  ],
  
  // 只允许特定 URL 的错误
  allowUrls: [
    /https?:\/\/(www\.)?example\.com/,
    /https?:\/\/api\.example\.com/,
  ],
});

Python 过滤 #

python
def before_send(event, hint):
    # 过滤本地开发环境的错误
    if "localhost" in event.get("request", {}).get("url", ""):
        return None
    
    # 过滤特定错误
    if "exc_info" in hint:
        exc_type, exc_value, tb = hint["exc_info"]
        if isinstance(exc_value, NetworkError):
            return None
    
    return event

sentry_sdk.init(
    dsn="https://xxxxxxxx@o123456.ingest.sentry.io/1234567",
    before_send=before_send,
    ignore_errors=[NetworkError, TimeoutError],
)

添加额外信息 #

使用 Scope #

javascript
// 临时 Scope
Sentry.withScope((scope) => {
  scope.setTag("feature", "checkout");
  scope.setExtra("orderId", "12345");
  scope.setUser({ id: "user-123", email: "user@example.com" });
  
  Sentry.captureException(new Error("Checkout failed"));
});

// 全局 Scope
Sentry.setTag("feature", "checkout");
Sentry.setExtra("orderId", "12345");
Sentry.setUser({ id: "user-123" });

captureException 选项 #

javascript
Sentry.captureException(new Error("Payment failed"), {
  tags: {
    feature: "payment",
    paymentMethod: "credit_card",
  },
  extra: {
    orderId: "12345",
    amount: 99.99,
    currency: "USD",
  },
  user: {
    id: "user-123",
    email: "user@example.com",
  },
  level: "error",
  fingerprint: ["payment-error", "{{ transaction }}"],
});

Python 添加信息 #

python
import sentry_sdk

# 使用上下文管理器
with sentry_sdk.push_scope() as scope:
    scope.set_tag("feature", "checkout")
    scope.set_extra("order_id", "12345")
    scope.set_user({"id": "user-123", "email": "user@example.com"})
    
    sentry_sdk.capture_exception(Exception("Checkout failed"))

# 全局设置
sentry_sdk.set_tag("feature", "checkout")
sentry_sdk.set_extra("order_id", "12345")
sentry_sdk.set_user({"id": "user-123"})

错误指纹(Fingerprinting) #

默认分组规则 #

Sentry 默认使用以下规则分组:

text
┌─────────────────────────────────────────────────────────────┐
│                    默认分组规则                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 错误类型(TypeError, ReferenceError...)                │
│  2. 错误消息                                                │
│  3. 堆栈跟踪                                                │
│                                                             │
│  相同指纹的错误会被分到同一个 Issue                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

自定义指纹 #

javascript
// 使用指纹分组
Sentry.captureException(new Error("Database connection failed"), {
  fingerprint: ["database-error", "{{ default }}"],
});

// 完全自定义分组
Sentry.captureException(new Error("API request failed"), {
  fingerprint: ["api-error", "{{ transaction }}"],
});

// 动态指纹
Sentry.withScope((scope) => {
  scope.setFingerprint(["payment-error", "{{ default }}"]);
  Sentry.captureException(new Error("Payment failed"));
});

指纹变量 #

变量 描述
{{ default }} 默认指纹
{{ transaction }} 事务名称
{{ level }} 错误级别
{{ logger }} 日志器名称

常见指纹场景 #

javascript
// 场景1:按 API 端点分组
Sentry.captureException(error, {
  fingerprint: ["api-error", "{{ transaction }}"],
});

// 场景2:按用户分组
Sentry.captureException(error, {
  fingerprint: ["user-error", "{{ user.id }}"],
});

// 场景3:按错误类型分组
Sentry.captureException(error, {
  fingerprint: ["{{ default }}", String(error.name)],
});

// 场景4:完全自定义
Sentry.captureException(error, {
  fingerprint: ["custom-group", "additional-info"],
});

异步错误处理 #

Promise 错误 #

javascript
// 自动捕获
async function fetchData() {
  const response = await fetch("/api/data");
  return response.json();
}

// 手动捕获
async function fetchDataSafe() {
  try {
    const response = await fetch("/api/data");
    return await response.json();
  } catch (error) {
    Sentry.captureException(error);
    throw error;
  }
}

// Promise 链
fetch("/api/data")
  .then((response) => response.json())
  .catch((error) => {
    Sentry.captureException(error);
    return null;
  });

async/await 错误 #

javascript
async function processOrder(orderId) {
  try {
    const order = await fetchOrder(orderId);
    const payment = await processPayment(order);
    await sendConfirmation(payment);
  } catch (error) {
    Sentry.withScope((scope) => {
      scope.setTag("orderId", orderId);
      scope.setExtra("orderStatus", "failed");
      Sentry.captureException(error);
    });
    throw error;
  }
}

Python 异步错误 #

python
import sentry_sdk
import asyncio

async def fetch_data():
    try:
        response = await aiohttp.get("/api/data")
        return await response.json()
    except Exception as error:
        sentry_sdk.capture_exception(error)
        raise

# 异步上下文
async def main():
    with sentry_sdk.start_transaction(name="main"):
        await fetch_data()

错误堆栈处理 #

获取堆栈跟踪 #

javascript
// JavaScript 自动获取堆栈
try {
  throw new Error("Error with stack trace");
} catch (error) {
  Sentry.captureException(error);
}

// 手动添加堆栈
Sentry.captureException(new Error("Error message"), {
  stacktrace: [
    {
      filename: "app.js",
      function: "processOrder",
      lineno: 42,
      colno: 15,
    },
  ],
});

Python 堆栈跟踪 #

python
import sentry_sdk
import traceback

try:
    raise Exception("Error with stack trace")
except Exception as error:
    # 自动包含堆栈
    sentry_sdk.capture_exception(error)

# 手动添加堆栈
try:
    raise Exception("Error")
except Exception:
    sentry_sdk.capture_event({
        "message": "Custom event with stack",
        "stacktrace": traceback.format_exc(),
    })

错误采样 #

基础采样 #

javascript
Sentry.init({
  dsn: "https://xxxxxxxx@o123456.ingest.sentry.io/1234567",
  
  // 错误采样率(0.0 - 1.0)
  sampleRate: 0.5,  // 50% 的错误会被上报
});

动态采样 #

javascript
Sentry.init({
  dsn: "https://xxxxxxxx@o123456.ingest.sentry.io/1234567",
  
  sampleRate: 1.0,
  
  beforeSend(event, hint) {
    // 根据错误类型决定是否上报
    const error = hint.originalException;
    
    if (error instanceof NetworkError) {
      // 网络错误只上报 10%
      return Math.random() < 0.1 ? event : null;
    }
    
    if (error instanceof ValidationError) {
      // 验证错误不上报
      return null;
    }
    
    // 其他错误全部上报
    return event;
  },
});

错误处理最佳实践 #

1. 合理使用错误级别 #

javascript
// ✅ 好的做法
Sentry.captureMessage("User login attempt from new device", {
  level: "info",
});

Sentry.captureMessage("API rate limit approaching", {
  level: "warning",
});

Sentry.captureException(new Error("Database connection failed"), {
  level: "error",
});

Sentry.captureException(new Error("Application crashed"), {
  level: "fatal",
});

// ❌ 不好的做法
Sentry.captureMessage("User clicked button", {
  level: "fatal",  // 级别不匹配
});

2. 提供有意义的上下文 #

javascript
// ✅ 好的做法
Sentry.withScope((scope) => {
  scope.setTag("feature", "checkout");
  scope.setTag("step", "payment");
  scope.setExtra("orderId", order.id);
  scope.setExtra("amount", order.total);
  scope.setExtra("paymentMethod", paymentMethod);
  scope.setUser({ id: user.id, email: user.email });
  
  Sentry.captureException(error);
});

// ❌ 不好的做法
Sentry.captureException(error);  // 缺少上下文

3. 避免过度上报 #

javascript
// ✅ 好的做法
Sentry.init({
  ignoreErrors: [
    "NetworkError",
    "Failed to fetch",
  ],
  beforeSend(event) {
    if (event.user?.id === "test-user") {
      return null;
    }
    return event;
  },
});

// ❌ 不好的做法
// 上报所有错误,包括测试用户和预期错误

4. 使用指纹优化分组 #

javascript
// ✅ 好的做法
Sentry.captureException(error, {
  fingerprint: ["api-error", "{{ transaction }}"],
});

// ❌ 不好的做法
// 让所有相似错误分散到不同 Issue

下一步 #

现在你已经掌握了错误捕获的基础知识,接下来学习 上下文信息 了解如何添加更丰富的错误上下文!

最后更新:2026-03-29