Skip to content

Commit 8e6c10a

Browse files
authored
Merge branch 'develop' into abhi-browser-logs-integration
2 parents 807d4cf + 00a1018 commit 8e6c10a

File tree

37 files changed

+183
-248
lines changed

37 files changed

+183
-248
lines changed

dev-packages/e2e-tests/test-applications/nuxt-3/server/plugins/customNitroErrorHandler.ts

+13-15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Context, GLOBAL_OBJ, dropUndefinedKeys, flush, logger, vercelWaitUntil } from '@sentry/core';
1+
import { Context, GLOBAL_OBJ, flush, logger, vercelWaitUntil } from '@sentry/core';
22
import * as SentryNode from '@sentry/node';
33
import { H3Error } from 'h3';
44
import type { CapturedErrorContext } from 'nitropack';
@@ -36,24 +36,22 @@ export default defineNitroPlugin(nitroApp => {
3636
});
3737

3838
function extractErrorContext(errorContext: CapturedErrorContext): Context {
39-
const structuredContext: Context = {
40-
method: undefined,
41-
path: undefined,
42-
tags: undefined,
43-
};
39+
const ctx: Context = {};
4440

45-
if (errorContext) {
46-
if (errorContext.event) {
47-
structuredContext.method = errorContext.event._method || undefined;
48-
structuredContext.path = errorContext.event._path || undefined;
49-
}
41+
if (!errorContext) {
42+
return ctx;
43+
}
5044

51-
if (Array.isArray(errorContext.tags)) {
52-
structuredContext.tags = errorContext.tags || undefined;
53-
}
45+
if (errorContext.event) {
46+
ctx.method = errorContext.event._method;
47+
ctx.path = errorContext.event._path;
48+
}
49+
50+
if (Array.isArray(errorContext.tags)) {
51+
ctx.tags = errorContext.tags;
5452
}
5553

56-
return dropUndefinedKeys(structuredContext);
54+
return ctx;
5755
}
5856

5957
async function flushIfServerless(): Promise<void> {

packages/browser/src/log.ts

-32
Original file line numberDiff line numberDiff line change
@@ -257,36 +257,4 @@ export function fatal(message: ParameterizedString, attributes?: Log['attributes
257257
captureLog('fatal', message, attributes);
258258
}
259259

260-
/**
261-
* @summary Capture a log with the `critical` level. Requires `_experiments.enableLogs` to be enabled.
262-
*
263-
* @param message - The message to log.
264-
* @param attributes - Arbitrary structured data that stores information about the log - e.g., { security: 'breach', severity: 'high' }.
265-
*
266-
* @example
267-
*
268-
* ```
269-
* Sentry.logger.critical('Security breach detected', {
270-
* type: 'unauthorized_access',
271-
* user: '132123',
272-
* endpoint: '/api/admin',
273-
* timestamp: Date.now()
274-
* });
275-
* ```
276-
*
277-
* @example With template strings
278-
*
279-
* ```
280-
* Sentry.logger.critical(Sentry.logger.fmt`Multiple failed login attempts from user ${user}`, {
281-
* attempts: 10,
282-
* timeWindow: '5m',
283-
* blocked: true,
284-
* timestamp: Date.now()
285-
* });
286-
* ```
287-
*/
288-
export function critical(message: ParameterizedString, attributes?: Log['attributes']): void {
289-
captureLog('critical', message, attributes);
290-
}
291-
292260
export { fmt } from '@sentry/core';

packages/browser/test/index.test.ts

-1
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,6 @@ describe('SentryBrowser', () => {
334334
expect(logger.warn).toBeDefined();
335335
expect(logger.error).toBeDefined();
336336
expect(logger.fatal).toBeDefined();
337-
expect(logger.critical).toBeDefined();
338337
});
339338
});
340339
});

packages/browser/test/log.test.ts

-15
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ describe('Logger', () => {
6464
expect(logger.warn).toBeTypeOf('function');
6565
expect(logger.error).toBeTypeOf('function');
6666
expect(logger.fatal).toBeTypeOf('function');
67-
expect(logger.critical).toBeTypeOf('function');
6867
});
6968

7069
it('should call _INTERNAL_captureLog with trace level', () => {
@@ -150,20 +149,6 @@ describe('Logger', () => {
150149
undefined,
151150
);
152151
});
153-
154-
it('should call _INTERNAL_captureLog with critical level', () => {
155-
logger.critical('Test critical message', { key: 'value' });
156-
expect(mockCaptureLog).toHaveBeenCalledWith(
157-
{
158-
level: 'critical',
159-
message: 'Test critical message',
160-
attributes: { key: 'value' },
161-
severityNumber: undefined,
162-
},
163-
expect.any(Object),
164-
undefined,
165-
);
166-
});
167152
});
168153

169154
describe('Automatic flushing', () => {

packages/browser/test/tracing/browserTracingIntegration.test.ts

+3
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,7 @@ describe('browserTracingIntegration', () => {
728728
sampled: true,
729729
sampleRand: expect.any(Number),
730730
dsc: {
731+
release: undefined,
731732
environment: 'production',
732733
public_key: 'examplePublicKey',
733734
sample_rate: '1',
@@ -768,6 +769,7 @@ describe('browserTracingIntegration', () => {
768769
sampled: false,
769770
sampleRand: expect.any(Number),
770771
dsc: {
772+
release: undefined,
771773
environment: 'production',
772774
public_key: 'examplePublicKey',
773775
sample_rate: '0',
@@ -892,6 +894,7 @@ describe('browserTracingIntegration', () => {
892894

893895
expect(dynamicSamplingContext).toBeDefined();
894896
expect(dynamicSamplingContext).toStrictEqual({
897+
release: undefined,
895898
environment: 'production',
896899
public_key: 'examplePublicKey',
897900
sample_rate: '1',

packages/cloudflare/src/transport.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/core';
2-
import { SentryError, createTransport, suppressTracing } from '@sentry/core';
2+
import { SENTRY_BUFFER_FULL_ERROR, createTransport, suppressTracing } from '@sentry/core';
33

44
export interface CloudflareTransportOptions extends BaseTransportOptions {
55
/** Fetch API init parameters. */
@@ -38,7 +38,7 @@ export class IsolatedPromiseBuffer {
3838
*/
3939
public add(taskProducer: () => PromiseLike<TransportMakeRequestResponse>): PromiseLike<TransportMakeRequestResponse> {
4040
if (this._taskProducers.length >= this._bufferSize) {
41-
return Promise.reject(new SentryError('Not adding Promise because buffer limit was reached.'));
41+
return Promise.reject(SENTRY_BUFFER_FULL_ERROR);
4242
}
4343

4444
this._taskProducers.push(taskProducer);

packages/cloudflare/test/transport.test.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createEnvelope, serializeEnvelope } from '@sentry/core';
1+
import { SENTRY_BUFFER_FULL_ERROR, createEnvelope, serializeEnvelope } from '@sentry/core';
22
import type { EventEnvelope, EventItem } from '@sentry/core';
33
import { afterAll, describe, expect, it, vi } from 'vitest';
44

@@ -140,7 +140,12 @@ describe('IsolatedPromiseBuffer', () => {
140140
await ipb.add(task2);
141141
await ipb.add(task3);
142142

143-
await expect(ipb.add(task4)).rejects.toThrowError('Not adding Promise because buffer limit was reached.');
143+
try {
144+
await ipb.add(task4);
145+
throw new Error('Should not be called');
146+
} catch (error) {
147+
expect(error).toBe(SENTRY_BUFFER_FULL_ERROR);
148+
}
144149
});
145150

146151
it('should not throw when one of the tasks throws when drained', async () => {

packages/core/src/client.ts

+46-13
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ import {
5353
import { createClientReportEnvelope } from './utils-hoist/clientreport';
5454
import { dsnToString, makeDsn } from './utils-hoist/dsn';
5555
import { addItemToEnvelope, createAttachmentEnvelopeItem } from './utils-hoist/envelope';
56-
import { SentryError } from './utils-hoist/error';
5756
import { isParameterizedString, isPlainObject, isPrimitive, isThenable } from './utils-hoist/is';
5857
import { logger } from './utils-hoist/logger';
5958
import { checkOrSetAlreadyCaught, uuid4 } from './utils-hoist/misc';
@@ -69,6 +68,41 @@ import { _getSpanForScope } from './utils/spanOnScope';
6968
const ALREADY_SEEN_ERROR = "Not capturing exception because it's already been captured.";
7069
const MISSING_RELEASE_FOR_SESSION_ERROR = 'Discarded session because of missing or non-string release';
7170

71+
const INTERNAL_ERROR_SYMBOL = Symbol.for('SentryInternalError');
72+
const DO_NOT_SEND_EVENT_SYMBOL = Symbol.for('SentryDoNotSendEventError');
73+
74+
interface InternalError {
75+
message: string;
76+
[INTERNAL_ERROR_SYMBOL]: true;
77+
}
78+
79+
interface DoNotSendEventError {
80+
message: string;
81+
[DO_NOT_SEND_EVENT_SYMBOL]: true;
82+
}
83+
84+
function _makeInternalError(message: string): InternalError {
85+
return {
86+
message,
87+
[INTERNAL_ERROR_SYMBOL]: true,
88+
};
89+
}
90+
91+
function _makeDoNotSendEventError(message: string): DoNotSendEventError {
92+
return {
93+
message,
94+
[DO_NOT_SEND_EVENT_SYMBOL]: true,
95+
};
96+
}
97+
98+
function _isInternalError(error: unknown): error is InternalError {
99+
return !!error && typeof error === 'object' && INTERNAL_ERROR_SYMBOL in error;
100+
}
101+
102+
function _isDoNotSendEventError(error: unknown): error is DoNotSendEventError {
103+
return !!error && typeof error === 'object' && DO_NOT_SEND_EVENT_SYMBOL in error;
104+
}
105+
72106
/**
73107
* Base implementation for all JavaScript SDK clients.
74108
*
@@ -975,10 +1009,10 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
9751009
},
9761010
reason => {
9771011
if (DEBUG_BUILD) {
978-
// If something's gone wrong, log the error as a warning. If it's just us having used a `SentryError` for
979-
// control flow, log just the message (no stack) as a log-level log.
980-
if (reason instanceof SentryError && reason.logLevel === 'log') {
1012+
if (_isDoNotSendEventError(reason)) {
9811013
logger.log(reason.message);
1014+
} else if (_isInternalError(reason)) {
1015+
logger.warn(reason.message);
9821016
} else {
9831017
logger.warn(reason);
9841018
}
@@ -1022,9 +1056,8 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
10221056
if (isError && typeof parsedSampleRate === 'number' && Math.random() > parsedSampleRate) {
10231057
this.recordDroppedEvent('sample_rate', 'error');
10241058
return rejectedSyncPromise(
1025-
new SentryError(
1059+
_makeDoNotSendEventError(
10261060
`Discarding event because it's not included in the random sample (sampling rate = ${sampleRate})`,
1027-
'log',
10281061
),
10291062
);
10301063
}
@@ -1035,7 +1068,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
10351068
.then(prepared => {
10361069
if (prepared === null) {
10371070
this.recordDroppedEvent('event_processor', dataCategory);
1038-
throw new SentryError('An event processor returned `null`, will not send event.', 'log');
1071+
throw _makeDoNotSendEventError('An event processor returned `null`, will not send event.');
10391072
}
10401073

10411074
const isInternalException = hint.data && (hint.data as { __sentry__: boolean }).__sentry__ === true;
@@ -1055,7 +1088,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
10551088
const spanCount = 1 + spans.length;
10561089
this.recordDroppedEvent('before_send', 'span', spanCount);
10571090
}
1058-
throw new SentryError(`${beforeSendLabel} returned \`null\`, will not send event.`, 'log');
1091+
throw _makeDoNotSendEventError(`${beforeSendLabel} returned \`null\`, will not send event.`);
10591092
}
10601093

10611094
const session = currentScope.getSession() || isolationScope.getSession();
@@ -1089,7 +1122,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
10891122
return processedEvent;
10901123
})
10911124
.then(null, reason => {
1092-
if (reason instanceof SentryError) {
1125+
if (_isDoNotSendEventError(reason) || _isInternalError(reason)) {
10931126
throw reason;
10941127
}
10951128

@@ -1099,7 +1132,7 @@ export abstract class Client<O extends ClientOptions = ClientOptions> {
10991132
},
11001133
originalException: reason,
11011134
});
1102-
throw new SentryError(
1135+
throw _makeInternalError(
11031136
`Event processing pipeline threw an error, original event will not be sent. Details have been sent as a new event.\nReason: ${reason}`,
11041137
);
11051138
});
@@ -1205,16 +1238,16 @@ function _validateBeforeSendResult(
12051238
return beforeSendResult.then(
12061239
event => {
12071240
if (!isPlainObject(event) && event !== null) {
1208-
throw new SentryError(invalidValueError);
1241+
throw _makeInternalError(invalidValueError);
12091242
}
12101243
return event;
12111244
},
12121245
e => {
1213-
throw new SentryError(`${beforeSendLabel} rejected with ${e}`);
1246+
throw _makeInternalError(`${beforeSendLabel} rejected with ${e}`);
12141247
},
12151248
);
12161249
} else if (!isPlainObject(beforeSendResult) && beforeSendResult !== null) {
1217-
throw new SentryError(invalidValueError);
1250+
throw _makeInternalError(invalidValueError);
12181251
}
12191252
return beforeSendResult;
12201253
}

packages/core/src/integrations/eventFilters.ts

-17
Original file line numberDiff line numberDiff line change
@@ -102,19 +102,12 @@ function _mergeOptions(
102102
...(internalOptions.disableErrorDefaults ? [] : DEFAULT_IGNORE_ERRORS),
103103
],
104104
ignoreTransactions: [...(internalOptions.ignoreTransactions || []), ...(clientOptions.ignoreTransactions || [])],
105-
ignoreInternal: internalOptions.ignoreInternal !== undefined ? internalOptions.ignoreInternal : true,
106105
};
107106
}
108107

109108
function _shouldDropEvent(event: Event, options: Partial<EventFiltersOptions>): boolean {
110109
if (!event.type) {
111110
// Filter errors
112-
113-
if (options.ignoreInternal && _isSentryError(event)) {
114-
DEBUG_BUILD &&
115-
logger.warn(`Event dropped due to being internal Sentry Error.\nEvent: ${getEventDescription(event)}`);
116-
return true;
117-
}
118111
if (_isIgnoredError(event, options.ignoreErrors)) {
119112
DEBUG_BUILD &&
120113
logger.warn(
@@ -196,16 +189,6 @@ function _isAllowedUrl(event: Event, allowUrls?: Array<string | RegExp>): boolea
196189
return !url ? true : stringMatchesSomePattern(url, allowUrls);
197190
}
198191

199-
function _isSentryError(event: Event): boolean {
200-
try {
201-
// @ts-expect-error can't be a sentry error if undefined
202-
return event.exception.values[0].type === 'SentryError';
203-
} catch (e) {
204-
// ignore
205-
}
206-
return false;
207-
}
208-
209192
function _getLastValidUrl(frames: StackFrame[] = []): string | null {
210193
for (let i = frames.length - 1; i >= 0; i--) {
211194
const frame = frames[i];

packages/core/src/tracing/dynamicSamplingContext.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
baggageHeaderToDynamicSamplingContext,
99
dynamicSamplingContextToSentryBaggageHeader,
1010
} from '../utils-hoist/baggage';
11-
import { addNonEnumerableProperty, dropUndefinedKeys } from '../utils-hoist/object';
11+
import { addNonEnumerableProperty } from '../utils-hoist/object';
1212
import { hasSpansEnabled } from '../utils/hasSpansEnabled';
1313
import { getRootSpan, spanIsSampled, spanToJSON } from '../utils/spanUtils';
1414
import { getCapturedScopesOnSpan } from './utils';
@@ -41,12 +41,14 @@ export function getDynamicSamplingContextFromClient(trace_id: string, client: Cl
4141

4242
const { publicKey: public_key } = client.getDsn() || {};
4343

44-
const dsc = dropUndefinedKeys({
44+
// Instead of conditionally adding non-undefined values, we add them and then remove them if needed
45+
// otherwise, the order of baggage entries changes, which "breaks" a bunch of tests etc.
46+
const dsc: DynamicSamplingContext = {
4547
environment: options.environment || DEFAULT_ENVIRONMENT,
4648
release: options.release,
4749
public_key,
4850
trace_id,
49-
}) satisfies DynamicSamplingContext;
51+
};
5052

5153
client.emit('createDsc', dsc);
5254

packages/core/src/transports/base.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ import {
1414
forEachEnvelopeItem,
1515
serializeEnvelope,
1616
} from '../utils-hoist/envelope';
17-
import { SentryError } from '../utils-hoist/error';
1817
import { logger } from '../utils-hoist/logger';
19-
import { type PromiseBuffer, makePromiseBuffer } from '../utils-hoist/promisebuffer';
18+
import { type PromiseBuffer, makePromiseBuffer, SENTRY_BUFFER_FULL_ERROR } from '../utils-hoist/promisebuffer';
2019
import { type RateLimits, isRateLimited, updateRateLimits } from '../utils-hoist/ratelimit';
2120
import { resolvedSyncPromise } from '../utils-hoist/syncpromise';
2221

@@ -85,7 +84,7 @@ export function createTransport(
8584
return buffer.add(requestTask).then(
8685
result => result,
8786
error => {
88-
if (error instanceof SentryError) {
87+
if (error === SENTRY_BUFFER_FULL_ERROR) {
8988
DEBUG_BUILD && logger.error('Skipped sending event because buffer is full.');
9089
recordEnvelopeLoss('queue_overflow');
9190
return resolvedSyncPromise({});

packages/core/src/types-hoist/log.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ParameterizedString } from './parameterize';
22

3-
export type LogSeverityLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal' | 'critical';
3+
export type LogSeverityLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
44

55
export type SerializedLogAttributeValueType =
66
| {

0 commit comments

Comments
 (0)