sentry-javascript为语言包,在该语言包下包含各项平台包 其中:
- 诸如react、vue、next等框架包为平台包,主要是引用browser功能并增添平台相关特性
- browser包为浏览器侧的统一内容封装,降低各平台间差异,其主要部分来自于core及utils
- core封装如hub、client、integration等核心概念,其hub部分由hub等于单独封装
- utils封装平台、模块无关的公用方法
当调用init时:
- 平台相关参数集成与默认参数处理
- 将客户端相关参数层层解析初始化最终传递进hub中
- 获取主载体环境(global)
- 在该载体上获取或创建hub
- 将客户端环境与该hub绑定
- 初始化设置客户端环境相关integration
- 如果不传递参数默认该客户端环境绑定默认integration
以下以react平台为例:
sentry/react
index.js
export * from '@sentry/browser';
export { init } from './sdk';
export { Profiler, withProfiler, useProfiler } from './profiler';
export type { ErrorBoundaryProps, FallbackRender } from './errorboundary';
export { ErrorBoundary, withErrorBoundary } from './errorboundary';
export { createReduxEnhancer } from './redux';
export { reactRouterV3Instrumentation } from './reactrouterv3';
export { reactRouterV4Instrumentation, reactRouterV5Instrumentation, withSentryRouting } from './reactrouter';
export { reactRouterV6Instrumentation, withSentryReactRouterV6Routing, wrapUseRoutes } from './reactrouterv6';
sdk
- 增加包相关元信息作为参数
- 调用browserInit进行初始化
import { BrowserOptions, init as browserInit, SDK_VERSION } from '@sentry/browser';
export function init(options: BrowserOptions): void {
options._metadata = options._metadata || {};
options._metadata.sdk = options._metadata.sdk || {
name: 'sentry.javascript.react',
packages: [
{
name: 'npm:@sentry/react',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};
browserInit(options);
}
sentry/browser
index.js
export * from './exports';
import { Integrations as CoreIntegrations } from '@sentry/core';
import { getGlobalObject } from '@sentry/utils';
import * as BrowserIntegrations from './integrations';
let windowIntegrations = {};
const _window = getGlobalObject<Window>();
if (_window.Sentry && _window.Sentry.Integrations) {
windowIntegrations = _window.Sentry.Integrations;
}
const INTEGRATIONS = {
...windowIntegrations,
...CoreIntegrations,
...BrowserIntegrations,
};
export { INTEGRATIONS as Integrations };
integration/index.js
export { GlobalHandlers } from './globalhandlers';
export { TryCatch } from './trycatch';
export { Breadcrumbs } from './breadcrumbs';
export { LinkedErrors } from './linkederrors';
export { HttpContext } from './httpcontext';
export { Dedupe } from './dedupe';
sdk.ts
export function init(options: BrowserOptions = {}): void {
if (options.defaultIntegrations === undefined) {
options.defaultIntegrations = defaultIntegrations;
}
if (options.release === undefined) {
const window = getGlobalObject<Window>();
if (window.SENTRY_RELEASE && window.SENTRY_RELEASE.id) {
options.release = window.SENTRY_RELEASE.id;
}
}
if (options.autoSessionTracking === undefined) {
options.autoSessionTracking = true;
}
if (options.sendClientReports === undefined) {
options.sendClientReports = true;
}
const clientOptions: BrowserClientOptions = {
...options,
stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),
integrations: getIntegrationsToSetup(options),
transport: options.transport || (supportsFetch() ? makeFetchTransport : makeXHRTransport),
};
initAndBind(BrowserClient, clientOptions);
if (options.autoSessionTracking) {
startSessionTracking();
}
}
sentry/core
index.js
export type { ClientClass } from './sdk';
export {
addBreadcrumb,
captureException,
captureEvent,
captureMessage,
configureScope,
startTransaction,
setContext,
setExtra,
setExtras,
setTag,
setTags,
setUser,
withScope,
addGlobalEventProcessor,
getCurrentHub,
getHubFromCarrier,
Hub,
makeMain,
Scope,
} from '@sentry/hub';
export { getEnvelopeEndpointWithUrlEncodedAuth, getReportDialogEndpoint } from './api';
export { BaseClient } from './baseclient';
export { initAndBind } from './sdk';
export { createTransport } from './transports/base';
export { SDK_VERSION } from './version';
export { getIntegrationsToSetup } from './integration';
export { FunctionToString, InboundFilters } from './integrations';
import * as Integrations from './integrations';
export { Integrations };
sdk.ts
import { getCurrentHub } from '@sentry/hub';
import { Client, ClientOptions } from '@sentry/types';
import { logger } from '@sentry/utils';
export type ClientClass<F extends Client, O extends ClientOptions> = new (options: O) => F;
export function initAndBind<F extends Client, O extends ClientOptions>(
clientClass: ClientClass<F, O>,
options: O,
): void {
if (options.debug === true) {
if (__DEBUG_BUILD__) {
logger.enable();
} else {
console.warn('[Sentry] Cannot initialize SDK with `debug` option using a non-debug bundle.');
}
}
const hub = getCurrentHub();
const scope = hub.getScope();
if (scope) {
scope.update(options.initialScope);
}
const client = new clientClass(options);
hub.bindClient(client);
}
integration.ts
import { addGlobalEventProcessor, getCurrentHub } from '@sentry/hub';
import { Integration, Options } from '@sentry/types';
import { arrayify, logger } from '@sentry/utils';
declare module '@sentry/types' {
interface Integration {
isDefaultInstance?: boolean;
}
}
export const installedIntegrations: string[] = [];
export type IntegrationIndex = {
[key: string]: Integration;
};
function filterDuplicates(integrations: Integration[]): Integration[] {
const integrationsByName: { [key: string]: Integration } = {};
integrations.forEach(currentInstance => {
const { name } = currentInstance;
const existingInstance = integrationsByName[name];
if (existingInstance && !existingInstance.isDefaultInstance && currentInstance.isDefaultInstance) {
return;
}
integrationsByName[name] = currentInstance;
});
return Object.values(integrationsByName);
}
export function getIntegrationsToSetup(options: Options): Integration[] {
const defaultIntegrations = options.defaultIntegrations || [];
const userIntegrations = options.integrations;
defaultIntegrations.forEach(integration => {
integration.isDefaultInstance = true;
});
let integrations: Integration[];
if (Array.isArray(userIntegrations)) {
integrations = [...defaultIntegrations, ...userIntegrations];
} else if (typeof userIntegrations === 'function') {
integrations = arrayify(userIntegrations(defaultIntegrations));
} else {
integrations = defaultIntegrations;
}
const finalIntegrations = filterDuplicates(integrations);
const debugIndex = finalIntegrations.findIndex(integration => integration.name === 'Debug');
if (debugIndex !== -1) {
const [debugInstance] = finalIntegrations.splice(debugIndex, 1);
finalIntegrations.push(debugInstance);
}
return finalIntegrations;
}
export function setupIntegrations(integrations: Integration[]): IntegrationIndex {
const integrationIndex: IntegrationIndex = {};
integrations.forEach(integration => {
integrationIndex[integration.name] = integration;
if (installedIntegrations.indexOf(integration.name) === -1) {
integration.setupOnce(addGlobalEventProcessor, getCurrentHub);
installedIntegrations.push(integration.name);
__DEBUG_BUILD__ && logger.log(`Integration installed: ${integration.name}`);
}
});
return integrationIndex;
}
sentry/hub
- 使用client参数生成hub
- 初始化创建integration进行监控
hub.ts
export class Hub implements HubInterface {
private readonly _stack: Layer[] = [{}];
private _lastEventId?: string;
public constructor(client?: Client, scope: Scope = new Scope(), private readonly _version: number = API_VERSION) {
this.getStackTop().scope = scope;
if (client) {
this.bindClient(client);
}
}
public bindClient(client?: Client): void {
const top = this.getStackTop();
top.client = client;
if (client && client.setupIntegrations) {
client.setupIntegrations();
}
}
}
|