diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 766e66a72a99..01115522182b 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -256,10 +256,10 @@ export abstract class Client { // todo(v11): Remove the experimental flag // eslint-disable-next-line deprecation/deprecation - const enableMetrics = this._options.enableMetrics ?? this._options._experiments?.enableMetrics ?? true; + this._options.enableMetrics = this._options.enableMetrics ?? this._options._experiments?.enableMetrics ?? true; // Setup metric flushing with weight and timeout tracking - if (enableMetrics) { + if (this._options.enableMetrics) { setupWeightBasedFlushing( this, 'afterCaptureMetric', diff --git a/packages/effect/README.md b/packages/effect/README.md index a209c930b659..c04ff97446e4 100644 --- a/packages/effect/README.md +++ b/packages/effect/README.md @@ -23,7 +23,8 @@ const MainLive = HttpLive.pipe( Sentry.effectLayer({ dsn: '__DSN__', enableLogs: true, - enableMetrics: true, + enableEffectLogs: true, + enableEffectMetrics: true, }), ), ); @@ -35,8 +36,8 @@ The `effectLayer` function initializes Sentry and returns an Effect Layer that p - Distributed tracing with automatic HTTP header extraction/injection - Effect spans traced as Sentry spans -- Effect logs forwarded to Sentry (when `enableLogs` is set) -- Effect metrics sent to Sentry (when `enableMetrics` is set) +- Effect logs forwarded to Sentry (when `enableEffectLogs` is set) +- Effect metrics sent to Sentry (when `enableEffectMetrics` is set) ## Links diff --git a/packages/effect/src/client/index.ts b/packages/effect/src/client/index.ts index 58b21991a8f4..d26255f38e56 100644 --- a/packages/effect/src/client/index.ts +++ b/packages/effect/src/client/index.ts @@ -1,6 +1,7 @@ import type { BrowserOptions } from '@sentry/browser'; import type * as EffectLayer from 'effect/Layer'; import { suspend as suspendLayer } from 'effect/Layer'; +import type { EffectLayerBaseOptions } from '../utils/buildEffectLayer'; import { buildEffectLayer } from '../utils/buildEffectLayer'; import { init } from './sdk'; @@ -9,15 +10,15 @@ export { init } from './sdk'; /** * Options for the Sentry Effect client layer. */ -export type EffectClientLayerOptions = BrowserOptions; +export type EffectClientLayerOptions = BrowserOptions & EffectLayerBaseOptions; /** * Creates an Effect Layer that initializes Sentry for browser clients. * * This layer provides Effect applications with full Sentry instrumentation including: * - Effect spans traced as Sentry spans - * - Effect logs forwarded to Sentry (when `enableLogs` is set) - * - Effect metrics sent to Sentry (when `enableMetrics` is set) + * - Effect logs forwarded to Sentry (when `enableEffectLogs` is set) + * - Effect metrics sent to Sentry (when `enableEffectMetrics` is set) * * @example * ```typescript diff --git a/packages/effect/src/server/index.ts b/packages/effect/src/server/index.ts index 10d2a7651bf1..1ef0bc542877 100644 --- a/packages/effect/src/server/index.ts +++ b/packages/effect/src/server/index.ts @@ -1,5 +1,6 @@ import type { NodeOptions } from '@sentry/node-core/light'; import type * as EffectLayer from 'effect/Layer'; +import type { EffectLayerBaseOptions } from '../utils/buildEffectLayer'; import { buildEffectLayer } from '../utils/buildEffectLayer'; import { init } from './sdk'; @@ -8,15 +9,15 @@ export { init } from './sdk'; /** * Options for the Sentry Effect server layer. */ -export type EffectServerLayerOptions = NodeOptions; +export type EffectServerLayerOptions = NodeOptions & EffectLayerBaseOptions; /** * Creates an Effect Layer that initializes Sentry for Node.js servers. * * This layer provides Effect applications with full Sentry instrumentation including: * - Effect spans traced as Sentry spans - * - Effect logs forwarded to Sentry (when `enableLogs` is set) - * - Effect metrics sent to Sentry (when `enableMetrics` is set) + * - Effect logs forwarded to Sentry (when `enableEffectLogs` is set) + * - Effect metrics sent to Sentry (when `enableEffectMetrics` is set) * * @example * ```typescript @@ -28,8 +29,8 @@ export type EffectServerLayerOptions = NodeOptions; * const MainLive = HttpLive.pipe( * Layer.provide(Sentry.effectLayer({ * dsn: '__DSN__', - * enableLogs: true, - * enableMetrics: true, + * enableEffectLogs: true, + * enableEffectMetrics: true, * })), * ); * diff --git a/packages/effect/src/utils/buildEffectLayer.ts b/packages/effect/src/utils/buildEffectLayer.ts index 44393fe25731..36879e0b956d 100644 --- a/packages/effect/src/utils/buildEffectLayer.ts +++ b/packages/effect/src/utils/buildEffectLayer.ts @@ -1,3 +1,4 @@ +import type { Client } from '@sentry/core'; import type * as EffectLayer from 'effect/Layer'; import { empty as emptyLayer, provideMerge } from 'effect/Layer'; import { defaultLogger, replace as replaceLogger } from 'effect/Logger'; @@ -6,8 +7,8 @@ import { SentryEffectMetricsLayer } from '../metrics'; import { SentryEffectTracerLayer } from '../tracer'; export interface EffectLayerBaseOptions { - enableLogs?: boolean; - enableMetrics?: boolean; + enableEffectLogs?: boolean; + enableEffectMetrics?: boolean; } /** @@ -19,21 +20,22 @@ export interface EffectLayerBaseOptions { */ export function buildEffectLayer( options: T, - client: unknown, + client: Client | undefined, ): EffectLayer.Layer { if (!client) { return emptyLayer; } - const { enableLogs = false, enableMetrics = true } = options; + const clientOptions = client.getOptions(); + const { enableEffectLogs = false, enableEffectMetrics = false } = options; let layer: EffectLayer.Layer = SentryEffectTracerLayer; - if (enableLogs) { + if (enableEffectLogs && clientOptions.enableLogs) { const effectLogger = replaceLogger(defaultLogger, SentryEffectLogger); layer = layer.pipe(provideMerge(effectLogger)); } - if (enableMetrics) { + if (enableEffectMetrics && clientOptions.enableMetrics) { layer = layer.pipe(provideMerge(SentryEffectMetricsLayer)); } diff --git a/packages/effect/test/buildEffectLayer.test.ts b/packages/effect/test/buildEffectLayer.test.ts index a42aa7e82e26..d74045d5cb99 100644 --- a/packages/effect/test/buildEffectLayer.test.ts +++ b/packages/effect/test/buildEffectLayer.test.ts @@ -1,14 +1,31 @@ import { describe, expect, it, vi } from '@effect/vitest'; import * as sentryCore from '@sentry/core'; import { logger as sentryLogger } from '@sentry/core'; +import type { NodeOptions } from '@sentry/node-core'; import { Effect, Layer } from 'effect'; import { empty as emptyLayer } from 'effect/Layer'; +import { init } from '../src/index.server'; import { buildEffectLayer } from '../src/utils/buildEffectLayer'; +function getMockTransport() { + return () => ({ + send: vi.fn().mockResolvedValue({}), + flush: vi.fn().mockResolvedValue(true), + }); +} + +function createClient(options: NodeOptions = {}) { + return init({ + dsn: 'https://username@domain/123', + transport: getMockTransport(), + ...options, + }); +} + describe('buildEffectLayer', () => { describe('when client is falsy', () => { it('returns empty layer when client is null', () => { - const layer = buildEffectLayer({}, null); + const layer = buildEffectLayer({}, undefined); expect(layer).toBeDefined(); expect(Layer.isLayer(layer)).toBe(true); @@ -25,45 +42,49 @@ describe('buildEffectLayer', () => { }); describe('when client is truthy', () => { - const mockClient = { mock: true }; - it('returns a valid layer with default options', () => { - const layer = buildEffectLayer({}, mockClient); + const client = createClient(); + const layer = buildEffectLayer({}, client); expect(layer).toBeDefined(); expect(Layer.isLayer(layer)).toBe(true); }); - it('returns a valid layer with enableLogs: false', () => { - const layer = buildEffectLayer({ enableLogs: false }, mockClient); + it('returns a valid layer with enableEffectLogs: false', () => { + const client = createClient(); + const layer = buildEffectLayer({ enableEffectLogs: false }, client); expect(layer).toBeDefined(); expect(Layer.isLayer(layer)).toBe(true); }); - it('returns a valid layer with enableLogs: true', () => { - const layer = buildEffectLayer({ enableLogs: true }, mockClient); + it('returns a valid layer with enableEffectLogs: true', () => { + const client = createClient(); + const layer = buildEffectLayer({ enableEffectLogs: true }, client); expect(layer).toBeDefined(); expect(Layer.isLayer(layer)).toBe(true); }); - it('returns a valid layer with enableMetrics: false', () => { - const layer = buildEffectLayer({ enableMetrics: false }, mockClient); + it('returns a valid layer with enableEffectMetrics: false', () => { + const client = createClient(); + const layer = buildEffectLayer({ enableEffectMetrics: false }, client); expect(layer).toBeDefined(); expect(Layer.isLayer(layer)).toBe(true); }); - it('returns a valid layer with enableMetrics: true', () => { - const layer = buildEffectLayer({ enableMetrics: true }, mockClient); + it('returns a valid layer with enableEffectMetrics: true', () => { + const client = createClient(); + const layer = buildEffectLayer({ enableEffectMetrics: true }, client); expect(layer).toBeDefined(); expect(Layer.isLayer(layer)).toBe(true); }); it('returns a valid layer with all features enabled', () => { - const layer = buildEffectLayer({ enableLogs: true, enableMetrics: true }, mockClient); + const client = createClient(); + const layer = buildEffectLayer({ enableEffectLogs: true, enableEffectMetrics: true }, client); expect(layer).toBeDefined(); expect(Layer.isLayer(layer)).toBe(true); @@ -73,7 +94,7 @@ describe('buildEffectLayer', () => { Effect.gen(function* () { const result = yield* Effect.succeed('test-result'); expect(result).toBe('test-result'); - }).pipe(Effect.provide(buildEffectLayer({}, mockClient))), + }).pipe(Effect.provide(buildEffectLayer({}, createClient()))), ); it.effect('layer with logs enabled routes Effect logs to Sentry logger', () => @@ -82,12 +103,13 @@ describe('buildEffectLayer', () => { yield* Effect.log('test log message'); expect(infoSpy).toHaveBeenCalledWith('test log message'); infoSpy.mockRestore(); - }).pipe(Effect.provide(buildEffectLayer({ enableLogs: true }, mockClient))), + }).pipe(Effect.provide(buildEffectLayer({ enableEffectLogs: true }, createClient({ enableLogs: true })))), ); - it('returns different layer when enableMetrics is true vs false', () => { - const layerWithMetrics = buildEffectLayer({ enableMetrics: true }, mockClient); - const layerWithoutMetrics = buildEffectLayer({ enableMetrics: false }, mockClient); + it('returns different layer when enableEffectMetrics is true vs false', () => { + const client = createClient(); + const layerWithMetrics = buildEffectLayer({ enableEffectMetrics: true }, client); + const layerWithoutMetrics = buildEffectLayer({ enableEffectMetrics: false }, client); expect(layerWithMetrics).not.toBe(layerWithoutMetrics); }); @@ -96,7 +118,11 @@ describe('buildEffectLayer', () => { Effect.gen(function* () { const result = yield* Effect.succeed('all-features'); expect(result).toBe('all-features'); - }).pipe(Effect.provide(buildEffectLayer({ enableLogs: true, enableMetrics: true }, mockClient))), + }).pipe( + Effect.provide( + buildEffectLayer({ enableEffectLogs: true, enableEffectMetrics: true }, createClient({ enableLogs: true })), + ), + ), ); it.effect('layer enables tracing for Effect spans via Sentry tracer', () => @@ -110,22 +136,22 @@ describe('buildEffectLayer', () => { }), ); startInactiveSpanSpy.mockRestore(); - }).pipe(Effect.provide(buildEffectLayer({}, mockClient))), + }).pipe(Effect.provide(buildEffectLayer({}, createClient()))), ); }); describe('with additional options', () => { - const mockClient = { mock: true }; + const client = createClient({ enableLogs: true }); it('accepts options with additional properties', () => { const layer = buildEffectLayer( { - enableLogs: true, - enableMetrics: true, + enableEffectLogs: true, + enableEffectMetrics: true, dsn: 'https://test@sentry.io/123', debug: true, - } as { enableLogs?: boolean; enableMetrics?: boolean; dsn?: string; debug?: boolean }, - mockClient, + } as { enableEffectLogs?: boolean; enableEffectMetrics?: boolean; dsn?: string; debug?: boolean }, + client, ); expect(layer).toBeDefined(); diff --git a/packages/effect/test/layer.test.ts b/packages/effect/test/layer.test.ts index 8d96f039062b..420e12a8ed5c 100644 --- a/packages/effect/test/layer.test.ts +++ b/packages/effect/test/layer.test.ts @@ -63,7 +63,7 @@ describe.each([ const layer = effectLayer({ dsn: TEST_DSN, transport: getMockTransport(), - enableLogs: true, + enableEffectLogs: true, }); expect(layer).toBeDefined(); @@ -73,8 +73,8 @@ describe.each([ const layer = effectLayer({ dsn: TEST_DSN, transport: getMockTransport(), - enableLogs: true, - enableMetrics: true, + enableEffectLogs: true, + enableEffectMetrics: true, }); expect(layer).toBeDefined();