Skip to content

Commit 778e047

Browse files
authored
Merge branch 'develop' into sig/truncation-redis
2 parents 29bdf86 + 10211f4 commit 778e047

File tree

23 files changed

+350
-217
lines changed

23 files changed

+350
-217
lines changed

.size-limit.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ module.exports = [
190190
path: createCDNPath('bundle.tracing.min.js'),
191191
gzip: false,
192192
brotli: false,
193-
limit: '124.1 KB',
193+
limit: '125 KB',
194194
},
195195
{
196196
name: 'CDN Bundle (incl. Tracing, Replay) - uncompressed',

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ convenient interface and improved consistency between various JavaScript environ
2828
- [![Forum](https://img.shields.io/badge/forum-sentry-green.svg)](https://forum.sentry.io/c/sdks)
2929
- [![Discord](https://img.shields.io/discord/621778831602221064)](https://discord.gg/Ww9hbqr)
3030
- [![Stack Overflow](https://img.shields.io/badge/stack%20overflow-sentry-green.svg)](http://stackoverflow.com/questions/tagged/sentry)
31-
- [![Twitter Follow](https://img.shields.io/twitter/follow/getsentry?label=getsentry&style=social)](https://twitter.com/intent/follow?screen_name=getsentry)
31+
- [![X Follow](https://img.shields.io/twitter/follow/sentry?label=sentry&style=social)](https://x.com/intent/follow?screen_name=sentry)
3232

3333
## Contents
3434

dev-packages/browser-integration-tests/suites/public-api/metrics/init.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ window.Sentry = Sentry;
44

55
Sentry.init({
66
dsn: 'https://public@dsn.ingest.sentry.io/1337',
7-
_experiments: {
8-
enableMetrics: true,
9-
},
107
release: '1.0.0',
118
environment: 'test',
129
integrations: integrations => {

dev-packages/e2e-tests/test-applications/nextjs-16/tests/middleware.test.ts

Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,15 @@ test('Faulty middlewares', async ({ request }) => {
6262
test('Should trace outgoing fetch requests inside middleware and create breadcrumbs for it', async ({ request }) => {
6363
test.skip(isDevMode, 'The fetch requests ends up in a separate tx in dev atm');
6464
const middlewareTransactionPromise = waitForTransaction('nextjs-16', async transactionEvent => {
65+
return transactionEvent?.transaction === 'middleware GET';
66+
});
67+
68+
// In some builds (especially webpack), fetch spans may end up in a separate transaction instead of as child spans
69+
// This test validates that the fetch is traced either way
70+
const fetchTransactionPromise = waitForTransaction('nextjs-16', async transactionEvent => {
6571
return (
66-
transactionEvent?.transaction === 'middleware GET' &&
67-
!!transactionEvent.spans?.find(span => span.op === 'http.client')
72+
transactionEvent?.transaction === 'GET http://localhost:3030/' ||
73+
transactionEvent?.contexts?.trace?.description === 'GET http://localhost:3030/'
6874
);
6975
});
7076

@@ -74,40 +80,7 @@ test('Should trace outgoing fetch requests inside middleware and create breadcru
7480

7581
const middlewareTransaction = await middlewareTransactionPromise;
7682

77-
expect(middlewareTransaction.spans).toEqual(
78-
expect.arrayContaining([
79-
{
80-
data: {
81-
'http.request.method': 'GET',
82-
'http.request.method_original': 'GET',
83-
'http.response.status_code': 200,
84-
'network.peer.address': '::1',
85-
'network.peer.port': 3030,
86-
'otel.kind': 'CLIENT',
87-
'sentry.op': 'http.client',
88-
'sentry.origin': 'auto.http.otel.node_fetch',
89-
'server.address': 'localhost',
90-
'server.port': 3030,
91-
url: 'http://localhost:3030/',
92-
'url.full': 'http://localhost:3030/',
93-
'url.path': '/',
94-
'url.query': '',
95-
'url.scheme': 'http',
96-
'user_agent.original': 'node',
97-
},
98-
description: 'GET http://localhost:3030/',
99-
op: 'http.client',
100-
origin: 'auto.http.otel.node_fetch',
101-
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
102-
span_id: expect.stringMatching(/[a-f0-9]{16}/),
103-
start_timestamp: expect.any(Number),
104-
status: 'ok',
105-
timestamp: expect.any(Number),
106-
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
107-
},
108-
]),
109-
);
110-
83+
// Breadcrumbs should always be created for the fetch request
11184
expect(middlewareTransaction.breadcrumbs).toEqual(
11285
expect.arrayContaining([
11386
{
@@ -118,4 +91,52 @@ test('Should trace outgoing fetch requests inside middleware and create breadcru
11891
},
11992
]),
12093
);
94+
95+
// Check if http.client span exists as a child of the middleware transaction
96+
const hasHttpClientSpan = !!middlewareTransaction.spans?.find(span => span.op === 'http.client');
97+
98+
if (hasHttpClientSpan) {
99+
// Check if fetch is traced as a child span of the middleware transaction
100+
expect(middlewareTransaction.spans).toEqual(
101+
expect.arrayContaining([
102+
{
103+
data: {
104+
'http.request.method': 'GET',
105+
'http.request.method_original': 'GET',
106+
'http.response.status_code': 200,
107+
'network.peer.address': '::1',
108+
'network.peer.port': 3030,
109+
'otel.kind': 'CLIENT',
110+
'sentry.op': 'http.client',
111+
'sentry.origin': 'auto.http.otel.node_fetch',
112+
'server.address': 'localhost',
113+
'server.port': 3030,
114+
url: 'http://localhost:3030/',
115+
'url.full': 'http://localhost:3030/',
116+
'url.path': '/',
117+
'url.query': '',
118+
'url.scheme': 'http',
119+
'user_agent.original': 'node',
120+
},
121+
description: 'GET http://localhost:3030/',
122+
op: 'http.client',
123+
origin: 'auto.http.otel.node_fetch',
124+
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
125+
span_id: expect.stringMatching(/[a-f0-9]{16}/),
126+
start_timestamp: expect.any(Number),
127+
status: 'ok',
128+
timestamp: expect.any(Number),
129+
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
130+
},
131+
]),
132+
);
133+
} else {
134+
// Alternatively, fetch is traced as a separate transaction, similar to Dev builds
135+
const fetchTransaction = await fetchTransactionPromise;
136+
137+
expect(fetchTransaction.contexts?.trace?.op).toBe('http.client');
138+
expect(fetchTransaction.contexts?.trace?.status).toBe('ok');
139+
expect(fetchTransaction.contexts?.trace?.data?.['http.request.method']).toBe('GET');
140+
expect(fetchTransaction.contexts?.trace?.data?.['url.full']).toBe('http://localhost:3030/');
141+
}
121142
});

dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/server-components.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ test('Sends a transaction for a request to app router', async ({ page }) => {
3939
headers: expect.objectContaining({
4040
'user-agent': expect.any(String),
4141
}),
42-
url: expect.stringContaining('/server-component/parameter/1337/42'),
4342
});
4443

4544
// The transaction should not contain any spans with the same name as the transaction

dev-packages/node-core-integration-tests/suites/public-api/metrics/scenario.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ const client = Sentry.init({
66
dsn: 'https://public@dsn.ingest.sentry.io/1337',
77
release: '1.0.0',
88
environment: 'test',
9-
_experiments: {
10-
enableMetrics: true,
11-
},
129
transport: loggingTransport,
1310
});
1411

dev-packages/node-integration-tests/suites/public-api/metrics/scenario.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@ Sentry.init({
55
dsn: 'https://public@dsn.ingest.sentry.io/1337',
66
release: '1.0.0',
77
environment: 'test',
8-
_experiments: {
9-
enableMetrics: true,
10-
},
118
transport: loggingTransport,
129
});
1310

packages/aws-serverless/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
"@opentelemetry/semantic-conventions": "^1.37.0",
7272
"@sentry/core": "10.23.0",
7373
"@sentry/node": "10.23.0",
74+
"@sentry/node-core": "10.23.0",
7475
"@types/aws-lambda": "^8.10.62"
7576
},
7677
"devDependencies": {

packages/aws-serverless/src/init.ts

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Integration, Options } from '@sentry/core';
22
import { applySdkMetadata, debug, getSDKSource } from '@sentry/core';
33
import type { NodeClient, NodeOptions } from '@sentry/node';
44
import { getDefaultIntegrationsWithoutPerformance, initWithoutDefaultIntegrations } from '@sentry/node';
5+
import { envToBool } from '@sentry/node-core';
56
import { DEBUG_BUILD } from './debug-build';
67
import { awsIntegration } from './integration/aws';
78
import { awsLambdaIntegration } from './integration/awslambda';
@@ -54,7 +55,10 @@ export function getDefaultIntegrations(_options: Options): Integration[] {
5455

5556
export interface AwsServerlessOptions extends NodeOptions {
5657
/**
57-
* If Sentry events should be proxied through the Lambda extension when using the Lambda layer. Defaults to `true` when using the Lambda layer.
58+
* If Sentry events should be proxied through the Lambda extension when using the Lambda layer.
59+
* Defaults to `true` when using the Lambda layer.
60+
*
61+
* Can also be configured via the `SENTRY_LAYER_EXTENSION` environment variable.
5862
*/
5963
useLayerExtension?: boolean;
6064
}
@@ -68,31 +72,41 @@ export function init(options: AwsServerlessOptions = {}): NodeClient | undefined
6872
const sdkSource = getSDKSource();
6973
const proxyWouldInterfere = shouldDisableLayerExtensionForProxy();
7074

75+
// Determine useLayerExtension value with the following priority:
76+
// 1. Explicit option value (if provided)
77+
// 2. Environment variable SENTRY_LAYER_EXTENSION (if set)
78+
// 3. Default logic based on sdkSource, tunnel, and proxy settings
79+
const useLayerExtensionFromEnv = envToBool(process.env.SENTRY_LAYER_EXTENSION, { strict: true });
80+
const defaultUseLayerExtension = sdkSource === 'aws-lambda-layer' && !options.tunnel && !proxyWouldInterfere;
81+
const useLayerExtension = options.useLayerExtension ?? useLayerExtensionFromEnv ?? defaultUseLayerExtension;
82+
7183
const opts = {
7284
defaultIntegrations: getDefaultIntegrations(options),
73-
useLayerExtension: sdkSource === 'aws-lambda-layer' && !options.tunnel && !proxyWouldInterfere,
85+
useLayerExtension,
7486
...options,
7587
};
7688

7789
if (opts.useLayerExtension) {
78-
if (sdkSource === 'aws-lambda-layer') {
79-
if (!opts.tunnel) {
80-
DEBUG_BUILD && debug.log('Proxying Sentry events through the Sentry Lambda extension');
81-
opts.tunnel = 'http://localhost:9000/envelope';
82-
} else {
90+
if (sdkSource !== 'aws-lambda-layer') {
91+
DEBUG_BUILD && debug.warn('The Sentry Lambda extension is only supported when using the AWS Lambda layer.');
92+
} else if (opts.tunnel || proxyWouldInterfere) {
93+
if (opts.tunnel) {
8394
DEBUG_BUILD &&
8495
debug.warn(
8596
`Using a custom tunnel with the Sentry Lambda extension is not supported. Events will be tunnelled to ${opts.tunnel} and not through the extension.`,
8697
);
8798
}
99+
100+
if (proxyWouldInterfere) {
101+
DEBUG_BUILD &&
102+
debug.warn(
103+
'Sentry Lambda extension is disabled due to proxy environment variables (http_proxy/https_proxy). Consider adding localhost to no_proxy to re-enable.',
104+
);
105+
}
88106
} else {
89-
DEBUG_BUILD && debug.warn('The Sentry Lambda extension is only supported when using the AWS Lambda layer.');
107+
DEBUG_BUILD && debug.log('Proxying Sentry events through the Sentry Lambda extension');
108+
opts.tunnel = 'http://localhost:9000/envelope';
90109
}
91-
} else if (sdkSource === 'aws-lambda-layer' && proxyWouldInterfere) {
92-
DEBUG_BUILD &&
93-
debug.warn(
94-
'Sentry Lambda extension disabled due to proxy environment variables (http_proxy/https_proxy). Consider adding localhost to no_proxy to re-enable.',
95-
);
96110
}
97111

98112
applySdkMetadata(opts, 'aws-serverless', ['aws-serverless'], sdkSource);

packages/aws-serverless/test/init.test.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,13 @@ const mockInitWithoutDefaultIntegrations = vi.mocked(initWithoutDefaultIntegrati
1919

2020
describe('init', () => {
2121
beforeEach(() => {
22+
// Clear all mocks between tests
23+
vi.clearAllMocks();
24+
2225
// Clean up environment variables between tests
2326
delete process.env.http_proxy;
2427
delete process.env.no_proxy;
28+
delete process.env.SENTRY_LAYER_EXTENSION;
2529
});
2630

2731
describe('Lambda extension setup', () => {
@@ -386,4 +390,96 @@ describe('init', () => {
386390
);
387391
});
388392
});
393+
394+
describe('SENTRY_LAYER_EXTENSION environment variable', () => {
395+
test('should enable useLayerExtension when SENTRY_LAYER_EXTENSION=true', () => {
396+
process.env.SENTRY_LAYER_EXTENSION = 'true';
397+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
398+
const options: AwsServerlessOptions = {};
399+
400+
init(options);
401+
402+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
403+
expect.objectContaining({
404+
useLayerExtension: true,
405+
tunnel: 'http://localhost:9000/envelope',
406+
}),
407+
);
408+
});
409+
410+
test('should disable useLayerExtension when SENTRY_LAYER_EXTENSION=false', () => {
411+
process.env.SENTRY_LAYER_EXTENSION = 'false';
412+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
413+
const options: AwsServerlessOptions = {};
414+
415+
init(options);
416+
417+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
418+
expect.objectContaining({
419+
useLayerExtension: false,
420+
}),
421+
);
422+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
423+
expect.not.objectContaining({
424+
tunnel: expect.any(String),
425+
}),
426+
);
427+
});
428+
429+
test('should fall back to default behavior when SENTRY_LAYER_EXTENSION is not set', () => {
430+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
431+
const options: AwsServerlessOptions = {};
432+
433+
init(options);
434+
435+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
436+
expect.objectContaining({
437+
useLayerExtension: true,
438+
tunnel: 'http://localhost:9000/envelope',
439+
}),
440+
);
441+
});
442+
443+
test('should prioritize explicit option over environment variable', () => {
444+
process.env.SENTRY_LAYER_EXTENSION = 'true';
445+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
446+
const options: AwsServerlessOptions = {
447+
useLayerExtension: false,
448+
};
449+
450+
init(options);
451+
452+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
453+
expect.objectContaining({
454+
useLayerExtension: false,
455+
}),
456+
);
457+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
458+
expect.not.objectContaining({
459+
tunnel: expect.any(String),
460+
}),
461+
);
462+
});
463+
464+
test('should not set tunnel even tho useLayerExtension is set via env var when proxy is explicitly set', () => {
465+
process.env.http_proxy = 'http://proxy.example.com:8080';
466+
process.env.SENTRY_LAYER_EXTENSION = 'true';
467+
mockGetSDKSource.mockReturnValue('aws-lambda-layer');
468+
const options: AwsServerlessOptions = {};
469+
470+
init(options);
471+
472+
// useLayerExtension is respected but tunnel is not set due to proxy interference
473+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
474+
expect.objectContaining({
475+
useLayerExtension: true,
476+
}),
477+
);
478+
expect(mockInitWithoutDefaultIntegrations).toHaveBeenCalledWith(
479+
expect.not.objectContaining({
480+
tunnel: expect.any(String),
481+
}),
482+
);
483+
});
484+
});
389485
});

0 commit comments

Comments
 (0)