Skip to content

Commit fb171b0

Browse files
committed
feat(node): Add dataloader instrumentation
1 parent e944daa commit fb171b0

File tree

8 files changed

+142
-0
lines changed

8 files changed

+142
-0
lines changed

dev-packages/node-integration-tests/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"connect": "^3.7.0",
4444
"cors": "^2.8.5",
4545
"cron": "^3.1.6",
46+
"dataloader": "2.2.2",
4647
"express": "^4.17.3",
4748
"generic-pool": "^3.9.0",
4849
"graphql": "^16.3.0",
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const { loggingTransport, startExpressServerAndSendPortToRunner } = require('@sentry-internal/node-integration-tests');
2+
const Sentry = require('@sentry/node');
3+
4+
Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
transport: loggingTransport,
9+
});
10+
11+
const PORT = 8008;
12+
13+
// Stop the process from exiting before the transaction is sent
14+
setInterval(() => {}, 1000);
15+
16+
const run = async () => {
17+
const express = require('express');
18+
const Dataloader = require('dataloader');
19+
20+
const app = express();
21+
const dataloader = new Dataloader(async keys => keys.map((_, idx) => idx), {
22+
cache: false,
23+
});
24+
25+
app.get('/', (req, res) => {
26+
const user = dataloader.load('user-1');
27+
res.send(user);
28+
});
29+
30+
startExpressServerAndSendPortToRunner(app, PORT);
31+
};
32+
33+
run();
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { cleanupChildProcesses, createRunner } from '../../../utils/runner';
2+
3+
describe('dataloader auto-instrumentation', () => {
4+
afterAll(async () => {
5+
cleanupChildProcesses();
6+
});
7+
8+
const EXPECTED_TRANSACTION = {
9+
transaction: 'GET /',
10+
spans: expect.arrayContaining([
11+
expect.objectContaining({
12+
data: expect.objectContaining({
13+
'sentry.origin': 'auto.db.otel.dataloader',
14+
'sentry.op': 'cache.get',
15+
}),
16+
description: 'dataloader.load',
17+
origin: 'auto.db.otel.dataloader',
18+
op: 'cache.get',
19+
status: 'ok',
20+
}),
21+
expect.objectContaining({
22+
data: expect.objectContaining({
23+
'sentry.origin': 'auto.db.otel.dataloader',
24+
'sentry.op': 'cache.get',
25+
}),
26+
description: 'dataloader.batch',
27+
origin: 'auto.db.otel.dataloader',
28+
op: 'cache.get',
29+
status: 'ok',
30+
}),
31+
]),
32+
};
33+
34+
test('should auto-instrument `dataloader` package.', done => {
35+
createRunner(__dirname, 'scenario.js')
36+
.expect({ transaction: EXPECTED_TRANSACTION })
37+
.start(done)
38+
.makeRequest('get', '/');
39+
});
40+
});

packages/node/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"@opentelemetry/core": "^1.25.1",
7171
"@opentelemetry/instrumentation": "^0.53.0",
7272
"@opentelemetry/instrumentation-connect": "0.39.0",
73+
"@opentelemetry/instrumentation-dataloader": "0.12.0",
7374
"@opentelemetry/instrumentation-express": "0.42.0",
7475
"@opentelemetry/instrumentation-fastify": "0.39.0",
7576
"@opentelemetry/instrumentation-fs": "0.15.0",

packages/node/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export { koaIntegration, setupKoaErrorHandler } from './integrations/tracing/koa
2828
export { connectIntegration, setupConnectErrorHandler } from './integrations/tracing/connect';
2929
export { spotlightIntegration } from './integrations/spotlight';
3030
export { genericPoolIntegration } from './integrations/tracing/genericPool';
31+
export { dataloaderIntegration } from './integrations/tracing/dataloader';
3132

3233
export { SentryContextManager } from './otel/contextManager';
3334
export { generateInstrumentOnce } from './otel/instrument';
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { DataloaderInstrumentation } from '@opentelemetry/instrumentation-dataloader';
2+
import {
3+
SEMANTIC_ATTRIBUTE_SENTRY_OP,
4+
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
5+
defineIntegration,
6+
spanToJSON,
7+
} from '@sentry/core';
8+
import type { IntegrationFn } from '@sentry/types';
9+
import { generateInstrumentOnce } from '../../otel/instrument';
10+
11+
const INTEGRATION_NAME = 'Dataloader';
12+
13+
export const instrumentDataloader = generateInstrumentOnce(INTEGRATION_NAME, () => new DataloaderInstrumentation({}));
14+
15+
const _dataloaderIntegration = (() => {
16+
return {
17+
name: INTEGRATION_NAME,
18+
setupOnce() {
19+
instrumentDataloader();
20+
},
21+
22+
setup(client) {
23+
client.on('spanStart', span => {
24+
const spanJSON = spanToJSON(span);
25+
if (spanJSON.description?.startsWith('dataloader')) {
26+
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.dataloader');
27+
}
28+
29+
// These are all possible dataloader span descriptions
30+
// Still checking for the future versions
31+
// in case they add support for `clear` and `prime`
32+
if (
33+
spanJSON.description === 'dataloader.load' ||
34+
spanJSON.description === 'dataloader.loadMany' ||
35+
spanJSON.description === 'dataloader.batch'
36+
) {
37+
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'cache.get');
38+
// TODO: We can try adding `key` to the `data` attribute upstream.
39+
// Or alternatively, we can add `requestHook` to the dataloader instrumentation.
40+
}
41+
});
42+
},
43+
};
44+
}) satisfies IntegrationFn;
45+
46+
/**
47+
* Dataloader integration
48+
*
49+
* Capture tracing data for Dataloader.
50+
*/
51+
export const dataloaderIntegration = defineIntegration(_dataloaderIntegration);

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Integration } from '@sentry/types';
22
import { instrumentHttp } from '../http';
33

44
import { connectIntegration, instrumentConnect } from './connect';
5+
import { dataloaderIntegration, instrumentDataloader } from './dataloader';
56
import { expressIntegration, instrumentExpress } from './express';
67
import { fastifyIntegration, instrumentFastify } from './fastify';
78
import { genericPoolIntegration, instrumentGenericPool } from './genericPool';
@@ -41,6 +42,7 @@ export function getAutoPerformanceIntegrations(): Integration[] {
4142
connectIntegration(),
4243
genericPoolIntegration(),
4344
kafkaIntegration(),
45+
dataloaderIntegration(),
4446
];
4547
}
4648

@@ -67,5 +69,6 @@ export function getOpenTelemetryInstrumentationToPreload(): (((options?: any) =>
6769
instrumentGraphql,
6870
instrumentRedis,
6971
instrumentGenericPool,
72+
instrumentDataloader,
7073
];
7174
}

yarn.lock

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7101,6 +7101,13 @@
71017101
"@opentelemetry/semantic-conventions" "^1.27.0"
71027102
"@types/connect" "3.4.36"
71037103

7104+
"@opentelemetry/[email protected]":
7105+
version "0.12.0"
7106+
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.12.0.tgz#de03a3948dec4f15fed80aa424d6bd5d6a8d10c7"
7107+
integrity sha512-pnPxatoFE0OXIZDQhL2okF//dmbiWFzcSc8pUg9TqofCLYZySSxDCgQc69CJBo5JnI3Gz1KP+mOjS4WAeRIH4g==
7108+
dependencies:
7109+
"@opentelemetry/instrumentation" "^0.53.0"
7110+
71047111
"@opentelemetry/[email protected]":
71057112
version "0.42.0"
71067113
resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.42.0.tgz#279f195aa66baee2b98623a16666c6229c8e7564"
@@ -15235,6 +15242,11 @@ data-urls@^4.0.0:
1523515242
whatwg-mimetype "^3.0.0"
1523615243
whatwg-url "^12.0.0"
1523715244

15245+
15246+
version "2.2.2"
15247+
resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0"
15248+
integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==
15249+
1523815250
date-fns@^2.29.2:
1523915251
version "2.29.3"
1524015252
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"

0 commit comments

Comments
 (0)