Skip to content

Commit 56f59d3

Browse files
committed
feat(effect): Remove effectLayer auto composition
1 parent 8af0f44 commit 56f59d3

File tree

12 files changed

+180
-413
lines changed

12 files changed

+180
-413
lines changed

dev-packages/e2e-tests/test-applications/effect-browser/src/index.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
// @ts-check
22
import * as Sentry from '@sentry/effect';
3-
import { Cause, Effect, Layer, Logger, LogLevel, Runtime } from 'effect';
3+
import * as Logger from 'effect/Logger';
4+
import * as Layer from 'effect/Layer';
5+
import * as Runtime from 'effect/Runtime';
6+
import * as LogLevel from 'effect/LogLevel';
7+
import * as Effect from 'effect/Effect';
48

59
const LogLevelLive = Logger.minimumLogLevel(LogLevel.Debug);
610
const AppLayer = Layer.mergeAll(
@@ -16,8 +20,9 @@ const AppLayer = Layer.mergeAll(
1620
environment: 'qa',
1721
tunnel: 'http://localhost:3031',
1822
enableLogs: true,
19-
enableEffectLogs: true,
2023
}),
24+
Layer.setTracer(Sentry.SentryEffectTracer),
25+
Logger.replace(Logger.defaultLogger, Sentry.SentryEffectLogger),
2126
LogLevelLive,
2227
);
2328

dev-packages/e2e-tests/test-applications/effect-browser/tests/transactions.test.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,6 @@ test('captures Effect spans with correct parent-child structure', async ({ page
105105
expect(spans).toContainEqual(
106106
expect.objectContaining({
107107
description: 'custom-effect-span',
108-
data: expect.objectContaining({
109-
'sentry.op': 'internal',
110-
}),
111108
}),
112109
);
113110

dev-packages/e2e-tests/test-applications/effect-node/src/app.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
import * as Sentry from '@sentry/effect';
22
import { HttpRouter, HttpServer, HttpServerResponse } from '@effect/platform';
33
import { NodeHttpServer, NodeRuntime } from '@effect/platform-node';
4-
import { Cause, Effect, Layer, Logger, LogLevel } from 'effect';
4+
import * as Effect from 'effect/Effect';
5+
import * as Cause from 'effect/Cause';
6+
import * as Layer from 'effect/Layer';
7+
import * as Logger from 'effect/Logger';
8+
import * as LogLevel from 'effect/LogLevel';
59
import { createServer } from 'http';
610

7-
const SentryLive = Sentry.effectLayer({
8-
dsn: process.env.E2E_TEST_DSN,
9-
environment: 'qa',
10-
debug: !!process.env.DEBUG,
11-
tunnel: 'http://localhost:3031/',
12-
tracesSampleRate: 1,
13-
enableLogs: true,
14-
enableEffectLogs: true,
15-
});
11+
const SentryLive = Layer.mergeAll(
12+
Sentry.effectLayer({
13+
dsn: process.env.E2E_TEST_DSN,
14+
environment: 'qa',
15+
debug: !!process.env.DEBUG,
16+
tunnel: 'http://localhost:3031/',
17+
tracesSampleRate: 1,
18+
enableLogs: true,
19+
}),
20+
Layer.setTracer(Sentry.SentryEffectTracer),
21+
Logger.replace(Logger.defaultLogger, Sentry.SentryEffectLogger),
22+
);
1623

1724
const router = HttpRouter.empty.pipe(
1825
HttpRouter.get('/test-success', HttpServerResponse.json({ version: 'v1' })),

dev-packages/e2e-tests/test-applications/effect-node/tests/transactions.test.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,29 @@ import { waitForTransaction } from '@sentry-internal/test-utils';
33

44
test('Sends an HTTP transaction', async ({ baseURL }) => {
55
const transactionEventPromise = waitForTransaction('effect-node', transactionEvent => {
6-
return (
7-
transactionEvent?.contexts?.trace?.op === 'http.server' &&
8-
transactionEvent?.transaction?.includes('/test-success')
9-
);
6+
return transactionEvent?.transaction === 'http.server GET';
107
});
118

129
await fetch(`${baseURL}/test-success`);
1310

1411
const transactionEvent = await transactionEventPromise;
1512

16-
expect(transactionEvent.contexts?.trace?.op).toBe('http.server');
17-
expect(transactionEvent.transaction).toContain('/test-success');
13+
expect(transactionEvent.transaction).toBe('http.server GET');
1814
});
1915

2016
test('Sends transaction with manual Effect span', async ({ baseURL }) => {
2117
const transactionEventPromise = waitForTransaction('effect-node', transactionEvent => {
2218
return (
23-
transactionEvent?.contexts?.trace?.op === 'http.server' &&
24-
transactionEvent?.transaction?.includes('/test-transaction')
19+
transactionEvent?.transaction === 'http.server GET' &&
20+
transactionEvent?.spans?.some(span => span.description === 'test-span')
2521
);
2622
});
2723

2824
await fetch(`${baseURL}/test-transaction`);
2925

3026
const transactionEvent = await transactionEventPromise;
3127

32-
expect(transactionEvent.contexts?.trace?.op).toBe('http.server');
33-
expect(transactionEvent.transaction).toContain('/test-transaction');
28+
expect(transactionEvent.transaction).toBe('http.server GET');
3429

3530
const spans = transactionEvent.spans || [];
3631
expect(spans).toContainEqual(
@@ -43,24 +38,22 @@ test('Sends transaction with manual Effect span', async ({ baseURL }) => {
4338
test('Sends Effect spans with correct parent-child structure', async ({ baseURL }) => {
4439
const transactionEventPromise = waitForTransaction('effect-node', transactionEvent => {
4540
return (
46-
transactionEvent?.contexts?.trace?.op === 'http.server' &&
47-
transactionEvent?.transaction?.includes('/test-effect-span')
41+
transactionEvent?.transaction === 'http.server GET' &&
42+
transactionEvent?.spans?.some(span => span.description === 'custom-effect-span')
4843
);
4944
});
5045

5146
await fetch(`${baseURL}/test-effect-span`);
5247

5348
const transactionEvent = await transactionEventPromise;
5449

55-
expect(transactionEvent.contexts?.trace?.op).toBe('http.server');
56-
expect(transactionEvent.transaction).toContain('/test-effect-span');
50+
expect(transactionEvent.transaction).toBe('http.server GET');
5751

5852
const spans = transactionEvent.spans || [];
5953

6054
expect(spans).toContainEqual(
6155
expect.objectContaining({
6256
description: 'custom-effect-span',
63-
op: 'internal',
6457
}),
6558
);
6659

@@ -77,15 +70,12 @@ test('Sends Effect spans with correct parent-child structure', async ({ baseURL
7770

7871
test('Sends transaction for error route', async ({ baseURL }) => {
7972
const transactionEventPromise = waitForTransaction('effect-node', transactionEvent => {
80-
return (
81-
transactionEvent?.contexts?.trace?.op === 'http.server' && transactionEvent?.transaction?.includes('/test-error')
82-
);
73+
return transactionEvent?.transaction === 'http.server GET';
8374
});
8475

8576
await fetch(`${baseURL}/test-error`);
8677

8778
const transactionEvent = await transactionEventPromise;
8879

89-
expect(transactionEvent.contexts?.trace?.op).toBe('http.server');
90-
expect(transactionEvent.transaction).toContain('/test-error');
80+
expect(transactionEvent.transaction).toBe('http.server GET');
9181
});

packages/effect/README.md

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,28 @@ This SDK does not have docs yet. Stay tuned.
1515
```typescript
1616
import * as Sentry from '@sentry/effect/server';
1717
import { NodeRuntime } from '@effect/platform-node';
18-
import { Layer } from 'effect';
18+
import { Layer, Logger } from 'effect';
1919
import { HttpLive } from './Http.js';
2020

21-
const MainLive = HttpLive.pipe(
22-
Layer.provide(
23-
Sentry.effectLayer({
24-
dsn: '__DSN__',
25-
tracesSampleRate: 1.0,
26-
enableLogs: true,
27-
enableEffectLogs: true,
28-
enableEffectMetrics: true,
29-
}),
30-
),
21+
const SentryLive = Layer.mergeAll(
22+
Sentry.effectLayer({
23+
dsn: '__DSN__',
24+
tracesSampleRate: 1.0,
25+
enableLogs: true,
26+
}),
27+
Layer.setTracer(Sentry.SentryEffectTracer),
28+
Logger.replace(Logger.defaultLogger, Sentry.SentryEffectLogger),
3129
);
3230

31+
const MainLive = HttpLive.pipe(Layer.provide(SentryLive));
3332
MainLive.pipe(Layer.launch, NodeRuntime.runMain);
3433
```
3534

36-
The `effectLayer` function initializes Sentry and returns an Effect Layer that provides:
35+
The `effectLayer` function initializes Sentry. To enable Effect instrumentation, compose with:
3736

38-
- Distributed tracing with automatic HTTP header extraction/injection
39-
- Effect spans traced as Sentry spans
40-
- Effect logs forwarded to Sentry (when `enableEffectLogs` is set)
41-
- Effect metrics sent to Sentry (when `enableEffectMetrics` is set)
37+
- `Layer.setTracer(Sentry.SentryEffectTracer)` - Effect spans traced as Sentry spans
38+
- `Logger.replace(Logger.defaultLogger, Sentry.SentryEffectLogger)` - Effect logs forwarded to Sentry
39+
- `Sentry.SentryEffectMetricsLayer` - Effect metrics sent to Sentry
4240

4341
## Links
4442

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,43 @@
11
import type { BrowserOptions } from '@sentry/browser';
22
import type * as EffectLayer from 'effect/Layer';
3-
import { suspend as suspendLayer } from 'effect/Layer';
4-
import type { EffectLayerBaseOptions } from '../utils/buildEffectLayer';
5-
import { buildEffectLayer } from '../utils/buildEffectLayer';
3+
import { empty as emptyLayer, suspend as suspendLayer } from 'effect/Layer';
64
import { init } from './sdk';
75

86
export { init } from './sdk';
97

108
/**
119
* Options for the Sentry Effect client layer.
1210
*/
13-
export type EffectClientLayerOptions = BrowserOptions & EffectLayerBaseOptions;
11+
export type EffectClientLayerOptions = BrowserOptions;
1412

1513
/**
1614
* Creates an Effect Layer that initializes Sentry for browser clients.
1715
*
18-
* This layer provides Effect applications with full Sentry instrumentation including:
19-
* - Effect spans traced as Sentry spans
20-
* - Effect logs forwarded to Sentry (when `enableEffectLogs` is set)
21-
* - Effect metrics sent to Sentry (when `enableEffectMetrics` is set)
16+
* To enable Effect tracing, logs, or metrics, compose with the respective layers:
17+
* - `Layer.setTracer(Sentry.SentryEffectTracer)` for tracing
18+
* - `Logger.replace(Logger.defaultLogger, Sentry.SentryEffectLogger)` for logs
19+
* - `Sentry.SentryEffectMetricsLayer` for metrics
2220
*
2321
* @example
2422
* ```typescript
2523
* import * as Sentry from '@sentry/effect/client';
26-
* import { Layer, Effect } from 'effect';
24+
* import { Layer, Logger, LogLevel } from 'effect';
2725
*
28-
* const ApiClientWithSentry = ApiClientLive.pipe(
29-
* Layer.provide(Sentry.effectLayer({
26+
* const SentryLive = Layer.mergeAll(
27+
* Sentry.effectLayer({
3028
* dsn: '__DSN__',
3129
* integrations: [Sentry.browserTracingIntegration()],
3230
* tracesSampleRate: 1.0,
33-
* })),
31+
* }),
32+
* Layer.setTracer(Sentry.SentryEffectTracer),
33+
* Logger.replace(Logger.defaultLogger, Sentry.SentryEffectLogger),
3434
* );
35-
*
36-
* Effect.runPromise(Effect.provide(myEffect, ApiClientWithSentry));
3735
* ```
3836
*/
3937
export function effectLayer(options: EffectClientLayerOptions): EffectLayer.Layer<never, never, never> {
40-
return suspendLayer(() => buildEffectLayer(options, init(options)));
38+
return suspendLayer(() => {
39+
init(options);
40+
41+
return emptyLayer;
42+
});
4143
}
Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,43 @@
11
import type { NodeOptions } from '@sentry/node-core/light';
22
import type * as EffectLayer from 'effect/Layer';
3-
import type { EffectLayerBaseOptions } from '../utils/buildEffectLayer';
4-
import { buildEffectLayer } from '../utils/buildEffectLayer';
3+
import { empty as emptyLayer, suspend as suspendLayer } from 'effect/Layer';
54
import { init } from './sdk';
65

76
export { init } from './sdk';
87

98
/**
109
* Options for the Sentry Effect server layer.
1110
*/
12-
export type EffectServerLayerOptions = NodeOptions & EffectLayerBaseOptions;
11+
export type EffectServerLayerOptions = NodeOptions;
1312

1413
/**
1514
* Creates an Effect Layer that initializes Sentry for Node.js servers.
1615
*
17-
* This layer provides Effect applications with full Sentry instrumentation including:
18-
* - Effect spans traced as Sentry spans
19-
* - Effect logs forwarded to Sentry (when `enableEffectLogs` is set)
20-
* - Effect metrics sent to Sentry (when `enableEffectMetrics` is set)
16+
* To enable Effect tracing, logs, or metrics, compose with the respective layers:
17+
* - `Layer.setTracer(Sentry.SentryEffectTracer)` for tracing
18+
* - `Logger.replace(Logger.defaultLogger, Sentry.SentryEffectLogger)` for logs
19+
* - `Sentry.SentryEffectMetricsLayer` for metrics
2120
*
2221
* @example
2322
* ```typescript
2423
* import * as Sentry from '@sentry/effect/server';
2524
* import { NodeRuntime } from '@effect/platform-node';
26-
* import { Layer } from 'effect';
25+
* import { Layer, Logger } from 'effect';
2726
* import { HttpLive } from './Http.js';
2827
*
29-
* const MainLive = HttpLive.pipe(
30-
* Layer.provide(Sentry.effectLayer({
31-
* dsn: '__DSN__',
32-
* enableEffectLogs: true,
33-
* enableEffectMetrics: true,
34-
* })),
28+
* const SentryLive = Layer.mergeAll(
29+
* Sentry.effectLayer({ dsn: '__DSN__' }),
30+
* Layer.setTracer(Sentry.SentryEffectTracer),
31+
* Logger.replace(Logger.defaultLogger, Sentry.SentryEffectLogger),
3532
* );
3633
*
34+
* const MainLive = HttpLive.pipe(Layer.provide(SentryLive));
3735
* MainLive.pipe(Layer.launch, NodeRuntime.runMain);
3836
* ```
3937
*/
4038
export function effectLayer(options: EffectServerLayerOptions): EffectLayer.Layer<never, never, never> {
41-
return buildEffectLayer(options, init(options));
39+
return suspendLayer(() => {
40+
init(options);
41+
return emptyLayer;
42+
});
4243
}

packages/effect/src/tracer.ts

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,10 @@
11
import type { Span } from '@sentry/core';
2-
import {
3-
getActiveSpan,
4-
getIsolationScope,
5-
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
6-
startInactiveSpan,
7-
withActiveSpan,
8-
} from '@sentry/core';
2+
import { getActiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan, withActiveSpan } from '@sentry/core';
93
import type * as Context from 'effect/Context';
104
import * as Exit from 'effect/Exit';
11-
import type * as Layer from 'effect/Layer';
12-
import { setTracer } from 'effect/Layer';
135
import * as Option from 'effect/Option';
146
import * as EffectTracer from 'effect/Tracer';
157

16-
const KIND_MAP: Record<EffectTracer.SpanKind, 'internal' | 'server' | 'client' | 'producer' | 'consumer'> = {
17-
internal: 'internal',
18-
client: 'client',
19-
server: 'server',
20-
producer: 'producer',
21-
consumer: 'consumer',
22-
};
23-
24-
function deriveOp(name: string, kind: EffectTracer.SpanKind): string {
25-
if (name.startsWith('http.server')) {
26-
return 'http.server';
27-
}
28-
29-
if (name.startsWith('http.client')) {
30-
return 'http.client';
31-
}
32-
33-
return KIND_MAP[kind];
34-
}
35-
368
function deriveOrigin(name: string): string {
379
if (name.startsWith('http.server') || name.startsWith('http.client')) {
3810
return 'auto.http.effect';
@@ -41,17 +13,6 @@ function deriveOrigin(name: string): string {
4113
return 'auto.function.effect';
4214
}
4315

44-
function deriveSpanName(name: string, kind: EffectTracer.SpanKind): string {
45-
if (name.startsWith('http.server') && kind === 'server') {
46-
const isolationScope = getIsolationScope();
47-
const transactionName = isolationScope.getScopeData().transactionName;
48-
if (transactionName) {
49-
return transactionName;
50-
}
51-
}
52-
return name;
53-
}
54-
5516
type HrTime = [number, number];
5617

5718
const SENTRY_SPAN_SYMBOL = Symbol.for('@sentry/effect.SentrySpan');
@@ -166,11 +127,8 @@ function createSentrySpan(
166127
const parentSentrySpan =
167128
Option.isSome(parent) && isSentrySpan(parent.value) ? parent.value.sentrySpan : (getActiveSpan() ?? null);
168129

169-
const spanName = deriveSpanName(name, kind);
170-
171130
const newSpan = startInactiveSpan({
172-
name: spanName,
173-
op: deriveOp(name, kind),
131+
name,
174132
startTime: nanosToHrTime(startTime),
175133
attributes: {
176134
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: deriveOrigin(name),

0 commit comments

Comments
 (0)