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