Skip to content

feat(node): Export tracing from @sentry/node #7503

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/node-integration-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
"scripts": {
"clean": "rimraf -g **/node_modules",
"prisma:init": "(cd suites/tracing/prisma-orm && ts-node ./setup.ts)",
"prisma:init:new": "(cd suites/tracing-new/prisma-orm && ts-node ./setup.ts)",
"lint": "run-s lint:prettier lint:eslint",
"lint:eslint": "eslint . --format stylish",
"lint:prettier": "prettier --check \"{suites,utils}/**/*.ts\"",
"fix": "run-s fix:eslint fix:prettier",
"fix:eslint": "eslint . --format stylish --fix",
"fix:prettier": "prettier --write \"{suites,utils}/**/*.ts\"",
"type-check": "tsc",
"pretest": "run-s --silent prisma:init",
"pretest": "run-s --silent prisma:init prisma:init:new",
"test": "ts-node ./utils/run-tests.ts",
"test:watch": "yarn test --watch"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as Sentry from '@sentry/node';
import * as Tracing from '@sentry/tracing';
import cors from 'cors';
import express from 'express';

Expand All @@ -8,7 +7,7 @@ const app = express();
Sentry.init({
dsn: 'https://[email protected]/1337',
release: '1.0',
integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })],
integrations: [new Sentry.Integrations.Http({ tracing: true }), new Sentry.Integrations.Express({ app })],
tracesSampleRate: 1.0,
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as Sentry from '@sentry/node';
import { ApolloServer, gql } from 'apollo-server';

Sentry.init({
dsn: 'https://[email protected]/1337',
release: '1.0',
tracesSampleRate: 1.0,
integrations: [new Sentry.Integrations.GraphQL(), new Sentry.Integrations.Apollo()],
});

const typeDefs = gql`
type Query {
hello: String
}
`;

const resolvers = {
Query: {
hello: () => {
return 'Hello world!';
},
},
};

const server = new ApolloServer({
typeDefs,
resolvers,
});

const transaction = Sentry.startTransaction({ name: 'test_transaction', op: 'transaction' });

Sentry.configureScope(scope => {
scope.setSpan(transaction);
});

void (async () => {
// Ref: https://www.apollographql.com/docs/apollo-server/testing/testing/#testing-using-executeoperation
await server.executeOperation({
query: '{hello}',
});

transaction.finish();
})();
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { assertSentryTransaction, conditionalTest, TestEnv } from '../../../utils';

// Node 10 is not supported by `graphql-js`
// Ref: https://github.com/graphql/graphql-js/blob/main/package.json
conditionalTest({ min: 12 })('GraphQL/Apollo Tests', () => {
test('should instrument GraphQL and Apollo Server.', async () => {
const env = await TestEnv.init(__dirname);
const envelope = await env.getEnvelopeRequest({ envelopeType: 'transaction' });

expect(envelope).toHaveLength(3);

const transaction = envelope[2];
const parentSpanId = (transaction as any)?.contexts?.trace?.span_id;
const graphqlSpanId = (transaction as any)?.spans?.[0].span_id;

expect(parentSpanId).toBeDefined();
expect(graphqlSpanId).toBeDefined();

assertSentryTransaction(transaction, {
transaction: 'test_transaction',
spans: [
{
description: 'execute',
op: 'graphql.execute',
parent_span_id: parentSpanId,
},
{
description: 'Query.hello',
op: 'graphql.resolve',
parent_span_id: graphqlSpanId,
},
],
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as Sentry from '@sentry/node';
import { MongoClient } from 'mongodb';

// suppress logging of the mongo download
global.console.log = () => null;

Sentry.init({
dsn: 'https://[email protected]/1337',
release: '1.0',
tracesSampleRate: 1.0,
integrations: [...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations()],
});

const client = new MongoClient(process.env.MONGO_URL || '', {
useUnifiedTopology: true,
});

async function run(): Promise<void> {
const transaction = Sentry.startTransaction({
name: 'Test Transaction',
op: 'transaction',
});

Sentry.configureScope(scope => {
scope.setSpan(transaction);
});

try {
await client.connect();

const database = client.db('admin');
const collection = database.collection('movies');

await collection.insertOne({ title: 'Rick and Morty' });
await collection.findOne({ title: 'Back to the Future' });
await collection.updateOne({ title: 'Back to the Future' }, { $set: { title: 'South Park' } });
await collection.findOne({ title: 'South Park' });

await collection.find({ title: 'South Park' }).toArray();
} finally {
if (transaction) transaction.finish();
await client.close();
}
}

void run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { MongoMemoryServer } from 'mongodb-memory-server-global';

import { assertSentryTransaction, conditionalTest, TestEnv } from '../../../../utils';

// This test can take longer.
jest.setTimeout(15000);

conditionalTest({ min: 12 })('MongoDB Test', () => {
let mongoServer: MongoMemoryServer;

beforeAll(async () => {
mongoServer = await MongoMemoryServer.create();
process.env.MONGO_URL = mongoServer.getUri();
}, 10000);

afterAll(async () => {
if (mongoServer) {
await mongoServer.stop();
}
});

test('should auto-instrument `mongodb` package.', async () => {
const env = await TestEnv.init(__dirname);
const envelope = await env.getEnvelopeRequest({ envelopeType: 'transaction' });

expect(envelope).toHaveLength(3);

assertSentryTransaction(envelope[2], {
transaction: 'Test Transaction',
spans: [
{
data: {
collectionName: 'movies',
dbName: 'admin',
namespace: 'admin.movies',
doc: '{"title":"Rick and Morty"}',
},
description: 'insertOne',
op: 'db',
},
{
data: {
collectionName: 'movies',
dbName: 'admin',
namespace: 'admin.movies',
query: '{"title":"Back to the Future"}',
},
description: 'findOne',
op: 'db',
},
{
data: {
collectionName: 'movies',
dbName: 'admin',
namespace: 'admin.movies',
filter: '{"title":"Back to the Future"}',
update: '{"$set":{"title":"South Park"}}',
},
description: 'updateOne',
op: 'db',
},
{
data: {
collectionName: 'movies',
dbName: 'admin',
namespace: 'admin.movies',
query: '{"title":"South Park"}',
},
description: 'findOne',
op: 'db',
},
{
data: {
collectionName: 'movies',
dbName: 'admin',
namespace: 'admin.movies',
query: '{"title":"South Park"}',
},
description: 'find',
op: 'db',
},
],
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as Sentry from '@sentry/node';
import mysql from 'mysql';

Sentry.init({
dsn: 'https://[email protected]/1337',
release: '1.0',
tracesSampleRate: 1.0,
integrations: [...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations()],
});

const connection = mysql.createConnection({
user: 'root',

Check failure

Code scanning / CodeQL

Hard-coded credentials

The hard-coded value "root" is used as [user name](1).
password: 'docker',

Check failure

Code scanning / CodeQL

Hard-coded credentials

The hard-coded value "docker" is used as [password](1).
});

connection.connect(function (err: unknown) {
if (err) {
return;
}
});

const transaction = Sentry.startTransaction({
op: 'transaction',
name: 'Test Transaction',
});

Sentry.configureScope(scope => {
scope.setSpan(transaction);
});

connection.query('SELECT 1 + 1 AS solution', function () {
connection.query('SELECT NOW()', ['1', '2'], () => {
if (transaction) transaction.finish();
connection.end();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { assertSentryTransaction, TestEnv } from '../../../../utils';

test('should auto-instrument `mysql` package.', async () => {
const env = await TestEnv.init(__dirname);
const envelope = await env.getEnvelopeRequest({ envelopeType: 'transaction' });

expect(envelope).toHaveLength(3);

assertSentryTransaction(envelope[2], {
transaction: 'Test Transaction',
spans: [
{
description: 'SELECT 1 + 1 AS solution',
op: 'db',
},

{
description: 'SELECT NOW()',
op: 'db',
},
],
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as Sentry from '@sentry/node';
import pg from 'pg';

Sentry.init({
dsn: 'https://[email protected]/1337',
release: '1.0',
tracesSampleRate: 1.0,
integrations: [...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations()],
});

const transaction = Sentry.startTransaction({
op: 'transaction',
name: 'Test Transaction',
});

Sentry.configureScope(scope => {
scope.setSpan(transaction);
});

const client = new pg.Client();
client.query('SELECT * FROM foo where bar ilike "baz%"', ['a', 'b'], () =>
client.query('SELECT * FROM bazz', () => {
client.query('SELECT NOW()', () => transaction.finish());
}),
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { assertSentryTransaction, TestEnv } from '../../../../utils';

class PgClient {
// https://node-postgres.com/api/client#clientquery
public query(_text: unknown, values: unknown, callback?: () => void) {
if (typeof callback === 'function') {
callback();
return;
}

if (typeof values === 'function') {
values();
return;
}

return Promise.resolve();
}
}

beforeAll(() => {
jest.mock('pg', () => {
return {
Client: PgClient,
native: {
Client: PgClient,
},
};
});
});

test('should auto-instrument `pg` package.', async () => {
const env = await TestEnv.init(__dirname);
const envelope = await env.getEnvelopeRequest({ envelopeType: 'transaction' });

expect(envelope).toHaveLength(3);

assertSentryTransaction(envelope[2], {
transaction: 'Test Transaction',
spans: [
{
description: 'SELECT * FROM foo where bar ilike "baz%"',
op: 'db',
},
{
description: 'SELECT * FROM bazz',
op: 'db',
},
{
description: 'SELECT NOW()',
op: 'db',
},
],
});
});
Loading