Sentry 上下文信息 #
什么是上下文? #
上下文是附加在错误事件上的额外信息,帮助开发者更好地理解和复现问题。
text
┌─────────────────────────────────────────────────────────────┐
│ 上下文的作用 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 没有上下文: │
│ TypeError: Cannot read property 'id' of undefined │
│ → 不知道是谁、在哪、做了什么 │
│ │
│ 有上下文: │
│ TypeError: Cannot read property 'id' of undefined │
│ ├── 用户: john@example.com │
│ ├── 页面: /dashboard/orders │
│ ├── 浏览器: Chrome 120 / Windows 10 │
│ ├── 操作: 点击"导出订单"按钮 │
│ └── 订单ID: order-12345 │
│ → 清楚知道问题发生场景,易于复现和修复 │
│ │
└─────────────────────────────────────────────────────────────┘
用户信息(User) #
设置用户信息 #
javascript
// 基本用户信息
Sentry.setUser({ id: "user-123" });
// 完整用户信息
Sentry.setUser({
id: "user-123",
email: "john@example.com",
username: "john_doe",
ip_address: "192.168.1.1",
// 自定义字段
role: "admin",
subscription: "premium",
});
Python 设置用户 #
python
import sentry_sdk
sentry_sdk.set_user({
"id": "user-123",
"email": "john@example.com",
"username": "john_doe",
"ip_address": "192.168.1.1",
"role": "admin",
"subscription": "premium",
})
用户登录时设置 #
javascript
// 用户登录成功后
async function login(credentials) {
const user = await authenticate(credentials);
Sentry.setUser({
id: user.id,
email: user.email,
username: user.username,
role: user.role,
});
return user;
}
// 用户登出时清除
function logout() {
Sentry.setUser(null);
}
用户信息最佳实践 #
javascript
// ✅ 好的做法
Sentry.setUser({
id: user.id, // 必需:唯一标识
email: user.email, // 可选:便于联系
username: user.name, // 可选:便于识别
});
// ❌ 不好的做法
Sentry.setUser({
id: user.id,
password: user.password, // 不要包含敏感信息!
ssn: user.ssn, // 不要包含敏感信息!
});
标签(Tags) #
什么是标签? #
标签是用于分类和过滤事件的键值对,是 Sentry 中最常用的上下文功能。
text
┌─────────────────────────────────────────────────────────────┐
│ 标签特点 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ✅ 可搜索:在 Sentry 界面中搜索和过滤 │
│ ✅ 可聚合:用于统计和图表 │
│ ✅ 索引化:高性能查询 │
│ ⚠️ 数量限制:每个事件最多 200 个标签 │
│ │
└─────────────────────────────────────────────────────────────┘
设置标签 #
javascript
// 单个标签
Sentry.setTag("feature", "checkout");
// 多个标签
Sentry.setTag("feature", "checkout");
Sentry.setTag("step", "payment");
Sentry.setTag("payment_method", "credit_card");
Python 设置标签 #
python
import sentry_sdk
sentry_sdk.set_tag("feature", "checkout")
sentry_sdk.set_tag("step", "payment")
sentry_sdk.set_tag("payment_method", "credit_card")
常用标签示例 #
javascript
// 功能模块
Sentry.setTag("feature", "user-authentication");
Sentry.setTag("module", "payment");
// 业务类型
Sentry.setTag("subscription_tier", "premium");
Sentry.setTag("customer_type", "enterprise");
// 技术信息
Sentry.setTag("api_version", "v2");
Sentry.setTag("database", "postgresql");
// 错误来源
Sentry.setTag("source", "api");
Sentry.setTag("endpoint", "/api/users");
使用 Scope 设置标签 #
javascript
// 临时标签(只影响当前事件)
Sentry.withScope((scope) => {
scope.setTag("feature", "checkout");
scope.setTag("orderId", "12345");
Sentry.captureException(new Error("Checkout failed"));
});
// 全局标签(影响所有事件)
Sentry.setTag("environment", "production");
Sentry.setTag("version", "1.0.0");
标签最佳实践 #
javascript
// ✅ 好的做法:使用一致的命名
Sentry.setTag("feature", "checkout");
Sentry.setTag("feature", "payment"); // 一致的命名空间
// ✅ 好的做法:使用有意义的值
Sentry.setTag("payment_method", "credit_card");
Sentry.setTag("payment_method", "paypal");
// ❌ 不好的做法:标签值太长或太复杂
Sentry.setTag("data", JSON.stringify(largeObject)); // 使用 Extra 代替
// ❌ 不好的做法:标签键不一致
Sentry.setTag("Feature", "checkout");
Sentry.setTag("feature", "checkout"); // 大小写不一致
额外数据(Extra) #
什么是额外数据? #
额外数据是附加在事件上的任意数据,不适合作为标签的数据可以放在这里。
text
┌─────────────────────────────────────────────────────────────┐
│ Extra vs Tags │
├─────────────────────────────────────────────────────────────┤
│ │
│ Tags(标签): │
│ - 用于搜索和过滤 │
│ - 值应该是简单的字符串 │
│ - 数量有限制 │
│ │
│ Extra(额外数据): │
│ - 用于存储详细信息 │
│ - 可以是复杂对象 │
│ - 不参与搜索 │
│ │
└─────────────────────────────────────────────────────────────┘
设置额外数据 #
javascript
// 单个字段
Sentry.setExtra("orderId", "12345");
// 多个字段
Sentry.setExtra("orderId", "12345");
Sentry.setExtra("orderTotal", 99.99);
Sentry.setExtra("currency", "USD");
// 复杂对象
Sentry.setExtra("order", {
id: "12345",
items: [
{ productId: "p1", quantity: 2, price: 49.99 },
{ productId: "p2", quantity: 1, price: 0.01 },
],
shipping: {
address: "123 Main St",
city: "New York",
},
});
Python 设置额外数据 #
python
import sentry_sdk
sentry_sdk.set_extra("order_id", "12345")
sentry_sdk.set_extra("order_total", 99.99)
sentry_sdk.set_extra("currency", "USD")
sentry_sdk.set_extra("order", {
"id": "12345",
"items": [
{"product_id": "p1", "quantity": 2, "price": 49.99},
],
})
使用 Scope 设置额外数据 #
javascript
Sentry.withScope((scope) => {
scope.setExtra("orderId", order.id);
scope.setExtra("orderItems", order.items.length);
scope.setExtra("paymentMethod", paymentMethod);
Sentry.captureException(error);
});
额外数据最佳实践 #
javascript
// ✅ 好的做法:存储有用的调试信息
Sentry.setExtra("requestBody", requestBody);
Sentry.setExtra("responseStatus", response.status);
Sentry.setExtra("retryCount", retryCount);
// ✅ 好的做法:存储相关对象
Sentry.setExtra("cart", {
items: cart.items,
total: cart.total,
});
// ❌ 不好的做法:存储敏感信息
Sentry.setExtra("password", user.password); // 不要这样做!
Sentry.setExtra("creditCard", payment.cardNumber); // 不要这样做!
// ❌ 不好的做法:存储过大的对象
Sentry.setExtra("allUsers", users); // 如果 users 很大,会占用大量空间
面包屑(Breadcrumbs) #
什么是面包屑? #
面包屑是事件发生前的操作记录,帮助理解错误发生前的用户行为路径。
text
┌─────────────────────────────────────────────────────────────┐
│ 面包屑示例 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 时间线: │
│ │
│ 10:30:01 [Navigation] 用户访问 /dashboard │
│ 10:30:02 [HTTP] GET /api/user/profile │
│ 10:30:03 [HTTP] GET /api/orders?page=1 │
│ 10:30:05 [User] 点击"导出订单"按钮 │
│ 10:30:06 [HTTP] POST /api/orders/export │
│ 10:30:07 [Error] TypeError: Cannot read property... │
│ │
└─────────────────────────────────────────────────────────────┘
自动面包屑 #
Sentry SDK 自动捕获以下面包屑:
javascript
// 自动捕获的面包屑类型
// 1. 导航(Navigation)
// 2. HTTP 请求(Fetch/XHR)
// 3. 控制台日志(Console)
// 4. 用户交互(Click/Input)
// 5. 历史记录(History)
手动添加面包屑 #
javascript
// 基本面包屑
Sentry.addBreadcrumb({
message: "User clicked checkout button",
level: "info",
});
// 完整面包屑
Sentry.addBreadcrumb({
category: "ui.click",
message: "User clicked checkout button",
level: "info",
data: {
buttonId: "checkout-btn",
page: "/cart",
},
});
// HTTP 面包屑
Sentry.addBreadcrumb({
category: "http",
message: "POST /api/orders",
level: "info",
data: {
method: "POST",
url: "/api/orders",
status_code: 200,
},
});
Python 添加面包屑 #
python
import sentry_sdk
# 基本面包屑
sentry_sdk.add_breadcrumb(
message="User clicked checkout button",
level="info",
)
# 完整面包屑
sentry_sdk.add_breadcrumb(
category="ui.click",
message="User clicked checkout button",
level="info",
data={
"button_id": "checkout-btn",
"page": "/cart",
},
)
面包屑类型 #
javascript
// 导航面包屑
Sentry.addBreadcrumb({
category: "navigation",
message: "Navigate to /checkout",
data: {
from: "/cart",
to: "/checkout",
},
});
// HTTP 面包屑
Sentry.addBreadcrumb({
category: "http",
message: "GET /api/products",
data: {
method: "GET",
url: "/api/products",
status_code: 200,
},
});
// 用户操作面包屑
Sentry.addBreadcrumb({
category: "ui.click",
message: "User clicked submit button",
data: {
element: "submit-btn",
form: "checkout-form",
},
});
// 自定义面包屑
Sentry.addBreadcrumb({
category: "custom",
message: "Custom event",
data: {
key: "value",
},
});
配置面包屑 #
javascript
Sentry.init({
dsn: "https://xxxxxxxx@o123456.ingest.sentry.io/1234567",
integrations: [
new Sentry.Integrations.Breadcrumbs({
console: true, // 控制台日志
dom: true, // DOM 事件
fetch: true, // Fetch 请求
history: true, // 历史记录
xhr: true, // XHR 请求
sentry: true, // Sentry 事件
}),
],
// 最大面包屑数量
maxBreadcrumbs: 100,
// 过滤面包屑
beforeBreadcrumb(breadcrumb, hint) {
// 忽略特定类型的面包屑
if (breadcrumb.category === "console") {
return null;
}
// 过滤敏感数据
if (breadcrumb.data?.url?.includes("password")) {
return null;
}
return breadcrumb;
},
});
请求上下文 #
设置请求信息 #
javascript
Sentry.setContext("request", {
method: "POST",
url: "/api/orders",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer xxx",
},
body: {
orderId: "12345",
items: [],
},
});
Python 请求上下文 #
python
import sentry_sdk
sentry_sdk.set_context("request", {
"method": "POST",
"url": "/api/orders",
"headers": {
"Content-Type": "application/json",
},
"body": {
"order_id": "12345",
},
})
Flask 请求上下文 #
python
from flask import request
import sentry_sdk
@app.route("/api/orders", methods=["POST"])
def create_order():
sentry_sdk.set_context("request", {
"method": request.method,
"url": request.url,
"headers": dict(request.headers),
"body": request.get_json(),
})
# 处理请求...
Express 请求上下文 #
javascript
const express = require("express");
const Sentry = require("@sentry/node");
const app = express();
app.use(Sentry.Handlers.requestHandler({
user: ["id", "email", "username"],
ip: true,
request: true,
transaction: "methodPath",
}));
app.post("/api/orders", (req, res) => {
Sentry.setContext("request", {
method: req.method,
url: req.url,
headers: req.headers,
body: req.body,
});
// 处理请求...
});
自定义上下文 #
设置自定义上下文 #
javascript
// 设备信息
Sentry.setContext("device", {
name: "iPhone 15 Pro",
model: "iPhone15,3",
os: "iOS 17.0",
screen: {
width: 1179,
height: 2556,
},
});
// 应用信息
Sentry.setContext("app", {
name: "My App",
version: "1.0.0",
build: "123",
environment: "production",
});
// 业务信息
Sentry.setContext("business", {
company: "Acme Inc",
subscription: "enterprise",
features: ["feature1", "feature2"],
});
Python 自定义上下文 #
python
import sentry_sdk
sentry_sdk.set_context("device", {
"name": "iPhone 15 Pro",
"model": "iPhone15,3",
"os": "iOS 17.0",
})
sentry_sdk.set_context("app", {
"name": "My App",
"version": "1.0.0",
"environment": "production",
})
上下文生命周期 #
全局上下文 #
javascript
// 应用启动时设置
Sentry.setTag("app_version", "1.0.0");
Sentry.setTag("environment", "production");
// 用户登录时设置
Sentry.setUser({ id: user.id, email: user.email });
// 这些上下文会影响所有后续事件
临时上下文 #
javascript
// 使用 withScope 创建临时上下文
Sentry.withScope((scope) => {
scope.setTag("feature", "checkout");
scope.setExtra("orderId", "12345");
Sentry.captureException(error);
// 离开 withScope 后,这些上下文会被清除
});
// 使用 configureScope 修改全局上下文
Sentry.configureScope((scope) => {
scope.setTag("feature", "checkout");
// 这个标签会保留
});
Python 上下文生命周期 #
python
import sentry_sdk
# 全局上下文
sentry_sdk.set_tag("app_version", "1.0.0")
# 临时上下文
with sentry_sdk.push_scope() as scope:
scope.set_tag("feature", "checkout")
scope.set_extra("order_id", "12345")
sentry_sdk.capture_exception(error)
# 离开 with 块后,上下文被清除
清除上下文 #
清除特定上下文 #
javascript
// 清除用户信息
Sentry.setUser(null);
// 清除标签
Sentry.setTag("feature", undefined);
// 清除额外数据
Sentry.setExtra("orderId", undefined);
// 清除上下文
Sentry.setContext("request", null);
清除所有上下文 #
javascript
// 清除所有上下文(保留用户信息)
Sentry.configureScope((scope) => {
scope.clear();
});
// 清除所有上下文(包括用户信息)
Sentry.configureScope((scope) => {
scope.clear();
scope.setUser(null);
});
Python 清除上下文 #
python
import sentry_sdk
# 清除用户信息
sentry_sdk.set_user(None)
# 清除所有上下文
sentry_sdk.Scope.get_global_scope().clear()
上下文最佳实践 #
1. 合理使用 Tags 和 Extra #
javascript
// ✅ 好的做法
// Tags:用于搜索和过滤
Sentry.setTag("feature", "checkout");
Sentry.setTag("error_type", "payment_failed");
// Extra:存储详细信息
Sentry.setExtra("orderDetails", order);
Sentry.setExtra("stackTrace", error.stack);
2. 及时更新用户信息 #
javascript
// ✅ 好的做法
// 用户登录
function onLogin(user) {
Sentry.setUser({
id: user.id,
email: user.email,
});
}
// 用户登出
function onLogout() {
Sentry.setUser(null);
}
3. 记录关键操作 #
javascript
// ✅ 好的做法
async function processPayment(order) {
Sentry.addBreadcrumb({
category: "payment",
message: "Starting payment process",
data: { orderId: order.id },
});
try {
const result = await paymentService.charge(order);
Sentry.addBreadcrumb({
category: "payment",
message: "Payment successful",
data: { transactionId: result.id },
});
return result;
} catch (error) {
Sentry.captureException(error);
throw error;
}
}
4. 避免敏感信息 #
javascript
// ❌ 不好的做法
Sentry.setUser({
id: user.id,
password: user.password, // 敏感信息
});
Sentry.setExtra("creditCard", {
number: "4111111111111111", // 敏感信息
cvv: "123", // 敏感信息
});
// ✅ 好的做法
Sentry.setUser({
id: user.id,
email: user.email,
});
Sentry.setExtra("payment", {
lastFour: "1111",
brand: "visa",
});
下一步 #
现在你已经掌握了上下文信息的使用方法,接下来学习 前端集成 了解如何在前端项目中集成 Sentry!
最后更新:2026-03-29