Skip to content

Commit 95b0a6c

Browse files
authored
feat(node): Export tracing from @sentry/node (#7503)
1 parent 2738b5f commit 95b0a6c

File tree

29 files changed

+681
-5
lines changed

29 files changed

+681
-5
lines changed

packages/node-integration-tests/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@
99
"scripts": {
1010
"clean": "rimraf -g **/node_modules",
1111
"prisma:init": "(cd suites/tracing/prisma-orm && ts-node ./setup.ts)",
12+
"prisma:init:new": "(cd suites/tracing-new/prisma-orm && ts-node ./setup.ts)",
1213
"lint": "run-s lint:prettier lint:eslint",
1314
"lint:eslint": "eslint . --format stylish",
1415
"lint:prettier": "prettier --check \"{suites,utils}/**/*.ts\"",
1516
"fix": "run-s fix:eslint fix:prettier",
1617
"fix:eslint": "eslint . --format stylish --fix",
1718
"fix:prettier": "prettier --write \"{suites,utils}/**/*.ts\"",
1819
"type-check": "tsc",
19-
"pretest": "run-s --silent prisma:init",
20+
"pretest": "run-s --silent prisma:init prisma:init:new",
2021
"test": "ts-node ./utils/run-tests.ts",
2122
"test:watch": "yarn test --watch"
2223
},

packages/node-integration-tests/suites/express/tracing/server.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as Sentry from '@sentry/node';
2-
import * as Tracing from '@sentry/tracing';
32
import cors from 'cors';
43
import express from 'express';
54

@@ -8,7 +7,7 @@ const app = express();
87
Sentry.init({
98
dsn: 'https://[email protected]/1337',
109
release: '1.0',
11-
integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })],
10+
integrations: [new Sentry.Integrations.Http({ tracing: true }), new Sentry.Integrations.Express({ app })],
1211
tracesSampleRate: 1.0,
1312
});
1413

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as Sentry from '@sentry/node';
2+
import { ApolloServer, gql } from 'apollo-server';
3+
4+
Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
integrations: [new Sentry.Integrations.GraphQL(), new Sentry.Integrations.Apollo()],
9+
});
10+
11+
const typeDefs = gql`
12+
type Query {
13+
hello: String
14+
}
15+
`;
16+
17+
const resolvers = {
18+
Query: {
19+
hello: () => {
20+
return 'Hello world!';
21+
},
22+
},
23+
};
24+
25+
const server = new ApolloServer({
26+
typeDefs,
27+
resolvers,
28+
});
29+
30+
const transaction = Sentry.startTransaction({ name: 'test_transaction', op: 'transaction' });
31+
32+
Sentry.configureScope(scope => {
33+
scope.setSpan(transaction);
34+
});
35+
36+
void (async () => {
37+
// Ref: https://www.apollographql.com/docs/apollo-server/testing/testing/#testing-using-executeoperation
38+
await server.executeOperation({
39+
query: '{hello}',
40+
});
41+
42+
transaction.finish();
43+
})();
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { assertSentryTransaction, conditionalTest, TestEnv } from '../../../utils';
2+
3+
// Node 10 is not supported by `graphql-js`
4+
// Ref: https://github.com/graphql/graphql-js/blob/main/package.json
5+
conditionalTest({ min: 12 })('GraphQL/Apollo Tests', () => {
6+
test('should instrument GraphQL and Apollo Server.', async () => {
7+
const env = await TestEnv.init(__dirname);
8+
const envelope = await env.getEnvelopeRequest({ envelopeType: 'transaction' });
9+
10+
expect(envelope).toHaveLength(3);
11+
12+
const transaction = envelope[2];
13+
const parentSpanId = (transaction as any)?.contexts?.trace?.span_id;
14+
const graphqlSpanId = (transaction as any)?.spans?.[0].span_id;
15+
16+
expect(parentSpanId).toBeDefined();
17+
expect(graphqlSpanId).toBeDefined();
18+
19+
assertSentryTransaction(transaction, {
20+
transaction: 'test_transaction',
21+
spans: [
22+
{
23+
description: 'execute',
24+
op: 'graphql.execute',
25+
parent_span_id: parentSpanId,
26+
},
27+
{
28+
description: 'Query.hello',
29+
op: 'graphql.resolve',
30+
parent_span_id: graphqlSpanId,
31+
},
32+
],
33+
});
34+
});
35+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import * as Sentry from '@sentry/node';
2+
import { MongoClient } from 'mongodb';
3+
4+
// suppress logging of the mongo download
5+
global.console.log = () => null;
6+
7+
Sentry.init({
8+
dsn: 'https://[email protected]/1337',
9+
release: '1.0',
10+
tracesSampleRate: 1.0,
11+
integrations: [...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations()],
12+
});
13+
14+
const client = new MongoClient(process.env.MONGO_URL || '', {
15+
useUnifiedTopology: true,
16+
});
17+
18+
async function run(): Promise<void> {
19+
const transaction = Sentry.startTransaction({
20+
name: 'Test Transaction',
21+
op: 'transaction',
22+
});
23+
24+
Sentry.configureScope(scope => {
25+
scope.setSpan(transaction);
26+
});
27+
28+
try {
29+
await client.connect();
30+
31+
const database = client.db('admin');
32+
const collection = database.collection('movies');
33+
34+
await collection.insertOne({ title: 'Rick and Morty' });
35+
await collection.findOne({ title: 'Back to the Future' });
36+
await collection.updateOne({ title: 'Back to the Future' }, { $set: { title: 'South Park' } });
37+
await collection.findOne({ title: 'South Park' });
38+
39+
await collection.find({ title: 'South Park' }).toArray();
40+
} finally {
41+
if (transaction) transaction.finish();
42+
await client.close();
43+
}
44+
}
45+
46+
void run();
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { MongoMemoryServer } from 'mongodb-memory-server-global';
2+
3+
import { assertSentryTransaction, conditionalTest, TestEnv } from '../../../../utils';
4+
5+
// This test can take longer.
6+
jest.setTimeout(15000);
7+
8+
conditionalTest({ min: 12 })('MongoDB Test', () => {
9+
let mongoServer: MongoMemoryServer;
10+
11+
beforeAll(async () => {
12+
mongoServer = await MongoMemoryServer.create();
13+
process.env.MONGO_URL = mongoServer.getUri();
14+
}, 10000);
15+
16+
afterAll(async () => {
17+
if (mongoServer) {
18+
await mongoServer.stop();
19+
}
20+
});
21+
22+
test('should auto-instrument `mongodb` package.', async () => {
23+
const env = await TestEnv.init(__dirname);
24+
const envelope = await env.getEnvelopeRequest({ envelopeType: 'transaction' });
25+
26+
expect(envelope).toHaveLength(3);
27+
28+
assertSentryTransaction(envelope[2], {
29+
transaction: 'Test Transaction',
30+
spans: [
31+
{
32+
data: {
33+
collectionName: 'movies',
34+
dbName: 'admin',
35+
namespace: 'admin.movies',
36+
doc: '{"title":"Rick and Morty"}',
37+
},
38+
description: 'insertOne',
39+
op: 'db',
40+
},
41+
{
42+
data: {
43+
collectionName: 'movies',
44+
dbName: 'admin',
45+
namespace: 'admin.movies',
46+
query: '{"title":"Back to the Future"}',
47+
},
48+
description: 'findOne',
49+
op: 'db',
50+
},
51+
{
52+
data: {
53+
collectionName: 'movies',
54+
dbName: 'admin',
55+
namespace: 'admin.movies',
56+
filter: '{"title":"Back to the Future"}',
57+
update: '{"$set":{"title":"South Park"}}',
58+
},
59+
description: 'updateOne',
60+
op: 'db',
61+
},
62+
{
63+
data: {
64+
collectionName: 'movies',
65+
dbName: 'admin',
66+
namespace: 'admin.movies',
67+
query: '{"title":"South Park"}',
68+
},
69+
description: 'findOne',
70+
op: 'db',
71+
},
72+
{
73+
data: {
74+
collectionName: 'movies',
75+
dbName: 'admin',
76+
namespace: 'admin.movies',
77+
query: '{"title":"South Park"}',
78+
},
79+
description: 'find',
80+
op: 'db',
81+
},
82+
],
83+
});
84+
});
85+
});
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import * as Sentry from '@sentry/node';
2+
import mysql from 'mysql';
3+
4+
Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
integrations: [...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations()],
9+
});
10+
11+
const connection = mysql.createConnection({
12+
user: 'root',
13+
password: 'docker',
14+
});
15+
16+
connection.connect(function (err: unknown) {
17+
if (err) {
18+
return;
19+
}
20+
});
21+
22+
const transaction = Sentry.startTransaction({
23+
op: 'transaction',
24+
name: 'Test Transaction',
25+
});
26+
27+
Sentry.configureScope(scope => {
28+
scope.setSpan(transaction);
29+
});
30+
31+
connection.query('SELECT 1 + 1 AS solution', function () {
32+
connection.query('SELECT NOW()', ['1', '2'], () => {
33+
if (transaction) transaction.finish();
34+
connection.end();
35+
});
36+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { assertSentryTransaction, TestEnv } from '../../../../utils';
2+
3+
test('should auto-instrument `mysql` package.', async () => {
4+
const env = await TestEnv.init(__dirname);
5+
const envelope = await env.getEnvelopeRequest({ envelopeType: 'transaction' });
6+
7+
expect(envelope).toHaveLength(3);
8+
9+
assertSentryTransaction(envelope[2], {
10+
transaction: 'Test Transaction',
11+
spans: [
12+
{
13+
description: 'SELECT 1 + 1 AS solution',
14+
op: 'db',
15+
},
16+
17+
{
18+
description: 'SELECT NOW()',
19+
op: 'db',
20+
},
21+
],
22+
});
23+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import * as Sentry from '@sentry/node';
2+
import pg from 'pg';
3+
4+
Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
integrations: [...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations()],
9+
});
10+
11+
const transaction = Sentry.startTransaction({
12+
op: 'transaction',
13+
name: 'Test Transaction',
14+
});
15+
16+
Sentry.configureScope(scope => {
17+
scope.setSpan(transaction);
18+
});
19+
20+
const client = new pg.Client();
21+
client.query('SELECT * FROM foo where bar ilike "baz%"', ['a', 'b'], () =>
22+
client.query('SELECT * FROM bazz', () => {
23+
client.query('SELECT NOW()', () => transaction.finish());
24+
}),
25+
);
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { assertSentryTransaction, TestEnv } from '../../../../utils';
2+
3+
class PgClient {
4+
// https://node-postgres.com/api/client#clientquery
5+
public query(_text: unknown, values: unknown, callback?: () => void) {
6+
if (typeof callback === 'function') {
7+
callback();
8+
return;
9+
}
10+
11+
if (typeof values === 'function') {
12+
values();
13+
return;
14+
}
15+
16+
return Promise.resolve();
17+
}
18+
}
19+
20+
beforeAll(() => {
21+
jest.mock('pg', () => {
22+
return {
23+
Client: PgClient,
24+
native: {
25+
Client: PgClient,
26+
},
27+
};
28+
});
29+
});
30+
31+
test('should auto-instrument `pg` package.', async () => {
32+
const env = await TestEnv.init(__dirname);
33+
const envelope = await env.getEnvelopeRequest({ envelopeType: 'transaction' });
34+
35+
expect(envelope).toHaveLength(3);
36+
37+
assertSentryTransaction(envelope[2], {
38+
transaction: 'Test Transaction',
39+
spans: [
40+
{
41+
description: 'SELECT * FROM foo where bar ilike "baz%"',
42+
op: 'db',
43+
},
44+
{
45+
description: 'SELECT * FROM bazz',
46+
op: 'db',
47+
},
48+
{
49+
description: 'SELECT NOW()',
50+
op: 'db',
51+
},
52+
],
53+
});
54+
});

0 commit comments

Comments
 (0)