Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 723a8d6

Browse files
committed
more
1 parent b5cc6cb commit 723a8d6

File tree

11 files changed

+191
-224
lines changed

11 files changed

+191
-224
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import * as Sentry from '@sentry/aws-serverless';
2+
3+
Sentry.init({
4+
dsn: process.env.SENTRY_DSN,
5+
tracesSampleRate: 1,
6+
debug: true,
7+
_experiments: {
8+
enableLambdaExtension: true,
9+
},
10+
});
11+
12+
export const handler = async (event, context) => {
13+
Sentry.startSpan({ name: 'manual-span', op: 'test' }, async () => {
14+
return 'Hello, world!';
15+
});
16+
};

‎dev-packages/e2e-tests/test-applications/aws-serverless/tests/layer.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,4 +242,52 @@ test.describe('Lambda layer', () => {
242242
}),
243243
);
244244
});
245+
246+
test('experimental extension works', async ({ lambdaClient }) => {
247+
const transactionEventPromise = waitForTransaction('aws-serverless-lambda-sam', transactionEvent => {
248+
return transactionEvent?.transaction === 'LayerExperimentalExtension';
249+
});
250+
251+
await lambdaClient.send(
252+
new InvokeCommand({
253+
FunctionName: 'LayerExperimentalExtension',
254+
Payload: JSON.stringify({}),
255+
}),
256+
);
257+
258+
const transactionEvent = await transactionEventPromise;
259+
260+
expect(transactionEvent.transaction).toEqual('LayerExperimentalExtension');
261+
expect(transactionEvent.contexts?.trace).toEqual({
262+
data: {
263+
'sentry.sample_rate': 1,
264+
'sentry.source': 'custom',
265+
'sentry.origin': 'auto.otel.aws-lambda',
266+
'sentry.op': 'function.aws.lambda',
267+
'cloud.account.id': '012345678912',
268+
'faas.execution': expect.any(String),
269+
'faas.id': 'arn:aws:lambda:us-east-1:012345678912:function:LayerExperimentalExtension',
270+
'faas.coldstart': true,
271+
'otel.kind': 'SERVER',
272+
},
273+
op: 'function.aws.lambda',
274+
origin: 'auto.otel.aws-lambda',
275+
span_id: expect.stringMatching(/[a-f0-9]{16}/),
276+
status: 'ok',
277+
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
278+
});
279+
280+
expect(transactionEvent.spans).toHaveLength(1);
281+
282+
expect(transactionEvent.spans).toContainEqual(
283+
expect.objectContaining({
284+
data: expect.objectContaining({
285+
'sentry.op': 'test',
286+
'sentry.origin': 'manual',
287+
}),
288+
description: 'manual-span',
289+
op: 'test',
290+
}),
291+
);
292+
});
245293
});

‎dev-packages/rollup-utils/bundleHelpers.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export function makeBaseBundleConfig(options) {
104104
output: {
105105
format: 'esm',
106106
},
107-
plugins: [commonJSPlugin,makePackageNodeEsm()],
107+
plugins: [commonJSPlugin],
108108
// Don't bundle any of Node's core modules
109109
external: builtinModules,
110110
};
Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
1-
import { makeBaseBundleConfig,makeBundleConfigVariants } from '@sentry-internal/rollup-utils';
1+
import { makeBaseBundleConfig } from '@sentry-internal/rollup-utils';
22

33
export default [
4-
...makeBundleConfigVariants(
5-
makeBaseBundleConfig({
6-
bundleType: 'lambda-extension',
7-
entrypoints: ['src/lambda-extension/index.ts'],
8-
outputFileBase: () => 'index',
9-
packageSpecificConfig: {
10-
output: {
11-
dir: 'build/aws/dist-serverless/sentry-extension',
12-
sourcemap: false,
13-
},
4+
makeBaseBundleConfig({
5+
bundleType: 'lambda-extension',
6+
entrypoints: ['src/lambda-extension/index.ts'],
7+
outputFileBase: 'index.mjs',
8+
packageSpecificConfig: {
9+
output: {
10+
dir: 'build/aws/dist-serverless/sentry-extension',
11+
sourcemap: false,
1412
},
15-
}),
16-
{ variants: ['.js'] },
17-
),
13+
},
14+
}),
1815
];

‎packages/aws-serverless/scripts/buildLambdaLayer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ async function buildLambdaLayer(): Promise<void> {
4848
fsForceMkdirSync('./build/aws/dist-serverless/extensions');
4949
fs.copyFileSync('./src/lambda-extension/sentry-extension', './build/aws/dist-serverless/extensions/sentry-extension');
5050
fs.chmodSync('./build/aws/dist-serverless/extensions/sentry-extension', 0o755);
51-
fs.chmodSync('./build/aws/dist-serverless/sentry-extension/index.js', 0o755);
51+
fs.chmodSync('./build/aws/dist-serverless/sentry-extension/index.mjs', 0o755);
5252

5353
const zipFilename = `sentry-node-serverless-${version}.zip`;
5454
console.log(`Creating final layer zip file ${zipFilename}.`);

‎packages/aws-serverless/src/init.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type { Integration, Options } from '@sentry/core';
2-
import { applySdkMetadata, getSDKSource } from '@sentry/core';
2+
import { applySdkMetadata, debug,getSDKSource } from '@sentry/core';
33
import type { NodeClient, NodeOptions } from '@sentry/node';
44
import { getDefaultIntegrationsWithoutPerformance, initWithoutDefaultIntegrations } from '@sentry/node';
5+
import { DEBUG_BUILD } from './debug-build';
56
import { awsIntegration } from './integration/aws';
67
import { awsLambdaIntegration } from './integration/awslambda';
7-
88
/**
99
* Get the default integrations for the AWSLambda SDK.
1010
*/
@@ -14,20 +14,30 @@ export function getDefaultIntegrations(_options: Options): Integration[] {
1414
return [...getDefaultIntegrationsWithoutPerformance(), awsIntegration(), awsLambdaIntegration()];
1515
}
1616

17+
export interface AwsServerlessOptions extends NodeOptions {
18+
_experiments?: NodeOptions['_experiments'] & {
19+
/**
20+
* If proxying Sentry events through the Sentry Lambda extension should be enabled.
21+
*/
22+
enableLambdaExtension?: boolean;
23+
};
24+
}
25+
1726
/**
1827
* Initializes the Sentry AWS Lambda SDK.
1928
*
2029
* @param options Configuration options for the SDK, @see {@link AWSLambdaOptions}.
2130
*/
22-
export function init(options: NodeOptions = {}): NodeClient | undefined {
31+
export function init(options: AwsServerlessOptions = {}): NodeClient | undefined {
2332
const opts = {
2433
defaultIntegrations: getDefaultIntegrations(options),
2534
...options,
2635
};
2736

2837
const sdkSource = getSDKSource();
2938

30-
if (sdkSource === 'aws-lambda-layer' && !opts.tunnel) {
39+
if (opts._experiments?.enableLambdaExtension && sdkSource === 'aws-lambda-layer' && !opts.tunnel) {
40+
DEBUG_BUILD && debug.log('Proxying Sentry events through the Sentry Lambda extension');
3141
opts.tunnel = 'http://localhost:9000/envelope';
3242
}
3343

‎packages/aws-serverless/src/lambda-extension/aws-lambda-extension.ts

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as http from 'node:http';
22
import { buffer } from 'node:stream/consumers';
3-
import { dsnStringToIngestEndpoint} from './utils';
3+
import { debug,dsnFromString,getEnvelopeEndpointWithUrlEncodedAuth} from '@sentry/core';
44

55
/**
66
* The Extension API Client.
@@ -16,9 +16,8 @@ export class AwsLambdaExtension {
1616

1717
/**
1818
* Register this extension as an external extension with AWS.
19-
* @returns The extension identifier, or null if the extension was not registered.
2019
*/
21-
public async register(): Promise<string|null> {
20+
public async register(): Promise<void> {
2221
const res = await fetch(`${this._baseUrl}/register`, {
2322
method: 'POST',
2423
body: JSON.stringify({
@@ -35,13 +34,10 @@ export class AwsLambdaExtension {
3534
}
3635

3736
this._extensionId = res.headers.get('lambda-extension-identifier');
38-
return res.headers.get('lambda-extension-identifier');
3937
}
4038

4139
/**
4240
* Advances the extension to the next event.
43-
* @param extensionId The extension identifier.
44-
* @returns The event data, or null if the extension is not ready to process events.
4541
*/
4642
public async next(): Promise<void> {
4743
if (!this._extensionId) {
@@ -59,14 +55,15 @@ export class AwsLambdaExtension {
5955
throw new Error(`Failed to advance to next event: ${await res.text()}`);
6056
}
6157

62-
const event = await res.json();
58+
const event = (await res.json())as{eventType: string};
6359

64-
console.log('EXTENSION EVENT', event);
60+
if (event.eventType === 'SHUTDOWN') {
61+
await new Promise(resolve => setTimeout(resolve, 1000));
62+
}
6563
}
6664

6765
/**
6866
* Reports an error to the extension API.
69-
* @param extensionId The extension identifier.
7067
* @param phase The phase of the extension.
7168
* @param err The error to report.
7269
*/
@@ -92,7 +89,7 @@ export class AwsLambdaExtension {
9289
});
9390

9491
if (!res.ok) {
95-
thrownewError(`Failed to report error: ${await res.text()}`);
92+
debug.error(`Failed to report error: ${await res.text()}`);
9693
}
9794

9895
throw err;
@@ -110,34 +107,32 @@ export class AwsLambdaExtension {
110107
const envelope = new TextDecoder().decode(envelopeBytes);
111108
const piece = envelope.split('\n')[0];
112109
const header = JSON.parse(piece ?? '{}') as { dsn?: string };
113-
const upstreamSentryUrl = dsnStringToIngestEndpoint(header.dsn || '');
114-
115-
console.log('tunneling to sentry', { upstreamSentryUrl });
116-
console.log('headers', req.headers);
117-
118-
fetch(upstreamSentryUrl, {
110+
if (!header.dsn) {
111+
throw new Error('DSN is not set');
112+
}
113+
const dsn = dsnFromString(header.dsn);
114+
if (!dsn) {
115+
throw new Error('Invalid DSN');
116+
}
117+
const upstreamSentryUrl = getEnvelopeEndpointWithUrlEncodedAuth(dsn);
118+
119+
await fetch(upstreamSentryUrl, {
119120
method: 'POST',
120121
body: envelopeBytes,
121-
})
122-
.then(() => {
123-
console.log('tunneled to sentry');
124-
})
125-
.catch(err => {
126-
console.error('error tunneling to sentry', err);
127-
});
122+
});
128123

129124
res.writeHead(200, { 'Content-Type': 'application/json' });
130125
res.end(JSON.stringify({}));
131126
} catch (e) {
132-
console.error('error tunneling to sentry', e);
127+
debug.error('Error tunneling to Sentry', e);
133128
res.writeHead(500, { 'Content-Type': 'application/json' });
134-
res.end(JSON.stringify({ error: 'error tunneling to sentry' }));
129+
res.end(JSON.stringify({ error: 'Error tunneling to Sentry' }));
135130
}
136131
}
137132
});
138133

139134
server.listen(9000, () => {
140-
console.log('server listening on port 9000');
135+
debug.log('Sentry proxy listening on port 9000');
141136
});
142137
}
143138
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/env node
2+
import { debug } from '@sentry/core';
23
import { AwsLambdaExtension } from './aws-lambda-extension';
34

45
async function main(): Promise<void> {
@@ -8,12 +9,12 @@ async function main(): Promise<void> {
89

910
extension.startSentryTunnel();
1011

12+
// eslint-disable-next-line no-constant-condition
1113
while (true) {
1214
await extension.next();
1315
}
1416
}
1517

1618
main().catch(err => {
17-
console.error('Fatal error:', err);
18-
// process.exit(1);
19+
debug.error('Error in Lambda Extension', err);
1920
});

‎packages/aws-serverless/src/lambda-extension/sentry-extension

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ OWN_FILENAME="$(basename 0ドル)"
55
LAMBDA_EXTENSION_NAME="$OWN_FILENAME" # (external) extension name has to match the filename
66

77
unset NODE_OPTIONS
8-
exec "/opt/${LAMBDA_EXTENSION_NAME}/index.js"
8+
exec "/opt/${LAMBDA_EXTENSION_NAME}/index.mjs"

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /