Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ref: Use span tree to send transaction events #12577

Closed
wants to merge 7 commits into from
Closed
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 @@ -7,7 +7,7 @@ const sentryTestProject = process.env.E2E_TEST_SENTRY_PROJECT;
const EVENT_POLLING_TIMEOUT = 90_000;

test('Sends an API route transaction', async ({ baseURL }) => {
const pageloadTransactionEventPromise = waitForTransaction('node-connect', transactionEvent => {
const transactionEventPromise = waitForTransaction('node-connect', transactionEvent => {
return (
transactionEvent?.contexts?.trace?.op === 'http.server' &&
transactionEvent?.transaction === 'GET /test-transaction'
Expand All @@ -16,8 +16,7 @@ test('Sends an API route transaction', async ({ baseURL }) => {

await fetch(`${baseURL}/test-transaction`);

const transactionEvent = await pageloadTransactionEventPromise;
const transactionEventId = transactionEvent.event_id;
const transactionEvent = await transactionEventPromise;

expect(transactionEvent.contexts?.trace).toEqual({
data: {
Expand Down Expand Up @@ -54,7 +53,7 @@ test('Sends an API route transaction', async ({ baseURL }) => {

expect(transactionEvent).toEqual(
expect.objectContaining({
spans: [
spans: expect.arrayContaining([
{
data: {
'sentry.origin': 'manual',
Expand Down Expand Up @@ -88,7 +87,7 @@ test('Sends an API route transaction', async ({ baseURL }) => {
trace_id: expect.any(String),
origin: 'auto.http.otel.connect',
},
],
]),
transaction: 'GET /test-transaction',
type: 'transaction',
transaction_info: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,31 @@ test('should report finished spans as children of the root transaction.', done =
.expect({
transaction: transaction => {
const rootSpanId = transaction.contexts?.trace?.span_id;
const span3Id = transaction.spans?.[1]?.span_id;
const span3Id = transaction.spans?.find(span => span.description === 'span_3')?.span_id;

expect(rootSpanId).toEqual(expect.any(String));
expect(span3Id).toEqual(expect.any(String));

assertSentryTransaction(transaction, {
transaction: 'root_span',
spans: [
{
spans: expect.arrayContaining([
expect.objectContaining({
description: 'span_1',
data: {
data: expect.objectContaining({
foo: 'bar',
baz: [1, 2, 3],
},
}),
parent_span_id: rootSpanId,
},
{
}),
expect.objectContaining({
description: 'span_3',
parent_span_id: rootSpanId,
},
{
}),
expect.objectContaining({
description: 'span_5',
parent_span_id: span3Id,
},
] as SpanJSON[],
}),
]) as SpanJSON[],
});
},
})
Expand Down
78 changes: 1 addition & 77 deletions packages/node/test/integration/transactions.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { TraceFlags, context, trace } from '@opentelemetry/api';
import type { SpanProcessor } from '@opentelemetry/sdk-trace-base';
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core';
import { SentrySpanProcessor } from '@sentry/opentelemetry';
import type { TransactionEvent } from '@sentry/types';
import { logger } from '@sentry/utils';

import * as Sentry from '../../src';
import { cleanupOtel, getProvider, mockSdkInit } from '../helpers/mockSdkInit';
import { cleanupOtel, mockSdkInit } from '../helpers/mockSdkInit';

describe('Integration | Transactions', () => {
afterEach(() => {
Expand Down Expand Up @@ -560,77 +557,4 @@ describe('Integration | Transactions', () => {
},
]);
});

it('cleans up spans that are not flushed for over 5 mins', async () => {
const beforeSendTransaction = jest.fn(() => null);

const now = Date.now();
jest.useFakeTimers();
jest.setSystemTime(now);

const logs: unknown[] = [];
jest.spyOn(logger, 'log').mockImplementation(msg => logs.push(msg));

mockSdkInit({ enableTracing: true, beforeSendTransaction });

const provider = getProvider();
const multiSpanProcessor = provider?.activeSpanProcessor as
| (SpanProcessor & { _spanProcessors?: SpanProcessor[] })
| undefined;
const spanProcessor = multiSpanProcessor?.['_spanProcessors']?.find(
spanProcessor => spanProcessor instanceof SentrySpanProcessor,
) as SentrySpanProcessor | undefined;

const exporter = spanProcessor ? spanProcessor['_exporter'] : undefined;

if (!exporter) {
throw new Error('No exporter found, aborting test...');
}

let innerSpan1Id: string | undefined;
let innerSpan2Id: string | undefined;

void Sentry.startSpan({ name: 'test name' }, async () => {
const subSpan = Sentry.startInactiveSpan({ name: 'inner span 1' });
innerSpan1Id = subSpan.spanContext().spanId;
subSpan.end();

Sentry.startSpan({ name: 'inner span 2' }, innerSpan => {
innerSpan2Id = innerSpan.spanContext().spanId;
});

// Pretend this is pending for 10 minutes
await new Promise(resolve => setTimeout(resolve, 10 * 60 * 1000));
});

jest.advanceTimersByTime(1);

// Child-spans have been added to the exporter, but they are pending since they are waiting for their parant
expect(exporter['_finishedSpans'].length).toBe(2);
expect(beforeSendTransaction).toHaveBeenCalledTimes(0);

// Now wait for 5 mins
jest.advanceTimersByTime(5 * 60 * 1_000 + 1);

// Adding another span will trigger the cleanup
Sentry.startSpan({ name: 'other span' }, () => {});

jest.advanceTimersByTime(1);

// Old spans have been cleared away
expect(exporter['_finishedSpans'].length).toBe(0);

// Called once for the 'other span'
expect(beforeSendTransaction).toHaveBeenCalledTimes(1);

expect(logs).toEqual(
expect.arrayContaining([
'SpanExporter has 1 unsent spans remaining',
'SpanExporter has 2 unsent spans remaining',
'SpanExporter exported 1 spans, 2 unsent spans remaining',
`SpanExporter dropping span inner span 1 (${innerSpan1Id}) because it is pending for more than 5 minutes.`,
`SpanExporter dropping span inner span 2 (${innerSpan2Id}) because it is pending for more than 5 minutes.`,
]),
);
});
});
5 changes: 5 additions & 0 deletions packages/node/test/sdk/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ afterEach(() => {
cleanupOtel();
});

// Prevent leakage between tests
afterEach(async () => {
await getClient()?.flush();
});

describe('withActiveSpan()', () => {
it('should set the active span within the callback', () => {
mockSdkInit();
Expand Down
Loading
Loading