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
12 changes: 12 additions & 0 deletions .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ module.exports = [
gzip: true,
disablePlugins: ['@size-limit/esbuild'],
},
// esbuild drops dead code differently from webpack + terser, so measure one bundle with it too.
{
name: '@sentry/browser (incl. Tracing) - esbuild with treeshaking flags',
path: 'packages/browser/build/npm/esm/prod/index.js',
import: createImport('init', 'browserTracingIntegration'),
gzip: true,
disablePlugins: ['@size-limit/webpack'],
modifyEsbuildConfig: function (config) {
config.define = { __SENTRY_DEBUG__: 'false' };
return config;
},
},
{
name: '@sentry/browser (incl. Tracing + Span Streaming)',
path: 'packages/browser/build/npm/esm/prod/index.js',
Expand Down
11 changes: 8 additions & 3 deletions packages/core/src/tracing/idleSpan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,11 +339,16 @@ export function startIdleSpan(startSpanOptions: StartSpanOptions, options: Parti
const spanEndedBeforeFinalTimeout = childEndTimestamp - childStartTimestamp <= timeoutWithMarginOfError;

if (DEBUG_BUILD) {
const stringifiedSpan = JSON.stringify(childSpan, undefined, 2);
if (!spanStartedBeforeIdleSpanEnd) {
debug.log('[Tracing] Discarding span since it happened after idle span was finished', stringifiedSpan);
debug.log(
'[Tracing] Discarding span since it happened after idle span was finished',
JSON.stringify(childSpan, undefined, 2),
);
} else if (!spanEndedBeforeFinalTimeout) {
debug.log('[Tracing] Discarding span since it finished after idle span final timeout', stringifiedSpan);
debug.log(
'[Tracing] Discarding span since it finished after idle span final timeout',
JSON.stringify(childSpan, undefined, 2),
);
}
}

Expand Down
93 changes: 47 additions & 46 deletions packages/core/src/tracing/logSpans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,58 @@ import type { Span } from '../types/span';
import { debug } from '../utils/debug-logger';
import { getRootSpan, spanIsSampled, spanToStaticSpanJSON } from '../utils/spanUtils';

// A ternary instead of an early return, so that esbuild can drop the debug-only body in non-debug builds.
/**
* Print a log message for a started span.
*/
export function logSpanStart(span: Span): void {
if (!DEBUG_BUILD) return;

const {
description = '< unknown name >',
op = '< unknown op >',
parent_span_id: parentSpanId,
} = spanToStaticSpanJSON(span);
const { spanId } = span.spanContext();

const sampled = spanIsSampled(span);
const rootSpan = getRootSpan(span);
const isRootSpan = rootSpan === span;

const header = `[Tracing] Starting ${sampled ? 'sampled' : 'unsampled'} ${isRootSpan ? 'root ' : ''}span`;

const infoParts: string[] = [`op: ${op}`, `name: ${description}`, `ID: ${spanId}`];

if (parentSpanId) {
infoParts.push(`parent ID: ${parentSpanId}`);
}

if (!isRootSpan) {
const { op, description } = spanToStaticSpanJSON(rootSpan);
infoParts.push(`root ID: ${rootSpan.spanContext().spanId}`);
if (op) {
infoParts.push(`root op: ${op}`);
}
if (description) {
infoParts.push(`root description: ${description}`);
}
}

debug.log(`${header}
export const logSpanStart: (span: Span) => void = DEBUG_BUILD
? span => {
const {
description = '< unknown name >',
op = '< unknown op >',
parent_span_id: parentSpanId,
} = spanToStaticSpanJSON(span);
const { spanId } = span.spanContext();

const sampled = spanIsSampled(span);
const rootSpan = getRootSpan(span);
const isRootSpan = rootSpan === span;

const header = `[Tracing] Starting ${sampled ? 'sampled' : 'unsampled'} ${isRootSpan ? 'root ' : ''}span`;

const infoParts: string[] = [`op: ${op}`, `name: ${description}`, `ID: ${spanId}`];

if (parentSpanId) {
infoParts.push(`parent ID: ${parentSpanId}`);
}

if (!isRootSpan) {
const { op, description } = spanToStaticSpanJSON(rootSpan);
infoParts.push(`root ID: ${rootSpan.spanContext().spanId}`);
if (op) {
infoParts.push(`root op: ${op}`);
}
if (description) {
infoParts.push(`root description: ${description}`);
}
}

debug.log(`${header}
${infoParts.join('\n ')}`);
}
}
: () => {};

/**
* Print a log message for an ended span.
*/
export function logSpanEnd(span: Span): void {
if (!DEBUG_BUILD) return;

const { description = '< unknown name >', op = '< unknown op >' } = spanToStaticSpanJSON(span);
const { spanId } = span.spanContext();
const rootSpan = getRootSpan(span);
const isRootSpan = rootSpan === span;

const msg = `[Tracing] Finishing "${op}" ${isRootSpan ? 'root ' : ''}span "${description}" with ID ${spanId}`;
debug.log(msg);
}
export const logSpanEnd: (span: Span) => void = DEBUG_BUILD
? span => {
const { description = '< unknown name >', op = '< unknown op >' } = spanToStaticSpanJSON(span);
const { spanId } = span.spanContext();
const rootSpan = getRootSpan(span);
const isRootSpan = rootSpan === span;

const msg = `[Tracing] Finishing "${op}" ${isRootSpan ? 'root ' : ''}span "${description}" with ID ${spanId}`;
debug.log(msg);
}
: () => {};
67 changes: 33 additions & 34 deletions packages/core/src/utils/dsn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,43 +80,42 @@ function dsnFromComponents(components: DsnComponents): DsnComponents {
};
}

function validateDsn(dsn: DsnComponents): boolean {
if (!DEBUG_BUILD) {
return true;
}

const { port, projectId, protocol } = dsn;
// A ternary instead of an early return, so that esbuild can drop the debug-only body in non-debug builds.
const validateDsn: (dsn: DsnComponents) => boolean = DEBUG_BUILD
? dsn => {
const { port, projectId, protocol } = dsn;

const requiredComponents: ReadonlyArray<keyof DsnComponents> = ['protocol', 'publicKey', 'host', 'projectId'];
const hasMissingRequiredComponent = requiredComponents.find(component => {
if (!dsn[component]) {
debug.error(`Invalid Sentry Dsn: ${component} missing`);
return true;
}
return false;
});

if (hasMissingRequiredComponent) {
return false;
}

if (!projectId.match(/^\d+$/)) {
debug.error(`Invalid Sentry Dsn: Invalid projectId ${projectId}`);
return false;
}

if (!isValidProtocol(protocol)) {
debug.error(`Invalid Sentry Dsn: Invalid protocol ${protocol}`);
return false;
}

if (port && isNaN(parseInt(port, 10))) {
debug.error(`Invalid Sentry Dsn: Invalid port ${port}`);
return false;
}

const requiredComponents: ReadonlyArray<keyof DsnComponents> = ['protocol', 'publicKey', 'host', 'projectId'];
const hasMissingRequiredComponent = requiredComponents.find(component => {
if (!dsn[component]) {
debug.error(`Invalid Sentry Dsn: ${component} missing`);
return true;
}
return false;
});

if (hasMissingRequiredComponent) {
return false;
}

if (!projectId.match(/^\d+$/)) {
debug.error(`Invalid Sentry Dsn: Invalid projectId ${projectId}`);
return false;
}

if (!isValidProtocol(protocol)) {
debug.error(`Invalid Sentry Dsn: Invalid protocol ${protocol}`);
return false;
}

if (port && isNaN(parseInt(port, 10))) {
debug.error(`Invalid Sentry Dsn: Invalid port ${port}`);
return false;
}

return true;
}
: () => true;

/**
* Extract the org ID from a DSN host.
Expand Down
48 changes: 48 additions & 0 deletions packages/core/test/lib/tracing/logSpans.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import { SentrySpan } from '../../../src/tracing/sentrySpan';

// must be var, because vi.mock hoists
var debugBuild: boolean = true;
vi.mock(import('../../../src/debug-build'), () => ({
get DEBUG_BUILD() {
return debugBuild ?? true;
},
}));

describe('logSpans', () => {
afterEach(() => {
debugBuild = true;
vi.restoreAllMocks();
vi.resetModules();
});

it('logs span start and end in debug builds', async () => {
const { logSpanEnd, logSpanStart } = await import('../../../src/tracing/logSpans');
const { debug } = await import('../../../src/utils/debug-logger');
const logSpy = vi.spyOn(debug, 'log').mockImplementation(() => {});
const span = new SentrySpan({ name: 'test span', op: 'test.op', sampled: true });

logSpanStart(span);
logSpanEnd(span);

expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('[Tracing] Starting sampled root span'));
expect(logSpy).toHaveBeenCalledWith(
expect.stringContaining(
`[Tracing] Finishing "test.op" root span "test span" with ID ${span.spanContext().spanId}`,
),
);
});

it('is a no-op in non-debug builds', async () => {
debugBuild = false;
const { logSpanEnd, logSpanStart } = await import('../../../src/tracing/logSpans');
const { debug } = await import('../../../src/utils/debug-logger');
const logSpy = vi.spyOn(debug, 'log').mockImplementation(() => {});
const span = new SentrySpan({ name: 'test span', op: 'test.op', sampled: true });

logSpanStart(span);
logSpanEnd(span);

expect(logSpy).not.toHaveBeenCalled();
});
});
9 changes: 6 additions & 3 deletions packages/core/test/lib/utils/dsn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { debug } from '../../../src/utils/debug-logger';
import { dsnToString, extractOrgIdFromClient, extractOrgIdFromDsnHost, makeDsn } from '../../../src/utils/dsn';
import { getDefaultTestClientOptions, TestClient } from '../../mocks/client';

let mockDebugBuild = true;
// must be var, because vi.mock hoists
var mockDebugBuild: boolean = true;

vi.mock('../../../src/debug-build', () => ({
get DEBUG_BUILD() {
return mockDebugBuild;
return mockDebugBuild ?? true;
},
}));

Expand Down Expand Up @@ -204,8 +205,10 @@ describe('Dsn', () => {
expect(dsn?.projectId).toBe('123');
});

test('skips validation for non-debug builds', () => {
test('skips validation for non-debug builds', async () => {
vi.resetModules();
mockDebugBuild = false;
const { makeDsn } = await import('../../../src/utils/dsn');
const dsn = makeDsn('httx://abc@192.168.1.1/123');
expect(dsn?.protocol).toBe('httx');
expect(dsn?.publicKey).toBe('abc');
Expand Down
Loading