From ffb750fe7e93108f8750aa98dbe1e04f03e88d90 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 11 Jul 2025 09:52:11 +0200 Subject: [PATCH] ref(core): Keep client-logger map on carrier & avoid side-effect Instead of having a side effect of writing on the GLOBAL_OBJ, we can use a global singleton instead. --- packages/core/src/carrier.ts | 7 +++++++ packages/core/src/logs/exports.ts | 20 ++++++++++++-------- packages/core/src/utils/worldwide.ts | 8 -------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/packages/core/src/carrier.ts b/packages/core/src/carrier.ts index 31827ad7b87e..b6d30082f847 100644 --- a/packages/core/src/carrier.ts +++ b/packages/core/src/carrier.ts @@ -1,6 +1,8 @@ import type { AsyncContextStack } from './asyncContext/stackStrategy'; import type { AsyncContextStrategy } from './asyncContext/types'; +import type { Client } from './client'; import type { Scope } from './scope'; +import type { SerializedLog } from './types-hoist/log'; import type { Logger } from './utils/logger'; import { SDK_VERSION } from './utils/version'; import { GLOBAL_OBJ } from './utils/worldwide'; @@ -27,6 +29,11 @@ export interface SentryCarrier { /** @deprecated Logger is no longer set. Instead, we keep enabled state in loggerSettings. */ logger?: Logger; loggerSettings?: { enabled: boolean }; + /** + * A map of Sentry clients to their log buffers. + * This is used to store logs that are sent to Sentry. + */ + clientToLogBufferMap?: WeakMap>; /** Overwrites TextEncoder used in `@sentry/core`, need for `react-native@0.73` and older */ encodePolyfill?: (input: string) => Uint8Array; diff --git a/packages/core/src/logs/exports.ts b/packages/core/src/logs/exports.ts index 8d8a2a292df8..641b1ab54651 100644 --- a/packages/core/src/logs/exports.ts +++ b/packages/core/src/logs/exports.ts @@ -1,3 +1,4 @@ +import { getGlobalSingleton } from '../carrier'; import type { Client } from '../client'; import { _getTraceInfoFromScope } from '../client'; import { getClient, getCurrentScope, getGlobalScope, getIsolationScope } from '../currentScopes'; @@ -9,15 +10,11 @@ import { isParameterizedString } from '../utils/is'; import { debug } from '../utils/logger'; import { _getSpanForScope } from '../utils/spanOnScope'; import { timestampInSeconds } from '../utils/time'; -import { GLOBAL_OBJ } from '../utils/worldwide'; import { SEVERITY_TEXT_TO_SEVERITY_NUMBER } from './constants'; import { createLogEnvelope } from './envelope'; const MAX_LOG_BUFFER_SIZE = 100; -// The reference to the Client <> LogBuffer map is stored to ensure it's always the same -GLOBAL_OBJ._sentryClientToLogBufferMap = new WeakMap>(); - /** * Converts a log attribute to a serialized log attribute. * @@ -92,11 +89,13 @@ function setLogAttribute( * the stable Sentry SDK API and can be changed or removed without warning. */ export function _INTERNAL_captureSerializedLog(client: Client, serializedLog: SerializedLog): void { + const bufferMap = _getBufferMap(); + const logBuffer = _INTERNAL_getLogBuffer(client); if (logBuffer === undefined) { - GLOBAL_OBJ._sentryClientToLogBufferMap?.set(client, [serializedLog]); + bufferMap.set(client, [serializedLog]); } else { - GLOBAL_OBJ._sentryClientToLogBufferMap?.set(client, [...logBuffer, serializedLog]); + bufferMap.set(client, [...logBuffer, serializedLog]); if (logBuffer.length >= MAX_LOG_BUFFER_SIZE) { _INTERNAL_flushLogsBuffer(client, logBuffer); } @@ -217,7 +216,7 @@ export function _INTERNAL_flushLogsBuffer(client: Client, maybeLogBuffer?: Array const envelope = createLogEnvelope(logBuffer, clientOptions._metadata, clientOptions.tunnel, client.getDsn()); // Clear the log buffer after envelopes have been constructed. - GLOBAL_OBJ._sentryClientToLogBufferMap?.set(client, []); + _getBufferMap().set(client, []); client.emit('flushLogs'); @@ -235,7 +234,7 @@ export function _INTERNAL_flushLogsBuffer(client: Client, maybeLogBuffer?: Array * @returns The log buffer for the given client. */ export function _INTERNAL_getLogBuffer(client: Client): Array | undefined { - return GLOBAL_OBJ._sentryClientToLogBufferMap?.get(client); + return _getBufferMap().get(client); } /** @@ -251,3 +250,8 @@ function getMergedScopeData(currentScope: Scope): ScopeData { mergeScopeData(scopeData, currentScope.getScopeData()); return scopeData; } + +function _getBufferMap(): WeakMap> { + // The reference to the Client <> LogBuffer map is stored on the carrier to ensure it's always the same + return getGlobalSingleton('clientToLogBufferMap', () => new WeakMap>()); +} diff --git a/packages/core/src/utils/worldwide.ts b/packages/core/src/utils/worldwide.ts index 0b7d763f3007..c6442d2308a9 100644 --- a/packages/core/src/utils/worldwide.ts +++ b/packages/core/src/utils/worldwide.ts @@ -13,8 +13,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { Carrier } from '../carrier'; -import type { Client } from '../client'; -import type { SerializedLog } from '../types-hoist/log'; import type { Span } from '../types-hoist/span'; import type { SdkSource } from './env'; @@ -38,12 +36,6 @@ export type InternalGlobal = { id?: string; }; SENTRY_SDK_SOURCE?: SdkSource; - /** - * A map of Sentry clients to their log buffers. - * - * This is used to store logs that are sent to Sentry. - */ - _sentryClientToLogBufferMap?: WeakMap>; /** * Debug IDs are indirectly injected by Sentry CLI or bundler plugins to directly reference a particular source map * for resolving of a source file. The injected code will place an entry into the record for each loaded bundle/JS