Skip to content

Commit a3294d1

Browse files
committed
feat(node): Implement Sentry-specific http instrumentation
1 parent 40ebfad commit a3294d1

File tree

13 files changed

+691
-425
lines changed

13 files changed

+691
-425
lines changed

dev-packages/e2e-tests/test-applications/node-otel-without-tracing/package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111
"test:assert": "pnpm test"
1212
},
1313
"dependencies": {
14-
"@opentelemetry/sdk-trace-node": "1.25.1",
15-
"@opentelemetry/exporter-trace-otlp-http": "0.52.1",
16-
"@opentelemetry/instrumentation-undici": "0.4.0",
17-
"@opentelemetry/instrumentation": "0.52.1",
14+
"@opentelemetry/sdk-trace-node": "1.26.0",
15+
"@opentelemetry/exporter-trace-otlp-http": "0.53.0",
16+
"@opentelemetry/instrumentation-undici": "0.6.0",
17+
"@opentelemetry/instrumentation-http": "0.53.0",
18+
"@opentelemetry/instrumentation": "0.53.0",
1819
"@sentry/core": "latest || *",
1920
"@sentry/node": "latest || *",
2021
"@sentry/opentelemetry": "latest || *",

dev-packages/e2e-tests/test-applications/node-otel-without-tracing/src/app.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import express from 'express';
77
const app = express();
88
const port = 3030;
99

10+
Sentry.setTag('root-level-tag', 'yes');
11+
1012
app.get('/test-success', function (req, res) {
1113
res.send({ version: 'v1' });
1214
});
@@ -23,8 +25,6 @@ app.get('/test-transaction', function (req, res) {
2325

2426
await fetch('http://localhost:3030/test-success');
2527

26-
await Sentry.flush();
27-
2828
res.send({});
2929
});
3030
});
@@ -38,7 +38,10 @@ app.get('/test-error', async function (req, res) {
3838
});
3939

4040
app.get('/test-exception/:id', function (req, _res) {
41-
throw new Error(`This is an exception with id ${req.params.id}`);
41+
const id = req.params.id;
42+
Sentry.setTag(`param-${id}`, id);
43+
44+
throw new Error(`This is an exception with id ${id}`);
4245
});
4346

4447
Sentry.setupExpressErrorHandler(app);

dev-packages/e2e-tests/test-applications/node-otel-without-tracing/src/instrument.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
12
const { NodeTracerProvider, BatchSpanProcessor } = require('@opentelemetry/sdk-trace-node');
23
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
34
const Sentry = require('@sentry/node');
4-
const { SentrySpanProcessor, SentryPropagator } = require('@sentry/opentelemetry');
5+
const { SentryPropagator } = require('@sentry/opentelemetry');
56
const { UndiciInstrumentation } = require('@opentelemetry/instrumentation-undici');
67
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
78

8-
const sentryClient = Sentry.init({
9+
Sentry.init({
910
environment: 'qa', // dynamic sampling bias to keep transactions
1011
dsn:
1112
process.env.E2E_TEST_DSN ||
@@ -15,6 +16,8 @@ const sentryClient = Sentry.init({
1516
tunnel: `http://localhost:3031/`, // proxy server
1617
// Tracing is completely disabled
1718

19+
integrations: [Sentry.httpIntegration({ spans: true })],
20+
1821
// Custom OTEL setup
1922
skipOpenTelemetrySetup: true,
2023
});
@@ -37,5 +40,5 @@ provider.register({
3740
});
3841

3942
registerInstrumentations({
40-
instrumentations: [new UndiciInstrumentation()],
43+
instrumentations: [new UndiciInstrumentation(), new HttpInstrumentation()],
4144
});

dev-packages/e2e-tests/test-applications/node-otel-without-tracing/tests/errors.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,24 @@ test('Sends correct error event', async ({ baseURL }) => {
2828
span_id: expect.any(String),
2929
});
3030
});
31+
32+
test('Isolates requests correctly', async ({ baseURL }) => {
33+
const errorEventPromise1 = waitForError('node-otel-without-tracing', event => {
34+
return !event.type && event.exception?.values?.[0]?.value === 'This is an exception with id 555-a';
35+
});
36+
const errorEventPromise2 = waitForError('node-otel-without-tracing', event => {
37+
return !event.type && event.exception?.values?.[0]?.value === 'This is an exception with id 555-b';
38+
});
39+
40+
fetch(`${baseURL}/test-exception/555-a`);
41+
fetch(`${baseURL}/test-exception/555-b`);
42+
43+
const errorEvent1 = await errorEventPromise1;
44+
const errorEvent2 = await errorEventPromise2;
45+
46+
expect(errorEvent1.transaction).toEqual('GET /test-exception/555-a');
47+
expect(errorEvent1.tags).toEqual({ 'root-level-tag': 'yes', 'param-555-a': '555-a' });
48+
49+
expect(errorEvent2.transaction).toEqual('GET /test-exception/555-b');
50+
expect(errorEvent2.tags).toEqual({ 'root-level-tag': 'yes', 'param-555-b': '555-b' });
51+
});

dev-packages/e2e-tests/test-applications/node-otel-without-tracing/tests/transactions.test.ts

Lines changed: 16 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ test('Sends an API route transaction to OTLP', async ({ baseURL }) => {
2222
);
2323
});
2424

25-
await fetch(`${baseURL}/test-transaction`);
25+
fetch(`${baseURL}/test-transaction`);
2626

2727
const otelData = await otelPromise;
2828

@@ -63,102 +63,21 @@ test('Sends an API route transaction to OTLP', async ({ baseURL }) => {
6363
startTimeUnixNano: expect.any(String),
6464
endTimeUnixNano: expect.any(String),
6565
attributes: [
66-
{
67-
key: 'http.url',
68-
value: {
69-
stringValue: 'http://localhost:3030/test-transaction',
70-
},
71-
},
72-
{
73-
key: 'http.host',
74-
value: {
75-
stringValue: 'localhost:3030',
76-
},
77-
},
78-
{
79-
key: 'net.host.name',
80-
value: {
81-
stringValue: 'localhost',
82-
},
83-
},
84-
{
85-
key: 'http.method',
86-
value: {
87-
stringValue: 'GET',
88-
},
89-
},
90-
{
91-
key: 'http.scheme',
92-
value: {
93-
stringValue: 'http',
94-
},
95-
},
96-
{
97-
key: 'http.target',
98-
value: {
99-
stringValue: '/test-transaction',
100-
},
101-
},
102-
{
103-
key: 'http.user_agent',
104-
value: {
105-
stringValue: 'node',
106-
},
107-
},
108-
{
109-
key: 'http.flavor',
110-
value: {
111-
stringValue: '1.1',
112-
},
113-
},
114-
{
115-
key: 'net.transport',
116-
value: {
117-
stringValue: 'ip_tcp',
118-
},
119-
},
120-
{
121-
key: 'sentry.origin',
122-
value: {
123-
stringValue: 'auto.http.otel.http',
124-
},
125-
},
126-
{
127-
key: 'net.host.ip',
128-
value: {
129-
stringValue: expect.any(String),
130-
},
131-
},
132-
{
133-
key: 'net.host.port',
134-
value: {
135-
intValue: 3030,
136-
},
137-
},
138-
{
139-
key: 'net.peer.ip',
140-
value: {
141-
stringValue: expect.any(String),
142-
},
143-
},
144-
{
145-
key: 'net.peer.port',
146-
value: {
147-
intValue: expect.any(Number),
148-
},
149-
},
150-
{
151-
key: 'http.status_code',
152-
value: {
153-
intValue: 200,
154-
},
155-
},
156-
{
157-
key: 'http.status_text',
158-
value: {
159-
stringValue: 'OK',
160-
},
161-
},
66+
{ key: 'http.url', value: { stringValue: 'http://localhost:3030/test-transaction' } },
67+
{ key: 'http.host', value: { stringValue: 'localhost:3030' } },
68+
{ key: 'net.host.name', value: { stringValue: 'localhost' } },
69+
{ key: 'http.method', value: { stringValue: 'GET' } },
70+
{ key: 'http.scheme', value: { stringValue: 'http' } },
71+
{ key: 'http.target', value: { stringValue: '/test-transaction' } },
72+
{ key: 'http.user_agent', value: { stringValue: 'node' } },
73+
{ key: 'http.flavor', value: { stringValue: '1.1' } },
74+
{ key: 'net.transport', value: { stringValue: 'ip_tcp' } },
75+
{ key: 'net.host.ip', value: { stringValue: expect.any(String) } },
76+
{ key: 'net.host.port', value: { intValue: 3030 } },
77+
{ key: 'net.peer.ip', value: { stringValue: expect.any(String) } },
78+
{ key: 'net.peer.port', value: { intValue: expect.any(Number) } },
79+
{ key: 'http.status_code', value: { intValue: 200 } },
80+
{ key: 'http.status_text', value: { stringValue: 'OK' } },
16281
],
16382
droppedAttributesCount: 0,
16483
events: [],

dev-packages/node-integration-tests/suites/tracing/requests/http-breadcrumbs/scenario.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ async function run(): Promise<void> {
2222
Sentry.addBreadcrumb({ message: 'manual breadcrumb' });
2323

2424
await makeHttpRequest(`${process.env.SERVER_URL}/api/v0`);
25-
await makeHttpRequest(`${process.env.SERVER_URL}/api/v1`);
25+
await makeHttpGet(`${process.env.SERVER_URL}/api/v1`);
2626
await makeHttpRequest(`${process.env.SERVER_URL}/api/v2`);
2727
await makeHttpRequest(`${process.env.SERVER_URL}/api/v3`);
2828

@@ -46,3 +46,16 @@ function makeHttpRequest(url: string): Promise<void> {
4646
.end();
4747
});
4848
}
49+
50+
function makeHttpGet(url: string): Promise<void> {
51+
return new Promise<void>(resolve => {
52+
http.get(url, httpRes => {
53+
httpRes.on('data', () => {
54+
// we don't care about data
55+
});
56+
httpRes.on('end', () => {
57+
resolve();
58+
});
59+
});
60+
});
61+
}

dev-packages/node-integration-tests/suites/tracing/requests/http-no-tracing/scenario.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import * as http from 'http';
1313

1414
async function run(): Promise<void> {
1515
await makeHttpRequest(`${process.env.SERVER_URL}/api/v0`);
16-
await makeHttpRequest(`${process.env.SERVER_URL}/api/v1`);
16+
await makeHttpGet(`${process.env.SERVER_URL}/api/v1`);
1717
await makeHttpRequest(`${process.env.SERVER_URL}/api/v2`);
1818
await makeHttpRequest(`${process.env.SERVER_URL}/api/v3`);
1919

@@ -37,3 +37,16 @@ function makeHttpRequest(url: string): Promise<void> {
3737
.end();
3838
});
3939
}
40+
41+
function makeHttpGet(url: string): Promise<void> {
42+
return new Promise<void>(resolve => {
43+
http.get(url, httpRes => {
44+
httpRes.on('data', () => {
45+
// we don't care about data
46+
});
47+
httpRes.on('end', () => {
48+
resolve();
49+
});
50+
});
51+
});
52+
}

dev-packages/node-integration-tests/suites/tracing/requests/http-no-tracing/test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,56 @@ test('outgoing http requests are correctly instrumented with tracing disabled',
3838
},
3939
],
4040
},
41+
breadcrumbs: [
42+
{
43+
message: 'manual breadcrumb',
44+
timestamp: expect.any(Number),
45+
},
46+
{
47+
category: 'http',
48+
data: {
49+
'http.method': 'GET',
50+
url: `${SERVER_URL}/api/v0`,
51+
status_code: 404,
52+
ADDED_PATH: '/api/v0',
53+
},
54+
timestamp: expect.any(Number),
55+
type: 'http',
56+
},
57+
{
58+
category: 'http',
59+
data: {
60+
'http.method': 'GET',
61+
url: `${SERVER_URL}/api/v1`,
62+
status_code: 404,
63+
ADDED_PATH: '/api/v1',
64+
},
65+
timestamp: expect.any(Number),
66+
type: 'http',
67+
},
68+
{
69+
category: 'http',
70+
data: {
71+
'http.method': 'GET',
72+
url: `${SERVER_URL}/api/v2`,
73+
status_code: 404,
74+
ADDED_PATH: '/api/v2',
75+
},
76+
timestamp: expect.any(Number),
77+
type: 'http',
78+
},
79+
{
80+
category: 'http',
81+
data: {
82+
'http.method': 'GET',
83+
url: `${SERVER_URL}/api/v3`,
84+
status_code: 404,
85+
ADDED_PATH: '/api/v3',
86+
},
87+
timestamp: expect.any(Number),
88+
type: 'http',
89+
},
90+
],
4191
},
4292
})
4393
.start(closeTestServer);

0 commit comments

Comments
 (0)