Skip to content

Commit 0ca8821

Browse files
authored
feat(node): Add useOperationNameForRootSpan tographqlIntegration (#13248)
This introduces a new option for the `graphqlIntegration`, `useOperationNameForRootSpan`, which is by default `true` but can be disabled in integration settings like this: `Sentry.graphqlIntegration({ useOperationNameForRootSpan: true })`. With this setting enabled, the graphql instrumentation will update the `http.server` root span it is in (if there is one) will be appended to the span name. So instead of having all root spans be `POST /graphql`, the names will now be e.g. `POST /graphql (query MyQuery)`. If there are multiple operations in a single http request, they will be appended like `POST /graphql (query Query1, query Query2)`, up to a limit of 5, at which point they will be appended as `+2` or similar. Closes #13238
1 parent b17ac59 commit 0ca8821

File tree

17 files changed

+536
-51
lines changed

17 files changed

+536
-51
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const { ApolloServer, gql } = require('apollo-server');
2+
const Sentry = require('@sentry/node');
3+
4+
module.exports = () => {
5+
return Sentry.startSpan({ name: 'Test Server Start' }, () => {
6+
return new ApolloServer({
7+
typeDefs: gql`type Query {
8+
hello: String
9+
world: String
10+
}
11+
type Mutation {
12+
login(email: String): String
13+
}`,
14+
resolvers: {
15+
Query: {
16+
hello: () => {
17+
return 'Hello!';
18+
},
19+
world: () => {
20+
return 'World!';
21+
},
22+
},
23+
Mutation: {
24+
login: async (_, { email }) => {
25+
return `${email}--token`;
26+
},
27+
},
28+
},
29+
introspection: false,
30+
debug: false,
31+
});
32+
});
33+
};

dev-packages/node-integration-tests/suites/tracing/apollo-graphql/scenario-mutation.js

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,37 +12,15 @@ Sentry.init({
1212
setInterval(() => {}, 1000);
1313

1414
async function run() {
15-
const { ApolloServer, gql } = require('apollo-server');
15+
const { gql } = require('apollo-server');
16+
const server = require('./apollo-server')();
1617

1718
await Sentry.startSpan(
1819
{
1920
name: 'Test Transaction',
2021
op: 'transaction',
2122
},
2223
async span => {
23-
const server = new ApolloServer({
24-
typeDefs: gql`
25-
type Query {
26-
hello: String
27-
}
28-
type Mutation {
29-
login(email: String): String
30-
}
31-
`,
32-
resolvers: {
33-
Query: {
34-
hello: () => {
35-
return 'Hello world!';
36-
},
37-
},
38-
Mutation: {
39-
login: async (_, { email }) => {
40-
return `${email}--token`;
41-
},
42-
},
43-
},
44-
});
45-
4624
// Ref: https://www.apollographql.com/docs/apollo-server/testing/testing/#testing-using-executeoperation
4725
await server.executeOperation({
4826
query: gql`mutation Mutation($email: String){

dev-packages/node-integration-tests/suites/tracing/apollo-graphql/scenario-query.js

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,14 @@ Sentry.init({
1212
setInterval(() => {}, 1000);
1313

1414
async function run() {
15-
const { ApolloServer, gql } = require('apollo-server');
15+
const server = require('./apollo-server')();
1616

1717
await Sentry.startSpan(
1818
{
1919
name: 'Test Transaction',
2020
op: 'transaction',
2121
},
2222
async span => {
23-
const typeDefs = gql`type Query { hello: String }`;
24-
25-
const resolvers = {
26-
Query: {
27-
hello: () => {
28-
return 'Hello world!';
29-
},
30-
},
31-
};
32-
33-
const server = new ApolloServer({
34-
typeDefs,
35-
resolvers,
36-
});
37-
3823
// Ref: https://www.apollographql.com/docs/apollo-server/testing/testing/#testing-using-executeoperation
3924
await server.executeOperation({
4025
query: '{hello}',

dev-packages/node-integration-tests/suites/tracing/apollo-graphql/test.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { createRunner } from '../../../utils/runner';
22

3+
// Graphql Instrumentation emits some spans by default on server start
4+
const EXPECTED_START_SERVER_TRANSACTION = {
5+
transaction: 'Test Server Start',
6+
};
7+
38
describe('GraphQL/Apollo Tests', () => {
4-
test('CJS - should instrument GraphQL queries used from Apollo Server.', done => {
9+
test('should instrument GraphQL queries used from Apollo Server.', done => {
510
const EXPECTED_TRANSACTION = {
611
transaction: 'Test Transaction',
712
spans: expect.arrayContaining([
@@ -18,10 +23,13 @@ describe('GraphQL/Apollo Tests', () => {
1823
]),
1924
};
2025

21-
createRunner(__dirname, 'scenario-query.js').expect({ transaction: EXPECTED_TRANSACTION }).start(done);
26+
createRunner(__dirname, 'scenario-query.js')
27+
.expect({ transaction: EXPECTED_START_SERVER_TRANSACTION })
28+
.expect({ transaction: EXPECTED_TRANSACTION })
29+
.start(done);
2230
});
2331

24-
test('CJS - should instrument GraphQL mutations used from Apollo Server.', done => {
32+
test('should instrument GraphQL mutations used from Apollo Server.', done => {
2533
const EXPECTED_TRANSACTION = {
2634
transaction: 'Test Transaction',
2735
spans: expect.arrayContaining([
@@ -39,6 +47,9 @@ describe('GraphQL/Apollo Tests', () => {
3947
]),
4048
};
4149

42-
createRunner(__dirname, 'scenario-mutation.js').expect({ transaction: EXPECTED_TRANSACTION }).start(done);
50+
createRunner(__dirname, 'scenario-mutation.js')
51+
.expect({ transaction: EXPECTED_START_SERVER_TRANSACTION })
52+
.expect({ transaction: EXPECTED_TRANSACTION })
53+
.start(done);
4354
});
4455
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const Sentry = require('@sentry/node');
2+
const { loggingTransport } = require('@sentry-internal/node-integration-tests');
3+
4+
const client = Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
integrations: [Sentry.graphqlIntegration({ useOperationNameForRootSpan: true })],
9+
transport: loggingTransport,
10+
});
11+
12+
const tracer = client.tracer;
13+
14+
// Stop the process from exiting before the transaction is sent
15+
setInterval(() => {}, 1000);
16+
17+
async function run() {
18+
const server = require('../apollo-server')();
19+
20+
await tracer.startActiveSpan('test span name', async span => {
21+
// Ref: https://www.apollographql.com/docs/apollo-server/testing/testing/#testing-using-executeoperation
22+
await server.executeOperation({
23+
query: 'query GetHello {hello}',
24+
});
25+
26+
setTimeout(() => {
27+
span.end();
28+
server.stop();
29+
}, 500);
30+
});
31+
}
32+
33+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
34+
run();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const Sentry = require('@sentry/node');
2+
const { loggingTransport } = require('@sentry-internal/node-integration-tests');
3+
4+
const client = Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
integrations: [Sentry.graphqlIntegration({ useOperationNameForRootSpan: true })],
9+
transport: loggingTransport,
10+
});
11+
12+
const tracer = client.tracer;
13+
14+
// Stop the process from exiting before the transaction is sent
15+
setInterval(() => {}, 1000);
16+
17+
async function run() {
18+
const server = require('../apollo-server')();
19+
20+
await tracer.startActiveSpan(
21+
'test span name',
22+
{
23+
kind: 1,
24+
attributes: { 'http.method': 'GET', 'http.route': '/test-graphql' },
25+
},
26+
async span => {
27+
for (let i = 1; i < 10; i++) {
28+
// Ref: https://www.apollographql.com/docs/apollo-server/testing/testing/#testing-using-executeoperation
29+
await server.executeOperation({
30+
query: `query GetHello${i} {hello}`,
31+
});
32+
}
33+
34+
setTimeout(() => {
35+
span.end();
36+
server.stop();
37+
}, 500);
38+
},
39+
);
40+
}
41+
42+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
43+
run();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
const Sentry = require('@sentry/node');
2+
const { loggingTransport } = require('@sentry-internal/node-integration-tests');
3+
4+
const client = Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
integrations: [Sentry.graphqlIntegration({ useOperationNameForRootSpan: true })],
9+
transport: loggingTransport,
10+
});
11+
12+
const tracer = client.tracer;
13+
14+
// Stop the process from exiting before the transaction is sent
15+
setInterval(() => {}, 1000);
16+
17+
async function run() {
18+
const server = require('../apollo-server')();
19+
20+
await tracer.startActiveSpan(
21+
'test span name',
22+
{
23+
kind: 1,
24+
attributes: { 'http.method': 'GET', 'http.route': '/test-graphql' },
25+
},
26+
async span => {
27+
// Ref: https://www.apollographql.com/docs/apollo-server/testing/testing/#testing-using-executeoperation
28+
await server.executeOperation({
29+
query: 'query GetWorld {world}',
30+
});
31+
32+
await server.executeOperation({
33+
query: 'query GetHello {hello}',
34+
});
35+
36+
setTimeout(() => {
37+
span.end();
38+
server.stop();
39+
}, 500);
40+
},
41+
);
42+
}
43+
44+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
45+
run();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
const Sentry = require('@sentry/node');
2+
const { loggingTransport } = require('@sentry-internal/node-integration-tests');
3+
4+
const client = Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
integrations: [Sentry.graphqlIntegration({ useOperationNameForRootSpan: true })],
9+
transport: loggingTransport,
10+
});
11+
12+
const tracer = client.tracer;
13+
14+
// Stop the process from exiting before the transaction is sent
15+
setInterval(() => {}, 1000);
16+
17+
async function run() {
18+
const { gql } = require('apollo-server');
19+
const server = require('../apollo-server')();
20+
21+
await tracer.startActiveSpan(
22+
'test span name',
23+
{
24+
kind: 1,
25+
attributes: { 'http.method': 'GET', 'http.route': '/test-graphql' },
26+
},
27+
async span => {
28+
// Ref: https://www.apollographql.com/docs/apollo-server/testing/testing/#testing-using-executeoperation
29+
await server.executeOperation({
30+
query: gql`mutation TestMutation($email: String){
31+
login(email: $email)
32+
}`,
33+
variables: { email: '[email protected]' },
34+
});
35+
36+
setTimeout(() => {
37+
span.end();
38+
server.stop();
39+
}, 500);
40+
},
41+
);
42+
}
43+
44+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
45+
run();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
const Sentry = require('@sentry/node');
2+
const { loggingTransport } = require('@sentry-internal/node-integration-tests');
3+
4+
const client = Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
integrations: [Sentry.graphqlIntegration({ useOperationNameForRootSpan: true })],
9+
transport: loggingTransport,
10+
});
11+
12+
const tracer = client.tracer;
13+
14+
// Stop the process from exiting before the transaction is sent
15+
setInterval(() => {}, 1000);
16+
17+
async function run() {
18+
const server = require('../apollo-server')();
19+
20+
await tracer.startActiveSpan(
21+
'test span name',
22+
{
23+
kind: 1,
24+
attributes: { 'http.method': 'GET', 'http.route': '/test-graphql' },
25+
},
26+
async span => {
27+
// Ref: https://www.apollographql.com/docs/apollo-server/testing/testing/#testing-using-executeoperation
28+
await server.executeOperation({
29+
query: 'query {hello}',
30+
});
31+
32+
setTimeout(() => {
33+
span.end();
34+
server.stop();
35+
}, 500);
36+
},
37+
);
38+
}
39+
40+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
41+
run();

0 commit comments

Comments
 (0)