From 55220cf189b0d252e267578d66a48d953181953f Mon Sep 17 00:00:00 2001 From: Burak Yigit Kaya Date: Tue, 13 Aug 2024 15:59:06 +0100 Subject: [PATCH] feat: Add support for SENTRY_SPOTLIGHT env var in Node (#13325) Enable sending events to spotlight by setting the `SENTRY_SPOTLIGHT` environment variable --------- Co-authored-by: Burak Yigit Kaya --- .vscode/settings.json | 2 +- packages/node/src/preload.ts | 7 ++- packages/node/src/sdk/index.ts | 13 ++++- packages/node/src/utils/envToBool.ts | 38 +++++++++++++ packages/node/test/utils/envToBool.test.ts | 66 ++++++++++++++++++++++ 5 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 packages/node/src/utils/envToBool.ts create mode 100644 packages/node/test/utils/envToBool.test.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 615ca5b24472..4926554ffe4b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -42,5 +42,5 @@ "[typescript]": { "editor.defaultFormatter": "biomejs.biome" }, - "cSpell.words": ["arrayify"] + "cSpell.words": ["arrayify", "OTEL"] } diff --git a/packages/node/src/preload.ts b/packages/node/src/preload.ts index 0d62b28d9c91..0e4af146197f 100644 --- a/packages/node/src/preload.ts +++ b/packages/node/src/preload.ts @@ -1,13 +1,14 @@ import { preloadOpenTelemetry } from './sdk/initOtel'; +import { envToBool } from './utils/envToBool'; -const debug = !!process.env.SENTRY_DEBUG; +const debug = envToBool(process.env.SENTRY_DEBUG); const integrationsStr = process.env.SENTRY_PRELOAD_INTEGRATIONS; const integrations = integrationsStr ? integrationsStr.split(',').map(integration => integration.trim()) : undefined; /** - * The @sentry/node/preload export can be used with the node --import and --require args to preload the OTEL instrumentation, - * without initializing the Sentry SDK. + * The @sentry/node/preload export can be used with the node --import and --require args to preload the OTEL + * instrumentation, without initializing the Sentry SDK. * * This is useful if you cannot initialize the SDK immediately, but still want to preload the instrumentation, * e.g. if you have to load the DSN from somewhere else. diff --git a/packages/node/src/sdk/index.ts b/packages/node/src/sdk/index.ts index 1a20458802a0..7276e809875a 100644 --- a/packages/node/src/sdk/index.ts +++ b/packages/node/src/sdk/index.ts @@ -41,6 +41,7 @@ import { getAutoPerformanceIntegrations } from '../integrations/tracing'; import { makeNodeTransport } from '../transports'; import type { NodeClientOptions, NodeOptions } from '../types'; import { isCjs } from '../utils/commonjs'; +import { envToBool } from '../utils/envToBool'; import { defaultStackParser, getSentryRelease } from './api'; import { NodeClient } from './client'; import { initOpenTelemetry, maybeInitializeEsmLoader } from './initOtel'; @@ -221,6 +222,15 @@ function getClientOptions( ? true : options.autoSessionTracking; + if (options.spotlight == null) { + const spotlightEnv = envToBool(process.env.SENTRY_SPOTLIGHT, { strict: true }); + if (spotlightEnv == null) { + options.spotlight = process.env.SENTRY_SPOTLIGHT; + } else { + options.spotlight = spotlightEnv; + } + } + const tracesSampleRate = getTracesSampleRate(options.tracesSampleRate); const baseOptions = dropUndefinedKeys({ @@ -292,8 +302,7 @@ function getTracesSampleRate(tracesSampleRate: NodeOptions['tracesSampleRate']): * for more details. */ function updateScopeFromEnvVariables(): void { - const sentryUseEnvironment = (process.env.SENTRY_USE_ENVIRONMENT || '').toLowerCase(); - if (!['false', 'n', 'no', 'off', '0'].includes(sentryUseEnvironment)) { + if (envToBool(process.env.SENTRY_USE_ENVIRONMENT) !== false) { const sentryTraceEnv = process.env.SENTRY_TRACE; const baggageEnv = process.env.SENTRY_BAGGAGE; const propagationContext = propagationContextFromHeaders(sentryTraceEnv, baggageEnv); diff --git a/packages/node/src/utils/envToBool.ts b/packages/node/src/utils/envToBool.ts new file mode 100644 index 000000000000..4f7fd2201ce8 --- /dev/null +++ b/packages/node/src/utils/envToBool.ts @@ -0,0 +1,38 @@ +export const FALSY_ENV_VALUES = new Set(['false', 'f', 'n', 'no', 'off', '0']); +export const TRUTHY_ENV_VALUES = new Set(['true', 't', 'y', 'yes', 'on', '1']); + +export type StrictBoolCast = { + strict: true; +}; + +export type LooseBoolCast = { + strict?: false; +}; + +export type BoolCastOptions = StrictBoolCast | LooseBoolCast; + +export function envToBool(value: unknown, options?: LooseBoolCast): boolean; +export function envToBool(value: unknown, options: StrictBoolCast): boolean | null; +export function envToBool(value: unknown, options?: BoolCastOptions): boolean | null; +/** + * A helper function which casts an ENV variable value to `true` or `false` using the constants defined above. + * In strict mode, it may return `null` if the value doesn't match any of the predefined values. + * + * @param value The value of the env variable + * @param options -- Only has `strict` key for now, which requires a strict match for `true` in TRUTHY_ENV_VALUES + * @returns true/false if the lowercase value matches the predefined values above. If not, null in strict mode, + * and Boolean(value) in loose mode. + */ +export function envToBool(value: unknown, options?: BoolCastOptions): boolean | null { + const normalized = String(value).toLowerCase(); + + if (FALSY_ENV_VALUES.has(normalized)) { + return false; + } + + if (TRUTHY_ENV_VALUES.has(normalized)) { + return true; + } + + return options && options.strict ? null : Boolean(value); +} diff --git a/packages/node/test/utils/envToBool.test.ts b/packages/node/test/utils/envToBool.test.ts new file mode 100644 index 000000000000..cd06ef132267 --- /dev/null +++ b/packages/node/test/utils/envToBool.test.ts @@ -0,0 +1,66 @@ +import { envToBool } from '../../src/utils/envToBool'; + +describe('envToBool', () => { + it.each([ + ['', true, null], + ['', false, false], + ['t', true, true], + ['T', true, true], + ['t', false, true], + ['T', false, true], + ['y', true, true], + ['Y', true, true], + ['y', false, true], + ['Y', false, true], + ['1', true, true], + ['1', false, true], + ['true', true, true], + ['true', false, true], + ['tRuE', true, true], + ['tRuE', false, true], + ['Yes', true, true], + ['Yes', false, true], + ['yes', true, true], + ['yes', false, true], + ['yEs', true, true], + ['yEs', false, true], + ['On', true, true], + ['On', false, true], + ['on', true, true], + ['on', false, true], + ['oN', true, true], + ['oN', false, true], + ['f', true, false], + ['f', false, false], + ['n', true, false], + ['N', true, false], + ['n', false, false], + ['N', false, false], + ['0', true, false], + ['0', false, false], + ['false', true, false], + ['false', false, false], + ['false', true, false], + ['false', false, false], + ['FaLsE', true, false], + ['FaLsE', false, false], + ['No', true, false], + ['No', false, false], + ['no', true, false], + ['no', false, false], + ['nO', true, false], + ['nO', false, false], + ['Off', true, false], + ['Off', false, false], + ['off', true, false], + ['off', false, false], + ['oFf', true, false], + ['oFf', false, false], + ['xxx', true, null], + ['xxx', false, true], + [undefined, false, false], + [undefined, true, null], + ])('%s becomes (strict: %s): %s', (value, strict, expected) => { + expect(envToBool(value, { strict })).toBe(expected); + }); +});