Skip to content

Commit

Permalink
feat(transport): Add client report hook to makeTransport (#5008)
Browse files Browse the repository at this point in the history
  • Loading branch information
lforst authored Apr 28, 2022
1 parent ebc938a commit de2822b
Show file tree
Hide file tree
Showing 32 changed files with 348 additions and 272 deletions.
2 changes: 1 addition & 1 deletion packages/browser/src/transports/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ export function makeFetchTransport(
}));
}

return createTransport({ bufferSize: options.bufferSize }, makeRequest);
return createTransport(options, makeRequest);
}
11 changes: 7 additions & 4 deletions packages/browser/src/transports/xhr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,20 @@ export interface XHRTransportOptions extends BaseTransportOptions {
*/
export function makeXHRTransport(options: XHRTransportOptions): Transport {
function makeRequest(request: TransportRequest): PromiseLike<TransportMakeRequestResponse> {
return new SyncPromise<TransportMakeRequestResponse>((resolve, _reject) => {
return new SyncPromise((resolve, reject) => {
const xhr = new XMLHttpRequest();

xhr.onerror = reject;

xhr.onreadystatechange = (): void => {
if (xhr.readyState === XHR_READYSTATE_DONE) {
resolve({
const response = {
headers: {
'x-sentry-rate-limits': xhr.getResponseHeader('X-Sentry-Rate-Limits'),
'retry-after': xhr.getResponseHeader('Retry-After'),
},
});
};
resolve(response);
}
};

Expand All @@ -47,5 +50,5 @@ export function makeXHRTransport(options: XHRTransportOptions): Transport {
});
}

return createTransport({ bufferSize: options.bufferSize }, makeRequest);
return createTransport(options, makeRequest);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { BrowserClientOptions } from '../../../src/client';
export function getDefaultBrowserClientOptions(options: Partial<BrowserClientOptions> = {}): BrowserClientOptions {
return {
integrations: [],
transport: () => createTransport({}, _ => resolvedSyncPromise({})),
transport: () => createTransport({ recordDroppedEvent: () => undefined }, _ => resolvedSyncPromise({})),
stackParser: () => [],
...options,
};
Expand Down
2 changes: 1 addition & 1 deletion packages/browser/test/unit/mocks/simpletransport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import { createTransport } from '@sentry/core';
import { resolvedSyncPromise } from '@sentry/utils';

export function makeSimpleTransport() {
return createTransport({}, () => resolvedSyncPromise({}));
return createTransport({ recordDroppedEvent: () => undefined }, () => resolvedSyncPromise({}));
}
2 changes: 1 addition & 1 deletion packages/browser/test/unit/sdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const PUBLIC_DSN = 'https://username@domain/123';
function getDefaultBrowserOptions(options: Partial<BrowserOptions> = {}): BrowserOptions {
return {
integrations: [],
transport: () => createTransport({}, _ => resolvedSyncPromise({})),
transport: () => createTransport({ recordDroppedEvent: () => undefined }, _ => resolvedSyncPromise({})),
stackParser: () => [],
...options,
};
Expand Down
1 change: 1 addition & 0 deletions packages/browser/test/unit/transports/fetch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { FetchImpl } from '../../../src/transports/utils';

const DEFAULT_FETCH_TRANSPORT_OPTIONS: FetchTransportOptions = {
url: 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7',
recordDroppedEvent: () => undefined,
};

const ERROR_ENVELOPE = createEnvelope<EventEnvelope>({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [
Expand Down
1 change: 1 addition & 0 deletions packages/browser/test/unit/transports/xhr.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { makeXHRTransport, XHRTransportOptions } from '../../../src/transports/x

const DEFAULT_XHR_TRANSPORT_OPTIONS: XHRTransportOptions = {
url: 'https://sentry.io/api/42/store/?sentry_key=123&sentry_version=7',
recordDroppedEvent: () => undefined,
};

const ERROR_ENVELOPE = createEnvelope<EventEnvelope>({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, [
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/baseclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
if (options.dsn) {
this._dsn = makeDsn(options.dsn);
const url = getEnvelopeEndpointWithUrlEncodedAuth(this._dsn, options.tunnel);
this._transport = options.transport({ ...options.transportOptions, url });
this._transport = options.transport({
recordDroppedEvent: () => undefined, // TODO(v7): Provide a proper function instead of noop
...options.transportOptions,
url,
});
} else {
IS_DEBUG_BUILD && logger.warn('No DSN provided, client will not do anything.');
}
Expand Down
67 changes: 55 additions & 12 deletions packages/core/src/transports/base.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
import {
DataCategory,
Envelope,
EnvelopeItem,
EventDropReason,
InternalBaseTransportOptions,
Transport,
TransportRequestExecutor,
} from '@sentry/types';
import {
getEnvelopeType,
createEnvelope,
envelopeItemTypeToDataCategory,
forEachEnvelopeItem,
isRateLimited,
logger,
makePromiseBuffer,
PromiseBuffer,
RateLimits,
resolvedSyncPromise,
SentryError,
serializeEnvelope,
updateRateLimits,
} from '@sentry/utils';

import { IS_DEBUG_BUILD } from '../flags';

export const DEFAULT_TRANSPORT_BUFFER_SIZE = 30;

/**
Expand All @@ -34,22 +41,58 @@ export function createTransport(
const flush = (timeout?: number): PromiseLike<boolean> => buffer.drain(timeout);

function send(envelope: Envelope): PromiseLike<void> {
const envCategory = getEnvelopeType(envelope);
const category = envCategory === 'event' ? 'error' : (envCategory as DataCategory);
const filteredEnvelopeItems: EnvelopeItem[] = [];

// Drop rate limited items from envelope
forEachEnvelopeItem(envelope, (item, type) => {
const envelopeItemDataCategory = envelopeItemTypeToDataCategory(type);
if (isRateLimited(rateLimits, envelopeItemDataCategory)) {
options.recordDroppedEvent('ratelimit_backoff', envelopeItemDataCategory);
} else {
filteredEnvelopeItems.push(item);
}
});

// Don't add to buffer if transport is already rate-limited
if (isRateLimited(rateLimits, category)) {
// Skip sending if envelope is empty after filtering out rate limited events
if (filteredEnvelopeItems.length === 0) {
return resolvedSyncPromise();
}

const requestTask = (): PromiseLike<void> =>
makeRequest({ body: serializeEnvelope(envelope) }).then(({ headers }): void => {
if (headers) {
rateLimits = updateRateLimits(rateLimits, headers);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const filteredEnvelope: Envelope = createEnvelope(envelope[0], filteredEnvelopeItems as any);

// Creates client report for each item in an envelope
const recordEnvelopeLoss = (reason: EventDropReason): void => {
forEachEnvelopeItem(filteredEnvelope, (_, type) => {
options.recordDroppedEvent(reason, envelopeItemTypeToDataCategory(type));
});
};

const requestTask = (): PromiseLike<void> =>
makeRequest({ body: serializeEnvelope(filteredEnvelope) }).then(
({ headers }): void => {
if (headers) {
rateLimits = updateRateLimits(rateLimits, headers);
}
},
error => {
IS_DEBUG_BUILD && logger.error('Failed while recording event:', error);
recordEnvelopeLoss('network_error');
},
);

return buffer.add(requestTask);
return buffer.add(requestTask).then(
result => result,
error => {
if (error instanceof SentryError) {
IS_DEBUG_BUILD && logger.error('Skipped sending event due to full buffer');
recordEnvelopeLoss('queue_overflow');
return resolvedSyncPromise();
} else {
throw error;
}
},
);
}

return {
Expand Down
Loading

0 comments on commit de2822b

Please sign in to comment.