Skip to content

Commit d8f12c2

Browse files
authored
fix(node): Ensure graphql errors result in errored spans (#16678)
Closes #16649
1 parent f0cad82 commit d8f12c2

File tree

3 files changed

+77
-1
lines changed

3 files changed

+77
-1
lines changed
Lines changed: 43 additions & 0 deletions
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+
Sentry.init({
5+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
transport: loggingTransport,
9+
});
10+
11+
// Stop the process from exiting before the transaction is sent
12+
setInterval(() => {}, 1000);
13+
14+
async function run() {
15+
const { gql } = require('apollo-server');
16+
const server = require('./apollo-server')();
17+
18+
await Sentry.startSpan(
19+
{
20+
name: 'Test Transaction',
21+
op: 'transaction',
22+
},
23+
async span => {
24+
// Ref: https://www.apollographql.com/docs/apollo-server/testing/testing/#testing-using-executeoperation
25+
await server.executeOperation({
26+
query: gql`
27+
mutation Mutation($email: String) {
28+
login(email: $email)
29+
}
30+
`,
31+
// We want to trigger an error by passing an invalid variable type
32+
variables: { email: 123 },
33+
});
34+
35+
setTimeout(() => {
36+
span.end();
37+
server.stop();
38+
}, 500);
39+
},
40+
);
41+
}
42+
43+
run();

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,29 @@ describe('GraphQL/Apollo Tests', () => {
5555
.start()
5656
.completed();
5757
});
58+
59+
test('should handle GraphQL errors.', async () => {
60+
const EXPECTED_TRANSACTION = {
61+
transaction: 'Test Transaction (mutation Mutation)',
62+
spans: expect.arrayContaining([
63+
expect.objectContaining({
64+
data: {
65+
'graphql.operation.name': 'Mutation',
66+
'graphql.operation.type': 'mutation',
67+
'graphql.source': 'mutation Mutation($email: String) {\n login(email: $email)\n}',
68+
'sentry.origin': 'auto.graphql.otel.graphql',
69+
},
70+
description: 'mutation Mutation',
71+
status: 'unknown_error',
72+
origin: 'auto.graphql.otel.graphql',
73+
}),
74+
]),
75+
};
76+
77+
await createRunner(__dirname, 'scenario-error.js')
78+
.expect({ transaction: EXPECTED_START_SERVER_TRANSACTION })
79+
.expect({ transaction: EXPECTED_TRANSACTION })
80+
.start()
81+
.completed();
82+
});
5883
});

packages/node/src/integrations/tracing/graphql.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { AttributeValue } from '@opentelemetry/api';
2+
import { SpanStatusCode } from '@opentelemetry/api';
23
import { GraphQLInstrumentation } from '@opentelemetry/instrumentation-graphql';
34
import type { IntegrationFn } from '@sentry/core';
45
import { defineIntegration, getRootSpan, spanToJSON } from '@sentry/core';
@@ -45,9 +46,16 @@ export const instrumentGraphql = generateInstrumentOnce(
4546

4647
return {
4748
...options,
48-
responseHook(span) {
49+
responseHook(span, result) {
4950
addOriginToSpan(span, 'auto.graphql.otel.graphql');
5051

52+
// We want to ensure spans are marked as errored if there are errors in the result
53+
// We only do that if the span is not already marked with a status
54+
const resultWithMaybeError = result as { errors?: { message: string }[] };
55+
if (resultWithMaybeError.errors?.length && !spanToJSON(span).status) {
56+
span.setStatus({ code: SpanStatusCode.ERROR });
57+
}
58+
5159
const attributes = spanToJSON(span).data;
5260

5361
// If operation.name is not set, we fall back to use operation.type only

0 commit comments

Comments
 (0)