diff --git a/dev-packages/node-integration-tests/suites/tracing/socket.io/scenario.js b/dev-packages/node-integration-tests/suites/tracing/socket.io/scenario.js new file mode 100644 index 000000000000..ed785f8d9797 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/socket.io/scenario.js @@ -0,0 +1,54 @@ +const { loggingTransport, sendPortToRunner } = require('@sentry-internal/node-integration-tests'); +const Sentry = require('@sentry/node'); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, + transport: loggingTransport, +}); + +// Stop the process from exiting before the transaction is sent +setInterval(() => {}, 1000); + +const http = require('http'); +const express = require('express'); +const cors = require('cors'); +const { Server } = require('socket.io'); +const ioc = require('socket.io-client'); + +const PORT = 3005; + +const run = async () => { + const app = express(); + app.use(cors()); + + const expressServer = http.createServer(app); + + const io = new Server(expressServer); + + app.get('/', (_req, res) => { + io.emit('test', 'TEST MESSAGE'); + res.send('123'); + }); + + expressServer.listen(PORT, () => { + io.on('connection', client => { + client.on('test_reply', _data => { + client.disconnect(); + io.close(); + }); + }); + + const clientSocket = ioc(`http://localhost:${PORT}`); + + clientSocket.on('test', _msg => { + clientSocket.emit('test_reply', { test_key: 'test_value' }); + }); + }); + + sendPortToRunner(PORT); +}; + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +run(); diff --git a/dev-packages/node-integration-tests/suites/tracing/socket.io/test.ts b/dev-packages/node-integration-tests/suites/tracing/socket.io/test.ts new file mode 100644 index 000000000000..45b608760422 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/socket.io/test.ts @@ -0,0 +1,59 @@ +import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; + +describe('socket.io auto instrumentation', () => { + afterAll(() => { + cleanupChildProcesses(); + }); + + test('should auto-instrument `socket.io` package', done => { + const SERVER_TRANSACTION = { + transaction: 'GET /', + spans: expect.arrayContaining([ + expect.objectContaining({ + origin: 'auto.socket.otel.socket_io', + data: { + 'sentry.origin': 'auto.socket.otel.socket_io', + 'messaging.destination': '/', + 'messaging.destination_kind': 'topic', + 'messaging.socket.io.event_name': 'test', + 'messaging.socket.io.namespace': '/', + 'messaging.system': 'socket.io', + 'otel.kind': 'PRODUCER', + 'sentry.op': 'message', + }, + description: '/ send', + op: 'message', + status: 'ok', + }), + ]), + }; + + const CLIENT_TRANSACTION = { + transaction: 'test_reply receive', + contexts: { + trace: expect.objectContaining({ + span_id: expect.any(String), + trace_id: expect.any(String), + data: expect.objectContaining({ + 'sentry.op': 'message', + 'sentry.origin': 'auto.socket.otel.socket_io', + 'otel.kind': 'CONSUMER', + 'messaging.system': 'socket.io', + 'messaging.destination': '/', + 'messaging.operation': 'receive', + 'messaging.socket.io.event_name': 'test_reply', + }), + origin: 'auto.socket.otel.socket_io', + op: 'message', + status: 'ok', + }), + }, + }; + + createRunner(__dirname, 'scenario.js') + .expect({ transaction: SERVER_TRANSACTION }) + .expect({ transaction: CLIENT_TRANSACTION }) + .start(done) + .makeRequest('get', '/'); + }); +}); diff --git a/packages/node/src/integrations/tracing/socket.io.ts b/packages/node/src/integrations/tracing/socket.io.ts index a68a1ae96599..d8aa4adea15c 100644 --- a/packages/node/src/integrations/tracing/socket.io.ts +++ b/packages/node/src/integrations/tracing/socket.io.ts @@ -2,7 +2,6 @@ import { SocketIoInstrumentation } from '@opentelemetry/instrumentation-socket.i import { defineIntegration } from '@sentry/core'; import type { IntegrationFn } from '@sentry/types'; import { generateInstrumentOnce } from '../../otel/instrument'; - import { addOriginToSpan } from '../../utils/addOriginToSpan'; const INTEGRATION_NAME = 'Socket.io'; @@ -14,6 +13,10 @@ export const instrumentSocketIo = generateInstrumentOnce( emitHook(span) { addOriginToSpan(span, 'auto.socket.otel.socket_io'); }, + onHook(span) { + addOriginToSpan(span, 'auto.socket.otel.socket_io'); + }, + traceReserved: true, }), );