diff --git a/dev-packages/e2e-tests/test-applications/node-hapi/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-hapi/tests/transactions.test.ts index 2c07128b6561..0f38cff86e79 100644 --- a/dev-packages/e2e-tests/test-applications/node-hapi/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/node-hapi/tests/transactions.test.ts @@ -56,6 +56,30 @@ test('Sends successful transaction', async ({ baseURL }) => { }, }), ); + + const spans = transactionEvent.spans || []; + + expect(spans).toEqual([ + { + data: { + 'hapi.type': 'router', + 'http.method': 'GET', + 'http.route': '/test-success', + 'otel.kind': 'INTERNAL', + 'sentry.op': 'router.hapi', + 'sentry.origin': 'auto.http.otel.hapi', + }, + description: 'GET /test-success', + op: 'router.hapi', + origin: 'auto.http.otel.hapi', + parent_span_id: expect.any(String), + span_id: expect.any(String), + start_timestamp: expect.any(Number), + status: 'ok', + timestamp: expect.any(Number), + trace_id: expect.any(String), + }, + ]); }); test('Sends parameterized transactions to Sentry', async ({ baseURL }) => { diff --git a/dev-packages/node-integration-tests/suites/tracing/hapi/test.ts b/dev-packages/node-integration-tests/suites/tracing/hapi/test.ts index 10967a6d6be2..903b3b0b6fa8 100644 --- a/dev-packages/node-integration-tests/suites/tracing/hapi/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/hapi/test.ts @@ -15,11 +15,12 @@ describe('hapi auto-instrumentation', () => { 'http.route': '/', 'http.method': 'GET', 'hapi.type': 'router', - 'sentry.origin': 'manual', - 'sentry.op': 'http', + 'sentry.origin': 'auto.http.otel.hapi', + 'sentry.op': 'router.hapi', }), description: 'GET /', - op: 'http', + op: 'router.hapi', + origin: 'auto.http.otel.hapi', status: 'ok', }), ]), diff --git a/packages/node/src/integrations/tracing/hapi/index.ts b/packages/node/src/integrations/tracing/hapi/index.ts index 7f55867c8bf5..42da7423ca5b 100644 --- a/packages/node/src/integrations/tracing/hapi/index.ts +++ b/packages/node/src/integrations/tracing/hapi/index.ts @@ -2,17 +2,21 @@ import { isWrapped } from '@opentelemetry/core'; import { HapiInstrumentation } from '@opentelemetry/instrumentation-hapi'; import { SDK_VERSION, + SEMANTIC_ATTRIBUTE_SENTRY_OP, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_STATUS_ERROR, captureException, defineIntegration, getActiveSpan, + getClient, getDefaultIsolationScope, getIsolationScope, getRootSpan, isEnabled, + spanToJSON, } from '@sentry/core'; import { addOpenTelemetryInstrumentation } from '@sentry/opentelemetry'; -import type { IntegrationFn } from '@sentry/types'; +import type { IntegrationFn, Span } from '@sentry/types'; import { consoleSandbox, logger } from '@sentry/utils'; import { DEBUG_BUILD } from '../../../debug-build'; import type { Boom, RequestEvent, ResponseObject, Server } from './types'; @@ -95,6 +99,16 @@ export const hapiErrorPlugin = { export async function setupHapiErrorHandler(server: Server): Promise { await server.register(hapiErrorPlugin); + // Sadly, middleware spans do not go through `requestHook`, so we handle those here + // We register this hook in this method, because if we register it in the integration `setup`, + // it would always run even for users that are not even using hapi + const client = getClient(); + if (client) { + client.on('spanStart', span => { + addHapiSpanAttributes(span); + }); + } + // eslint-disable-next-line @typescript-eslint/unbound-method if (!isWrapped(server.register) && isEnabled()) { consoleSandbox(() => { @@ -105,3 +119,20 @@ export async function setupHapiErrorHandler(server: Server): Promise { }); } } + +function addHapiSpanAttributes(span: Span): void { + const attributes = spanToJSON(span).data || {}; + + // this is one of: router, plugin, server.ext + const type = attributes['hapi.type']; + + // If this is already set, or we have no Hapi span, no need to process again... + if (attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] || !type) { + return; + } + + span.setAttributes({ + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.hapi', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: `${type}.hapi`, + }); +}