Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ test('Sends an app_creation transaction', async () => {
contexts: { trace: { op?: string; origin?: string; data?: Record<string, unknown> } };
};

expect(transaction.contexts.trace.op).toBe('app_creation.nestjs');
expect(transaction.contexts.trace.op).toBe('function');
expect(transaction.contexts.trace.origin).toBe('auto.http.nestjs');
expect(transaction.contexts.trace.data).toEqual(
expect.objectContaining({
component: '@nestjs/core',
'nestjs.type': 'app_creation',
'nestjs.module': 'AppModule',
'sentry.op': 'app_creation.nestjs',
'sentry.op': 'function',
'sentry.origin': 'auto.http.nestjs',
}),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ test('Event emitter', async () => {
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
data: {
'sentry.source': 'custom',
'sentry.op': 'event.nestjs',
'sentry.op': 'function',
'sentry.origin': 'auto.event.nestjs',
},
origin: 'auto.event.nestjs',
op: 'event.nestjs',
op: 'function',
status: 'ok',
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ test.skip('Sends an API route transaction', async ({ baseURL }) => {
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
data: {
'sentry.origin': 'auto.http.nestjs',
'sentry.op': 'request_context.nestjs',
'sentry.op': 'function',
component: '@nestjs/core',
'nestjs.version': expect.any(String),
'nestjs.type': 'request_context',
Expand All @@ -117,7 +117,7 @@ test.skip('Sends an API route transaction', async ({ baseURL }) => {
start_timestamp: expect.any(Number),
timestamp: expect.any(Number),
status: 'ok',
op: 'request_context.nestjs',
op: 'function',
origin: 'auto.http.nestjs',
},
{
Expand Down Expand Up @@ -798,8 +798,16 @@ test('Sets error status on nest spans when a handler throws', async ({ baseURL }

expect(transactionEvent.spans).toEqual(
expect.arrayContaining([
expect.objectContaining({ op: 'request_context.nestjs', status: 'internal_error' }),
expect.objectContaining({ op: 'function', status: 'internal_error' }),
expect.objectContaining({
op: 'function',
status: 'internal_error',
data: expect.objectContaining({ 'nestjs.type': 'request_context' }),
}),
expect.objectContaining({
op: 'function',
status: 'internal_error',
data: expect.objectContaining({ 'nestjs.type': 'handler' }),
}),
]),
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ test('@OnEvent opens an event.nestjs transaction', async ({ baseURL }) => {
await fetch(`${baseURL}/test-event`);
const transactionEvent = await transactionPromise;

expect(transactionEvent.contexts?.trace?.op).toBe('event.nestjs');
expect(transactionEvent.contexts?.trace?.op).toBe('function');
expect(transactionEvent.contexts?.trace?.origin).toBe('auto.event.nestjs');
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ test('app_creation: emits a "Create Nest App" transaction at startup', async ()
contexts: { trace: { op?: string; origin?: string; data?: Record<string, unknown> } };
};

expect(transaction.contexts.trace.op).toBe('app_creation.nestjs');
expect(transaction.contexts.trace.op).toBe('function');
expect(transaction.contexts.trace.origin).toBe('auto.http.nestjs');
expect(transaction.contexts.trace.data).toEqual(
expect.objectContaining({
Expand All @@ -47,10 +47,13 @@ test('request_context + handler: a route transaction nests the nestjs spans', as
await fetch(`${baseURL}/test-transaction`);
const transactionEvent = await transactionPromise;

// request_context span, identified by its controller/callback attributes.
// request_context span, identified by its `nestjs.type` attribute — it shares
// the `function` op and `auto.http.nestjs` origin with the request_handler span.
// Its description isn't asserted: the span carries `http.*` attributes, so
// the OTel span-name inference rewrites it to `GET /test-transaction`
const requestContext = findSpan(transactionEvent, 'request_context.nestjs', 'auto.http.nestjs');
const requestContext = (transactionEvent.spans ?? []).find(
span => span.data?.['nestjs.type'] === 'request_context' && span.origin === 'auto.http.nestjs',
);
expect(requestContext).toBeDefined();
expect(requestContext?.data).toMatchObject({
'nestjs.type': 'request_context',
Expand Down
4 changes: 2 additions & 2 deletions packages/nestjs/src/integrations/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SENTRY_OP } from '@sentry/conventions/attributes';
import { WEB_SERVER_MIDDLEWARE_SPAN_OP } from '@sentry/conventions/op';
import { WEB_SERVER_FUNCTION_SPAN_OP, WEB_SERVER_MIDDLEWARE_SPAN_OP } from '@sentry/conventions/op';
import type { Span } from '@sentry/core';
import {
addNonEnumerableProperty,
Expand Down Expand Up @@ -108,7 +108,7 @@ export function getEventSpanOptions(event: string): {
return {
name: `event ${event}`,
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'event.nestjs',
[SENTRY_OP]: WEB_SERVER_FUNCTION_SPAN_OP,
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.event.nestjs',
},
forceTransaction: true,
Expand Down
13 changes: 6 additions & 7 deletions packages/nestjs/src/integrations/wrap-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ interface NestRequest {
url?: string;
}

/** Span options for the `Create Nest App` (app_creation) span. */
/** Span options for the `Create Nest App` (app_creation, `function` op) span. */
export function getAppCreationSpanOptions(
moduleVersion?: string,
moduleName?: string,
): { name: string; op: string; attributes: SpanAttributes } {
): { name: string; attributes: SpanAttributes } {
return {
name: 'Create Nest App',
op: `${NestType.APP_CREATION}.nestjs`,
attributes: {
component: NESTJS_COMPONENT,
[SENTRY_OP]: WEB_SERVER_FUNCTION_SPAN_OP,
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: HTTP_ORIGIN,
[AttributeNames.TYPE]: NestType.APP_CREATION,
[AttributeNames.VERSION]: moduleVersion || undefined,
Expand Down Expand Up @@ -73,7 +73,7 @@ export function wrapRouteHandler(callback: AnyFn, moduleVersion?: string): AnyFn

/**
* Wrap the per-request handler that `RouterExecutionContext.create` returns so
* each request opens the `request_context.nestjs` span (REQUEST_CONTEXT),
* each request opens the request-context (`function` op) span (REQUEST_CONTEXT),
* carrying controller/callback names plus the per-request http.* attributes.
*/
export function wrapRequestContextHandler(
Expand All @@ -89,6 +89,7 @@ export function wrapRequestContextHandler(
const attributes: SpanAttributes = {
component: NESTJS_COMPONENT,
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: HTTP_ORIGIN,
[SENTRY_OP]: WEB_SERVER_FUNCTION_SPAN_OP,
[AttributeNames.TYPE]: NestType.REQUEST_CONTEXT,
[AttributeNames.CONTROLLER]: instanceName,
[AttributeNames.CALLBACK]: callbackName,
Expand All @@ -98,9 +99,7 @@ export function wrapRequestContextHandler(
[HTTP_METHOD]: req.method || undefined,
[URL_FULL]: req.originalUrl || req.url || undefined,
};
return startSpan({ name: spanName, op: `${NestType.REQUEST_CONTEXT}.nestjs`, attributes }, () =>
handler.apply(this, handlerArgs),
);
return startSpan({ name: spanName, attributes }, () => handler.apply(this, handlerArgs));
};
markWrapped(wrapped);
return wrapped;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ describe('NestJS orchestrion subscriber: app_creation', () => {
expect(span).toBeDefined();
const json = spanToJSON(span!);
expect(json.description).toBe('Create Nest App');
expect(json.op).toBe('app_creation.nestjs');
expect(json.op).toBe('function');
expect(json.origin).toBe('auto.http.nestjs');
expect(json.data).toMatchObject({
component: '@nestjs/core',
Expand Down Expand Up @@ -241,7 +241,7 @@ describe('NestJS orchestrion subscriber: request_context / request_handler', ()

expect(contextSpanJson).toBeDefined();
expect(contextSpanJson!.description).toBe('CatsController.getCats');
expect(contextSpanJson!.op).toBe('request_context.nestjs');
expect(contextSpanJson!.op).toBe('function');
expect(contextSpanJson!.origin).toBe('auto.http.nestjs');
expect(contextSpanJson!.data).toMatchObject({
component: '@nestjs/core',
Expand Down Expand Up @@ -872,7 +872,7 @@ describe('NestJS orchestrion subscriber: schedule / event / bullmq', () => {

const json = spanToJSON(spanInside!);
expect(json.description).toBe('event user.created');
expect(json.op).toBe('event.nestjs');
expect(json.op).toBe('function');
expect(json.origin).toBe('auto.event.nestjs');
});

Expand Down
Loading