Skip to content

Commit

Permalink
Introduce event categories for every outcome
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilogorek committed Sep 7, 2021
1 parent 32a3a57 commit e5ce2b0
Show file tree
Hide file tree
Showing 9 changed files with 59 additions and 23 deletions.
19 changes: 13 additions & 6 deletions packages/browser/src/transports/base.ts
Expand Up @@ -35,7 +35,7 @@ export abstract class BaseTransport implements Transport {
/** Locks transport after receiving rate limits in a response */
protected readonly _rateLimits: Record<string, Date> = {};

protected _outcomes: { [key in Outcome]?: number } = {};
protected _outcomes: { [key: string]: number } = {};

public constructor(public options: TransportOptions) {
this._api = new API(options.dsn, options._metadata, options.tunnel);
Expand Down Expand Up @@ -68,13 +68,13 @@ export abstract class BaseTransport implements Transport {
/**
* @inheritDoc
*/
public recordLostEvent(type: Outcome): void {
public recordLostEvent(type: Outcome, category: SentryRequestType): void {
if (!this.options.sendClientReport) {
return;
}

logger.log(`Adding ${type} outcome`);
this._outcomes[type] = (this._outcomes[type] ?? 0) + 1;
const key = `${type}:${CATEGORY_MAPPING[category]}`;
logger.log(`Adding ${key} outcome`);
this._outcomes[key] = (this._outcomes[key] ?? 0) + 1;
}

/**
Expand Down Expand Up @@ -106,7 +106,14 @@ export abstract class BaseTransport implements Transport {
});
const item = JSON.stringify({
timestamp: Date.now(),
discarded_events: this._outcomes,
discarded_events: Object.keys(outcomes).map(key => {
const [category, reason] = key.split(':');
return {
reason,
category,
quantity: outcomes[key],
};
}),
});
const envelope = `${itemHeaders}\n${item}`;

Expand Down
6 changes: 3 additions & 3 deletions packages/browser/src/transports/fetch.ts
Expand Up @@ -113,7 +113,7 @@ export class FetchTransport extends BaseTransport {
*/
private _sendRequest(sentryRequest: SentryRequest, originalPayload: Event | Session): PromiseLike<Response> {
if (this._isRateLimited(sentryRequest.type)) {
this.recordLostEvent(Outcome.RateLimit);
this.recordLostEvent(Outcome.RateLimit, sentryRequest.type);

return Promise.reject({
event: originalPayload,
Expand Down Expand Up @@ -164,9 +164,9 @@ export class FetchTransport extends BaseTransport {
)
.then(undefined, reason => {
if (reason instanceof SentryError) {
this.recordLostEvent(Outcome.QueueSize);
this.recordLostEvent(Outcome.QueueSize, sentryRequest.type);
} else {
this.recordLostEvent(Outcome.NetworkError);
this.recordLostEvent(Outcome.NetworkError, sentryRequest.type);
}
throw reason;
});
Expand Down
6 changes: 3 additions & 3 deletions packages/browser/src/transports/xhr.ts
Expand Up @@ -26,7 +26,7 @@ export class XHRTransport extends BaseTransport {
*/
private _sendRequest(sentryRequest: SentryRequest, originalPayload: Event | Session): PromiseLike<Response> {
if (this._isRateLimited(sentryRequest.type)) {
this.recordLostEvent(Outcome.RateLimit);
this.recordLostEvent(Outcome.RateLimit, sentryRequest.type);

return Promise.reject({
event: originalPayload,
Expand Down Expand Up @@ -65,9 +65,9 @@ export class XHRTransport extends BaseTransport {
)
.then(undefined, reason => {
if (reason instanceof SentryError) {
this.recordLostEvent(Outcome.QueueSize);
this.recordLostEvent(Outcome.QueueSize, sentryRequest.type);
} else {
this.recordLostEvent(Outcome.NetworkError);
this.recordLostEvent(Outcome.NetworkError, sentryRequest.type);
}
throw reason;
});
Expand Down
19 changes: 13 additions & 6 deletions packages/core/src/baseclient.ts
Expand Up @@ -10,6 +10,7 @@ import {
Outcome,
SessionStatus,
Severity,
Transport,
} from '@sentry/types';
import {
dateTimestampInSeconds,
Expand Down Expand Up @@ -182,13 +183,19 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
return this._options;
}

/**
* @inheritDoc
*/
public getTransport(): Transport {
return this.getTransport();
}

/**
* @inheritDoc
*/
public flush(timeout?: number): PromiseLike<boolean> {
return this._isClientDoneProcessing(timeout).then(clientFinished => {
return this._getBackend()
.getTransport()
return this.getTransport()
.close(timeout)
.then(transportFlushed => clientFinished && transportFlushed);
});
Expand Down Expand Up @@ -499,7 +506,7 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
protected _processEvent(event: Event, hint?: EventHint, scope?: Scope): PromiseLike<Event> {
// eslint-disable-next-line @typescript-eslint/unbound-method
const { beforeSend, sampleRate } = this.getOptions();
const transport = this._getBackend().getTransport();
const transport = this.getTransport();

if (!this._isEnabled()) {
return SyncPromise.reject(new SentryError('SDK not enabled, will not capture event.'));
Expand All @@ -510,7 +517,7 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
// 0.0 === 0% events are sent
// Sampling for transaction happens somewhere else
if (!isTransaction && typeof sampleRate === 'number' && Math.random() > sampleRate) {
transport.recordLostEvent?.(Outcome.SampleRate);
transport.recordLostEvent?.(Outcome.SampleRate, 'event');
return SyncPromise.reject(
new SentryError(
`Discarding event because it's not included in the random sample (sampling rate = ${sampleRate})`,
Expand All @@ -521,7 +528,7 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
return this._prepareEvent(event, scope, hint)
.then(prepared => {
if (prepared === null) {
transport.recordLostEvent?.(Outcome.EventProcessor);
transport.recordLostEvent?.(Outcome.EventProcessor, event.type || 'event');
throw new SentryError('An event processor returned null, will not send event.');
}

Expand All @@ -535,7 +542,7 @@ export abstract class BaseClient<B extends Backend, O extends Options> implement
})
.then(processedEvent => {
if (processedEvent === null) {
transport.recordLostEvent?.(Outcome.BeforeSend);
transport.recordLostEvent?.(Outcome.BeforeSend, event.type || 'event');
throw new SentryError('`beforeSend` returned `null`, will not send event.');
}

Expand Down
16 changes: 13 additions & 3 deletions packages/core/test/lib/base.test.ts
Expand Up @@ -88,6 +88,16 @@ describe('BaseClient', () => {
});
});

describe('getTransport()', () => {
test('returns the transport from backend', () => {
expect.assertions(1);
const options = { dsn: PUBLIC_DSN, transport: FakeTransport };
const client = new TestClient(options);
expect(client.getTransport()).toBeInstanceOf(FakeTransport);
expect(TestBackend.instance!.getTransport()).toBe(client.getTransport());
});
});

describe('getBreadcrumbs() / addBreadcrumb()', () => {
test('adds a breadcrumb', () => {
expect.assertions(1);
Expand Down Expand Up @@ -906,7 +916,7 @@ describe('BaseClient', () => {
});

const delay = 1;
const transportInstance = (client as any)._getBackend().getTransport() as FakeTransport;
const transportInstance = client.getTransport() as FakeTransport;
transportInstance.delay = delay;

client.captureMessage('test');
Expand Down Expand Up @@ -935,7 +945,7 @@ describe('BaseClient', () => {
setTimeout(() => resolve({ message, level }), 150);
}),
);
const transportInstance = (client as any)._getBackend().getTransport() as FakeTransport;
const transportInstance = client.getTransport() as FakeTransport;
transportInstance.delay = delay;

client.captureMessage('test async');
Expand All @@ -959,7 +969,7 @@ describe('BaseClient', () => {
});

const delay = 1;
const transportInstance = (client as any)._getBackend().getTransport() as FakeTransport;
const transportInstance = client.getTransport() as FakeTransport;
transportInstance.delay = delay;

expect(client.captureMessage('test')).toBeTruthy();
Expand Down
2 changes: 1 addition & 1 deletion packages/node/src/client.ts
Expand Up @@ -98,7 +98,7 @@ export class NodeClient extends BaseClient<NodeBackend, NodeOptions> {
if (!release) {
logger.warn('Cannot initialise an instance of SessionFlusher if no release is provided!');
} else {
this._sessionFlusher = new SessionFlusher(this._backend.getTransport(), {
this._sessionFlusher = new SessionFlusher(this.getTransport(), {
release,
environment,
});
Expand Down
7 changes: 7 additions & 0 deletions packages/tracing/src/transaction.ts
Expand Up @@ -2,6 +2,7 @@ import { getCurrentHub, Hub } from '@sentry/hub';
import {
Event,
Measurements,
Outcome,
Transaction as TransactionInterface,
TransactionContext,
TransactionMetadata,
Expand Down Expand Up @@ -102,6 +103,12 @@ export class Transaction extends SpanClass implements TransactionInterface {
if (this.sampled !== true) {
// At this point if `sampled !== true` we want to discard the transaction.
logger.log('[Tracing] Discarding transaction because its trace was not chosen to be sampled.');

this._hub
.getClient()
?.getTransport()
.recordLostEvent?.(Outcome.SampleRate, 'transaction');

return undefined;
}

Expand Down
4 changes: 4 additions & 0 deletions packages/types/src/client.ts
Expand Up @@ -5,6 +5,7 @@ import { Options } from './options';
import { Scope } from './scope';
import { Session } from './session';
import { Severity } from './severity';
import { Transport } from './transport';

/**
* User-Facing Sentry SDK Client.
Expand Down Expand Up @@ -59,6 +60,9 @@ export interface Client<O extends Options = Options> {
/** Returns the current options. */
getOptions(): O;

/** Returns clients transport. */
getTransport(): Transport;

/**
* Flush the event queue and set the client to `enabled = false`. See {@link Client.flush}.
*
Expand Down
3 changes: 2 additions & 1 deletion packages/types/src/transport.ts
@@ -1,5 +1,6 @@
import { DsnLike } from './dsn';
import { Event } from './event';
import { SentryRequestType } from './request';
import { Response } from './response';
import { SdkMetadata } from './sdkmetadata';
import { Session, SessionAggregates } from './session';
Expand Down Expand Up @@ -42,7 +43,7 @@ export interface Transport {
/**
* Increment the counter for the specific client outcome
*/
recordLostEvent?(type: Outcome): void;
recordLostEvent?(type: Outcome, category: SentryRequestType): void;
}

/** JSDoc */
Expand Down

0 comments on commit e5ce2b0

Please sign in to comment.