UNPKG

@sentry/core

Version:
558 lines (489 loc) 14 kB
Object.defineProperty(exports, '__esModule', { value: true }); const utils = require('@sentry/utils'); const constants = require('./constants.js'); const scope = require('./scope.js'); const session = require('./session.js'); /** * API compatibility version of this hub. * * WARNING: This number should only be increased when the global interface * changes and new methods are introduced. * * @hidden */ const API_VERSION = 4; /** * Default maximum number of breadcrumbs added to an event. Can be overwritten * with {@link Options.maxBreadcrumbs}. */ const DEFAULT_BREADCRUMBS = 100; /** * A layer in the process stack. * @hidden */ /** * @inheritDoc */ class Hub { /** Is a {@link Layer}[] containing the client and scope */ __init() {this._stack = [{}];} /** Contains the last event id of a captured event. */ /** * Creates a new instance of the hub, will push one {@link Layer} into the * internal stack on creation. * * @param client bound to the hub. * @param scope bound to the hub. * @param version number, higher number means higher priority. */ constructor(client, scope$1 = new scope.Scope(), _version = API_VERSION) {this._version = _version;Hub.prototype.__init.call(this); this.getStackTop().scope = scope$1; if (client) { this.bindClient(client); } } /** * @inheritDoc */ isOlderThan(version) { return this._version < version; } /** * @inheritDoc */ bindClient(client) { const top = this.getStackTop(); top.client = client; if (client && client.setupIntegrations) { client.setupIntegrations(); } } /** * @inheritDoc */ pushScope() { // We want to clone the content of prev scope const scope$1 = scope.Scope.clone(this.getScope()); this.getStack().push({ client: this.getClient(), scope: scope$1, }); return scope$1; } /** * @inheritDoc */ popScope() { if (this.getStack().length <= 1) return false; return !!this.getStack().pop(); } /** * @inheritDoc */ withScope(callback) { const scope = this.pushScope(); try { callback(scope); } finally { this.popScope(); } } /** * @inheritDoc */ getClient() { return this.getStackTop().client ; } /** Returns the scope of the top stack. */ getScope() { return this.getStackTop().scope; } /** Returns the scope stack for domains or the process. */ getStack() { return this._stack; } /** Returns the topmost scope layer in the order domain > local > process. */ getStackTop() { return this._stack[this._stack.length - 1]; } /** * @inheritDoc */ captureException(exception, hint) { const eventId = (this._lastEventId = hint && hint.event_id ? hint.event_id : utils.uuid4()); const syntheticException = new Error('Sentry syntheticException'); this._withClient((client, scope) => { client.captureException( exception, { originalException: exception, syntheticException, ...hint, event_id: eventId, }, scope, ); }); return eventId; } /** * @inheritDoc */ captureMessage( message, // eslint-disable-next-line deprecation/deprecation level, hint, ) { const eventId = (this._lastEventId = hint && hint.event_id ? hint.event_id : utils.uuid4()); const syntheticException = new Error(message); this._withClient((client, scope) => { client.captureMessage( message, level, { originalException: message, syntheticException, ...hint, event_id: eventId, }, scope, ); }); return eventId; } /** * @inheritDoc */ captureEvent(event, hint) { const eventId = hint && hint.event_id ? hint.event_id : utils.uuid4(); if (!event.type) { this._lastEventId = eventId; } this._withClient((client, scope) => { client.captureEvent(event, { ...hint, event_id: eventId }, scope); }); return eventId; } /** * @inheritDoc */ lastEventId() { return this._lastEventId; } /** * @inheritDoc */ addBreadcrumb(breadcrumb, hint) { const { scope, client } = this.getStackTop(); if (!scope || !client) return; const { beforeBreadcrumb = null, maxBreadcrumbs = DEFAULT_BREADCRUMBS } = (client.getOptions && client.getOptions()) || {}; if (maxBreadcrumbs <= 0) return; const timestamp = utils.dateTimestampInSeconds(); const mergedBreadcrumb = { timestamp, ...breadcrumb }; const finalBreadcrumb = beforeBreadcrumb ? (utils.consoleSandbox(() => beforeBreadcrumb(mergedBreadcrumb, hint)) ) : mergedBreadcrumb; if (finalBreadcrumb === null) return; if (client.emit) { client.emit('beforeAddBreadcrumb', finalBreadcrumb, hint); } scope.addBreadcrumb(finalBreadcrumb, maxBreadcrumbs); } /** * @inheritDoc */ setUser(user) { const scope = this.getScope(); if (scope) scope.setUser(user); } /** * @inheritDoc */ setTags(tags) { const scope = this.getScope(); if (scope) scope.setTags(tags); } /** * @inheritDoc */ setExtras(extras) { const scope = this.getScope(); if (scope) scope.setExtras(extras); } /** * @inheritDoc */ setTag(key, value) { const scope = this.getScope(); if (scope) scope.setTag(key, value); } /** * @inheritDoc */ setExtra(key, extra) { const scope = this.getScope(); if (scope) scope.setExtra(key, extra); } /** * @inheritDoc */ // eslint-disable-next-line @typescript-eslint/no-explicit-any setContext(name, context) { const scope = this.getScope(); if (scope) scope.setContext(name, context); } /** * @inheritDoc */ configureScope(callback) { const { scope, client } = this.getStackTop(); if (scope && client) { callback(scope); } } /** * @inheritDoc */ run(callback) { const oldHub = makeMain(this); try { callback(this); } finally { makeMain(oldHub); } } /** * @inheritDoc */ getIntegration(integration) { const client = this.getClient(); if (!client) return null; try { return client.getIntegration(integration); } catch (_oO) { (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && utils.logger.warn(`Cannot retrieve integration ${integration.id} from the current Hub`); return null; } } /** * @inheritDoc */ startTransaction(context, customSamplingContext) { return this._callExtensionMethod('startTransaction', context, customSamplingContext); } /** * @inheritDoc */ traceHeaders() { return this._callExtensionMethod('traceHeaders'); } /** * @inheritDoc */ captureSession(endSession = false) { // both send the update and pull the session from the scope if (endSession) { return this.endSession(); } // only send the update this._sendSessionUpdate(); } /** * @inheritDoc */ endSession() { const layer = this.getStackTop(); const scope = layer && layer.scope; const session$1 = scope && scope.getSession(); if (session$1) { session.closeSession(session$1); } this._sendSessionUpdate(); // the session is over; take it off of the scope if (scope) { scope.setSession(); } } /** * @inheritDoc */ startSession(context) { const { scope, client } = this.getStackTop(); const { release, environment = constants.DEFAULT_ENVIRONMENT } = (client && client.getOptions()) || {}; // Will fetch userAgent if called from browser sdk const { userAgent } = utils.GLOBAL_OBJ.navigator || {}; const session$1 = session.makeSession({ release, environment, ...(scope && { user: scope.getUser() }), ...(userAgent && { userAgent }), ...context, }); if (scope) { // End existing session if there's one const currentSession = scope.getSession && scope.getSession(); if (currentSession && currentSession.status === 'ok') { session.updateSession(currentSession, { status: 'exited' }); } this.endSession(); // Afterwards we set the new session on the scope scope.setSession(session$1); } return session$1; } /** * Returns if default PII should be sent to Sentry and propagated in ourgoing requests * when Tracing is used. */ shouldSendDefaultPii() { const client = this.getClient(); const options = client && client.getOptions(); return Boolean(options && options.sendDefaultPii); } /** * Sends the current Session on the scope */ _sendSessionUpdate() { const { scope, client } = this.getStackTop(); if (!scope) return; const session = scope.getSession(); if (session) { if (client && client.captureSession) { client.captureSession(session); } } } /** * Internal helper function to call a method on the top client if it exists. * * @param method The method to call on the client. * @param args Arguments to pass to the client function. */ _withClient(callback) { const { scope, client } = this.getStackTop(); if (client) { callback(client, scope); } } /** * Calls global extension method and binding current instance to the function call */ // @ts-ignore Function lacks ending return statement and return type does not include 'undefined'. ts(2366) // eslint-disable-next-line @typescript-eslint/no-explicit-any _callExtensionMethod(method, ...args) { const carrier = getMainCarrier(); const sentry = carrier.__SENTRY__; if (sentry && sentry.extensions && typeof sentry.extensions[method] === 'function') { return sentry.extensions[method].apply(this, args); } (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && utils.logger.warn(`Extension method ${method} couldn't be found, doing nothing.`); } } /** * Returns the global shim registry. * * FIXME: This function is problematic, because despite always returning a valid Carrier, * it has an optional `__SENTRY__` property, which then in turn requires us to always perform an unnecessary check * at the call-site. We always access the carrier through this function, so we can guarantee that `__SENTRY__` is there. **/ function getMainCarrier() { utils.GLOBAL_OBJ.__SENTRY__ = utils.GLOBAL_OBJ.__SENTRY__ || { extensions: {}, hub: undefined, }; return utils.GLOBAL_OBJ; } /** * Replaces the current main hub with the passed one on the global object * * @returns The old replaced hub */ function makeMain(hub) { const registry = getMainCarrier(); const oldHub = getHubFromCarrier(registry); setHubOnCarrier(registry, hub); return oldHub; } /** * Returns the default hub instance. * * If a hub is already registered in the global carrier but this module * contains a more recent version, it replaces the registered version. * Otherwise, the currently registered hub will be returned. */ function getCurrentHub() { // Get main carrier (global for every environment) const registry = getMainCarrier(); // If there's no hub, or its an old API, assign a new one if (!hasHubOnCarrier(registry) || getHubFromCarrier(registry).isOlderThan(API_VERSION)) { setHubOnCarrier(registry, new Hub()); } // Prefer domains over global if they are there (applicable only to Node environment) if (utils.isNodeEnv()) { return getHubFromActiveDomain(registry); } // Return hub that lives on a global object return getHubFromCarrier(registry); } /** * Try to read the hub from an active domain, and fallback to the registry if one doesn't exist * @returns discovered hub */ function getHubFromActiveDomain(registry) { try { const sentry = getMainCarrier().__SENTRY__; const activeDomain = sentry && sentry.extensions && sentry.extensions.domain && sentry.extensions.domain.active; // If there's no active domain, just return global hub if (!activeDomain) { return getHubFromCarrier(registry); } // If there's no hub on current domain, or it's an old API, assign a new one if (!hasHubOnCarrier(activeDomain) || getHubFromCarrier(activeDomain).isOlderThan(API_VERSION)) { const registryHubTopStack = getHubFromCarrier(registry).getStackTop(); setHubOnCarrier(activeDomain, new Hub(registryHubTopStack.client, scope.Scope.clone(registryHubTopStack.scope))); } // Return hub that lives on a domain return getHubFromCarrier(activeDomain); } catch (_Oo) { // Return hub that lives on a global object return getHubFromCarrier(registry); } } /** * This will tell whether a carrier has a hub on it or not * @param carrier object */ function hasHubOnCarrier(carrier) { return !!(carrier && carrier.__SENTRY__ && carrier.__SENTRY__.hub); } /** * This will create a new {@link Hub} and add to the passed object on * __SENTRY__.hub. * @param carrier object * @hidden */ function getHubFromCarrier(carrier) { return utils.getGlobalSingleton('hub', () => new Hub(), carrier); } /** * This will set passed {@link Hub} on the passed object's __SENTRY__.hub attribute * @param carrier object * @param hub Hub * @returns A boolean indicating success or failure */ function setHubOnCarrier(carrier, hub) { if (!carrier) return false; const __SENTRY__ = (carrier.__SENTRY__ = carrier.__SENTRY__ || {}); __SENTRY__.hub = hub; return true; } exports.API_VERSION = API_VERSION; exports.Hub = Hub; exports.getCurrentHub = getCurrentHub; exports.getHubFromCarrier = getHubFromCarrier; exports.getMainCarrier = getMainCarrier; exports.makeMain = makeMain; exports.setHubOnCarrier = setHubOnCarrier; //# sourceMappingURL=hub.js.map