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