SDK JavaScript của Sentry phiên bản 20.x áp dụng triết lý Unified API — một khuôn khổ thiết kế nhằm chuẩn hóa cách tương tác với hệ thống giám sát lỗi và hiệu năng trên mọi nền tảng. Mục tiêu cốt lõi là loại bỏ sự phân mảnh về thuật ngữ, hành vi và luồng dữ liệu giữa các SDK khác nhau, đồng thời mở rộng khả năng tích hợp mà không làm phức tạp giao diện người dùng.
Động lực đằng sau Unified API
Trước đây, mỗi SDK của Sentry (Python, Java, JavaScript, v.v.) phát triển độc lập theo nhu cầu cụ thể và bối cảnh kỹ thuật riêng. Hệ quả là:
- Các khái niệm như "scope", "breadcrumb", hay "context" được triển khai với tên gọi, hành vi và phạm vi ảnh hưởng khác nhau.
- Một số SDK chỉ tập trung vào báo cáo lỗi (explicit client-centric), khiến việc gắn kết các tính năng phụ trợ như theo dõi hành trình người dùng (breadcrumbs) hoặc gắn thẻ ngữ cảnh trở nên cồng kềnh hoặc không khả thi.
- Tài liệu và hỗ trợ bị phân tán do thiếu sự nhất quán trong mô hình lập trình.
Nguyên tắc thiết kế then chốt
Unified API không chỉ là việc đổi tên hàm — đó là một hệ sinh thái được suy nghĩ lại từ gốc:
- Tính nhất quán ngôn ngữ: Tất cả SDK đều sử dụng cùng bộ từ vựng (ví dụ:
configureScope,addBreadcrumb,captureException) bất kể ngôn ngữ đích. - Tính mở rộng theo chiều dọc: Không chỉ báo lỗi — API hỗ trợ transaction monitoring, performance tracing, enrichment context, và event sampling ngay từ lớp trừu tượng cao nhất.
- Quản lý ngữ cảnh thông minh: Mỗi luồng thực thi (hoặc execution context trong môi trường không đồng bộ như JavaScript) có một hub riêng, quản lý một ngăn xếp scope — đảm bảo dữ liệu ngữ cảnh không bị rò rỉ giữa các yêu cầu hoặc tác vụ.
- Khả năng hoạt động khi chưa cấu hình: Các hàm như
addBreadcrumbhoặcconfigureScopekhông gây lỗi nếu SDK chưa được khởi tạo (init()). Chúng trở thành no-op, cho phép thư viện thứ ba tích hợp an toàn mà không cần kiểm tra trạng thái SDK. - Tính đa nền tảng thực tế: API tránh phụ thuộc vào cơ chế đặc thù (ví dụ: thread-local storage trong JS), thay vào đó tận dụng các mô hình sẵn có (như
AsyncLocalStoragetrong Node.js hoặcZone.jstrong trình duyệt).
Các thành phần lõi
HUB — Trung tâm điều phối ngữ cảnh
Mỗi hub là một đơn vị quản lý độc lập gồm một client (đã cấu hình) và một ngăn xếp scope. Trong JavaScript, hub mặc định được gắn với AsyncLocalStorage để duy trì ngữ cảnh qua chuỗi lời gọi bất đồng bộ.
import { Hub } from '@sentry/core';
const customHub = new Hub(
new Client({ dsn: 'https://xxx@sentry.io/123' }),
new Scope()
);
// Chuyển hub hiện tại sang customHub tạm thời
Hub.run(customHub, () => {
Sentry.captureMessage('This uses customHub');
});
SCOPE — Đóng gói dữ liệu đi kèm sự kiện
Scope chứa dữ liệu sẽ được tự động thêm vào mọi sự kiện gửi đi trong phạm vi đó: tags, contexts, user, level override, fingerprint, và cả các processor xử lý sự kiện.
Sentry.configureScope((scope) => {
scope.setContext('game', {
level: 42,
mode: 'hardcore',
lastCheckpoint: 'castle-dungeon'
});
scope.setTag('feature', 'multiplayer');
scope.setUser({ id: 'player_789', email: 'hero@example.com' });
});
CLIENT — Đơn vị chịu trách nhiệm xây dựng và gửi sự kiện
Client là thành phần vô trạng thái, nhận đầu vào là sự kiện thô và scope, rồi áp dụng quy tắc chuyển đổi, lọc, và gửi qua transport. Nó không quản lý ngữ cảnh — đó là vai trò của hub.
class CustomTransport extends BaseTransport {
async send(envelope) {
const payload = serializeEnvelope(envelope);
await fetch('https://custom-ingest.example.com/', {
method: 'POST',
body: payload,
headers: { 'Content-Type': 'application/x-sentry-envelope' }
});
}
}
const client = new Client({
dsn: 'https://xxx@sentry.io/123',
transport: () => new CustomTransport()
});
Luồng xử lý sự kiện (Event Pipeline)
Khi gọi captureException hoặc captureEvent, sự kiện trải qua chuỗi xử lý tuần tự:
- Kiểm tra trạng thái SDK: Nếu không có client hoạt động → sự kiện bị loại bỏ ngay lập tức.
- Lấy mẫu (Sampling): Dựa trên
tracesSampleRatehoặcsampleRateđể quyết định giữ hay loại bỏ. - Áp dụng scope: Gộp dữ liệu từ scope hiện tại (tags, contexts, user...) và chạy từng
eventProcessorđã đăng ký. - beforeSend hook: Cho phép sửa đổi hoặc hủy sự kiện trước khi gửi đi.
- Gửi qua transport: Đưa vào hàng đợi nội bộ; transport chịu trách nhiệm retry, rate limiting, và persistence nếu cần.
API tĩnh — Cửa ngõ dễ tiếp cận nhất
Các hàm cấp cao như Sentry.init(), Sentry.captureMessage(), hay Sentry.addBreadcrumb() đều là alias cho các phương thức tương ứng trên Hub::getCurrent(). Chúng che giấu độ phức tạp của hub và scope, nhưng vẫn đảm bảo tính đúng đắn trong môi trường bất đồng bộ.
// Tất cả đều tương đương với việc gọi trên hub hiện tại
Sentry.init({ dsn: 'https://...' });
Sentry.captureException(new Error('Boom!'));
Sentry.addBreadcrumb({ message: 'User clicked start', level: 'info' });
// Tương đương:
Hub.getCurrent().captureException(...);
Hub.getCurrent().addBreadcrumb(...);
Chế độ "minimal" — Tích hợp không ràng buộc
Gói @sentry/minimal cung cấp một lớp facade nhẹ, không phụ thuộc vào SDK đầy đủ. Khi không có SDK nào được cài đặt, mọi hàm đều trả về undefined hoặc không làm gì cả — giúp thư viện bên ngoài (ví dụ: một router hoặc logger) tích hợp breadcrumbs mà không lo crash hay phụ thuộc vòng.
// Trong thư viện thứ ba — không cần import SDK đầy đủ
import { addBreadcrumb } from '@sentry/minimal';
export function trackNavigation(to, from) {
addBreadcrumb({
category: 'navigation',
message: `From ${from} → ${to}`,
level: 'debug'
});
}