Skip to content

feat(metrics): Remove metrics method from BaseClient #10789

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

Merged
merged 5 commits into from
Feb 22, 2024
Merged
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
8 changes: 4 additions & 4 deletions docs/event-sending.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ This document gives an outline for how event sending works, and which which plac
- `createEnvelope()`
- `addItemToEnvelope()`
- `createAttachmentEnvelopeItem()`
- `baseclient._sendEnvelope()`
- `baseclient.sendEnvelope()`
- `transport.send()`

## Transactions
Expand Down Expand Up @@ -54,7 +54,7 @@ This document gives an outline for how event sending works, and which which plac
- `createEnvelope()`
- `addItemToEnvelope()`
- `createAttachmentEnvelopeItem()`
- `baseclient._sendEnvelope()`
- `baseclient.sendEnvelope()`
- `transport.send()`

## Sessions
Expand All @@ -70,7 +70,7 @@ This document gives an outline for how event sending works, and which which plac
- `createSessionEnvelope()`
- `getSdkMetadataForEnvelopeHeader()`
- `createEnvelope()`
- `baseclient._sendEnvelope()`
- `baseclient.sendEnvelope()`
- `transport.send()`
- `updateSession()`

Expand All @@ -94,5 +94,5 @@ This document gives an outline for how event sending works, and which which plac
- `browser.client._flushOutcomes()`
- `getEnvelopeEndpointWithUrlEncodedAuth()`
- `createClientReportEnvelope()`
- `baseclient._sendEnvelope()`
- `baseclient.sendEnvelope()`
- `transport.send()`
8 changes: 4 additions & 4 deletions packages/browser/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ export class BrowserClient extends BaseClient<BrowserClientOptions> {
tunnel: this.getOptions().tunnel,
});

// _sendEnvelope should not throw
// sendEnvelope should not throw
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this._sendEnvelope(envelope);
this.sendEnvelope(envelope);
}

/**
Expand Down Expand Up @@ -130,8 +130,8 @@ export class BrowserClient extends BaseClient<BrowserClientOptions> {

const envelope = createClientReportEnvelope(outcomes, this._options.tunnel && dsnToString(this._dsn));

// _sendEnvelope should not throw
// sendEnvelope should not throw
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this._sendEnvelope(envelope);
this.sendEnvelope(envelope);
}
}
55 changes: 18 additions & 37 deletions packages/core/src/baseclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import type {
FeedbackEvent,
Integration,
IntegrationClass,
MetricBucketItem,
Outcome,
ParameterizedString,
SdkMetadata,
Expand Down Expand Up @@ -52,7 +51,6 @@ import { createEventEnvelope, createSessionEnvelope } from './envelope';
import type { IntegrationIndex } from './integration';
import { afterSetupIntegrations } from './integration';
import { setupIntegration, setupIntegrations } from './integration';
import { createMetricEnvelope } from './metrics/envelope';
import type { Scope } from './scope';
import { updateSession } from './session';
import { getDynamicSamplingContextFromClient } from './tracing/dynamicSamplingContext';
Expand Down Expand Up @@ -377,7 +375,7 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
env = addItemToEnvelope(env, createAttachmentEnvelopeItem(attachment));
}

const promise = this._sendEnvelope(env);
const promise = this.sendEnvelope(env);
if (promise) {
promise.then(sendResponse => this.emit('afterSendEvent', event, sendResponse), null);
}
Expand All @@ -389,9 +387,9 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
public sendSession(session: Session | SessionAggregates): void {
const env = createSessionEnvelope(session, this._dsn, this._options._metadata, this._options.tunnel);

// _sendEnvelope should not throw
// sendEnvelope should not throw
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this._sendEnvelope(env);
this.sendEnvelope(env);
}

/**
Expand All @@ -415,23 +413,6 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
}
}

/**
* @inheritDoc
*/
public captureAggregateMetrics(metricBucketItems: Array<MetricBucketItem>): void {
DEBUG_BUILD && logger.log(`Flushing aggregated metrics, number of metrics: ${metricBucketItems.length}`);
const metricsEnvelope = createMetricEnvelope(
metricBucketItems,
this._dsn,
this._options._metadata,
this._options.tunnel,
);

// _sendEnvelope should not throw
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this._sendEnvelope(metricsEnvelope);
}

// Keep on() & emit() signatures in sync with types' client.ts interface
/* eslint-disable @typescript-eslint/unified-signatures */

Expand Down Expand Up @@ -540,6 +521,21 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
}
}

/**
* @inheritdoc
*/
public sendEnvelope(envelope: Envelope): PromiseLike<void | TransportMakeRequestResponse> | void {
this.emit('beforeEnvelope', envelope);

if (this._isEnabled() && this._transport) {
return this._transport.send(envelope).then(null, reason => {
DEBUG_BUILD && logger.error('Error while sending event:', reason);
});
} else {
DEBUG_BUILD && logger.error('Transport disabled');
}
}

/* eslint-enable @typescript-eslint/unified-signatures */

/** Setup integrations for this client. */
Expand Down Expand Up @@ -823,21 +819,6 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
);
}

/**
* @inheritdoc
*/
protected _sendEnvelope(envelope: Envelope): PromiseLike<void | TransportMakeRequestResponse> | void {
this.emit('beforeEnvelope', envelope);

if (this._isEnabled() && this._transport) {
return this._transport.send(envelope).then(null, reason => {
DEBUG_BUILD && logger.error('Error while sending event:', reason);
});
} else {
DEBUG_BUILD && logger.error('Transport disabled');
}
}

/**
* Clears outcomes on this client and returns them.
*/
Expand Down
7 changes: 4 additions & 3 deletions packages/core/src/metrics/aggregator.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type {
Client,
ClientOptions,
MeasurementUnit,
MetricsAggregator as MetricsAggregatorBase,
Primitive,
} from '@sentry/types';
import { timestampInSeconds } from '@sentry/utils';
import type { BaseClient } from '../baseclient';
import { DEFAULT_FLUSH_INTERVAL, MAX_WEIGHT, NAME_AND_TAG_KEY_NORMALIZATION_REGEX, SET_METRIC_TYPE } from './constants';
import { captureAggregateMetrics } from './envelope';
import { METRIC_MAP } from './instance';
import { updateMetricSummaryOnActiveSpan } from './metric-summary';
import type { MetricBucket, MetricType } from './types';
Expand Down Expand Up @@ -39,7 +40,7 @@ export class MetricsAggregator implements MetricsAggregatorBase {
// Force flush is used on either shutdown, flush() or when we exceed the max weight.
private _forceFlush: boolean;

public constructor(private readonly _client: Client<ClientOptions>) {
public constructor(private readonly _client: BaseClient<ClientOptions>) {
this._buckets = new Map();
this._bucketsTotalWeight = 0;
this._interval = setInterval(() => this._flush(), DEFAULT_FLUSH_INTERVAL);
Expand Down Expand Up @@ -166,7 +167,7 @@ export class MetricsAggregator implements MetricsAggregatorBase {
// TODO(@anonrig): Optimization opportunity.
// This copy operation can be avoided if we store the key in the bucketItem.
const buckets = Array.from(flushedBuckets).map(([, bucketItem]) => bucketItem);
this._client.captureAggregateMetrics(buckets);
captureAggregateMetrics(this._client, buckets);
}
}
}
8 changes: 5 additions & 3 deletions packages/core/src/metrics/browser-aggregator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { Client, ClientOptions, MeasurementUnit, MetricsAggregator, Primitive } from '@sentry/types';
import type { ClientOptions, MeasurementUnit, MetricsAggregator, Primitive } from '@sentry/types';
import { timestampInSeconds } from '@sentry/utils';
import type { BaseClient } from '../baseclient';
import { DEFAULT_BROWSER_FLUSH_INTERVAL, NAME_AND_TAG_KEY_NORMALIZATION_REGEX, SET_METRIC_TYPE } from './constants';
import { captureAggregateMetrics } from './envelope';
import { METRIC_MAP } from './instance';
import { updateMetricSummaryOnActiveSpan } from './metric-summary';
import type { MetricBucket, MetricType } from './types';
Expand All @@ -19,7 +21,7 @@ export class BrowserMetricsAggregator implements MetricsAggregator {
private _buckets: MetricBucket;
private readonly _interval: ReturnType<typeof setInterval>;

public constructor(private readonly _client: Client<ClientOptions>) {
public constructor(private readonly _client: BaseClient<ClientOptions>) {
this._buckets = new Map();
this._interval = setInterval(() => this.flush(), DEFAULT_BROWSER_FLUSH_INTERVAL);
}
Expand Down Expand Up @@ -80,7 +82,7 @@ export class BrowserMetricsAggregator implements MetricsAggregator {

// TODO(@anonrig): Use Object.values() when we support ES6+
const metricBuckets = Array.from(this._buckets).map(([, bucketItem]) => bucketItem);
this._client.captureAggregateMetrics(metricBuckets);
captureAggregateMetrics(this._client, metricBuckets);

this._buckets.clear();
}
Expand Down
31 changes: 29 additions & 2 deletions packages/core/src/metrics/envelope.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,34 @@
import type { DsnComponents, MetricBucketItem, SdkMetadata, StatsdEnvelope, StatsdItem } from '@sentry/types';
import { createEnvelope, dsnToString } from '@sentry/utils';
import type {
ClientOptions,
DsnComponents,
MetricBucketItem,
SdkMetadata,
StatsdEnvelope,
StatsdItem,
} from '@sentry/types';
import { createEnvelope, dsnToString, logger } from '@sentry/utils';
import type { BaseClient } from '../baseclient';
import { serializeMetricBuckets } from './utils';

/**
* Captures aggregated metrics to the supplied client.
*/
export function captureAggregateMetrics(
client: BaseClient<ClientOptions>,
metricBucketItems: Array<MetricBucketItem>,
): void {
logger.log(`Flushing aggregated metrics, number of metrics: ${metricBucketItems.length}`);
const dsn = client.getDsn();
const metadata = client.getSdkMetadata();
const tunnel = client.getOptions().tunnel;

const metricsEnvelope = createMetricEnvelope(metricBucketItems, dsn, metadata, tunnel);

// sendEnvelope should not throw
// eslint-disable-next-line @typescript-eslint/no-floating-promises
client.sendEnvelope(metricsEnvelope);
}

/**
* Create envelope from a metric aggregate.
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/server-runtime-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,9 @@ export class ServerRuntimeClient<

DEBUG_BUILD && logger.info('Sending checkin:', checkIn.monitorSlug, checkIn.status);

// _sendEnvelope should not throw
// sendEnvelope should not throw
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this._sendEnvelope(envelope);
this.sendEnvelope(envelope);

return id;
}
Expand Down
29 changes: 5 additions & 24 deletions packages/core/test/lib/metrics/aggregator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,45 +81,26 @@ describe('MetricsAggregator', () => {

describe('close', () => {
test('should flush immediately', () => {
const capture = jest.spyOn(testClient, 'captureAggregateMetrics');
const capture = jest.spyOn(testClient, 'sendEnvelope');
const aggregator = new MetricsAggregator(testClient);
aggregator.add('c', 'requests', 1);
aggregator.close();
// It should clear the interval.
expect(clearInterval).toHaveBeenCalled();
expect(capture).toBeCalled();
expect(capture).toBeCalledTimes(1);
expect(capture).toBeCalledWith([
{
metric: { _value: 1 },
metricType: 'c',
name: 'requests',
tags: {},
timestamp: expect.any(Number),
unit: 'none',
},
]);
});
});

describe('flush', () => {
test('should flush immediately', () => {
const capture = jest.spyOn(testClient, 'captureAggregateMetrics');
const capture = jest.spyOn(testClient, 'sendEnvelope');
const aggregator = new MetricsAggregator(testClient);
aggregator.add('c', 'requests', 1);
aggregator.flush();
expect(capture).toBeCalled();
expect(capture).toBeCalledTimes(1);
expect(capture).toBeCalledWith([
{
metric: { _value: 1 },
metricType: 'c',
name: 'requests',
tags: {},
timestamp: expect.any(Number),
unit: 'none',
},
]);

capture.mockReset();
aggregator.close();
// It should clear the interval.
Expand All @@ -130,7 +111,7 @@ describe('MetricsAggregator', () => {
});

test('should not capture if empty', () => {
const capture = jest.spyOn(testClient, 'captureAggregateMetrics');
const capture = jest.spyOn(testClient, 'sendEnvelope');
const aggregator = new MetricsAggregator(testClient);
aggregator.add('c', 'requests', 1);
aggregator.flush();
Expand All @@ -143,7 +124,7 @@ describe('MetricsAggregator', () => {

describe('add', () => {
test('it should respect the max weight and flush if exceeded', () => {
const capture = jest.spyOn(testClient, 'captureAggregateMetrics');
const capture = jest.spyOn(testClient, 'sendEnvelope');
const aggregator = new MetricsAggregator(testClient);

for (let i = 0; i < MAX_WEIGHT; i++) {
Expand Down
6 changes: 2 additions & 4 deletions packages/core/test/lib/serverruntimeclient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,7 @@ describe('ServerRuntimeClient', () => {
});
client = new ServerRuntimeClient(options);

// @ts-expect-error accessing private method
const sendEnvelopeSpy = jest.spyOn(client, '_sendEnvelope');
const sendEnvelopeSpy = jest.spyOn(client, 'sendEnvelope');

const id = client.captureCheckIn(
{ monitorSlug: 'foo', status: 'in_progress' },
Expand Down Expand Up @@ -145,8 +144,7 @@ describe('ServerRuntimeClient', () => {
const options = getDefaultClientOptions({ dsn: PUBLIC_DSN, serverName: 'bar', enabled: false });
client = new ServerRuntimeClient(options);

// @ts-expect-error accessing private method
const sendEnvelopeSpy = jest.spyOn(client, '_sendEnvelope');
const sendEnvelopeSpy = jest.spyOn(client, 'sendEnvelope');

client.captureCheckIn({ monitorSlug: 'foo', status: 'in_progress' });

Expand Down
9 changes: 3 additions & 6 deletions packages/node-experimental/test/sdk/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,7 @@ describe('NodeClient', () => {
});
const client = new NodeClient(options);

// @ts-expect-error accessing private method
const sendEnvelopeSpy = jest.spyOn(client, '_sendEnvelope');
const sendEnvelopeSpy = jest.spyOn(client, 'sendEnvelope');

const id = client.captureCheckIn(
{ monitorSlug: 'foo', status: 'in_progress' },
Expand Down Expand Up @@ -464,8 +463,7 @@ describe('NodeClient', () => {
});
const client = new NodeClient(options);

// @ts-expect-error accessing private method
const sendEnvelopeSpy = jest.spyOn(client, '_sendEnvelope');
const sendEnvelopeSpy = jest.spyOn(client, 'sendEnvelope');

const id = client.captureCheckIn({ monitorSlug: 'heartbeat-monitor', status: 'ok' });

Expand All @@ -491,8 +489,7 @@ describe('NodeClient', () => {
const options = getDefaultNodeClientOptions({ serverName: 'bar', enabled: false });
const client = new NodeClient(options);

// @ts-expect-error accessing private method
const sendEnvelopeSpy = jest.spyOn(client, '_sendEnvelope');
const sendEnvelopeSpy = jest.spyOn(client, 'sendEnvelope');

client.captureCheckIn({ monitorSlug: 'foo', status: 'in_progress' });

Expand Down
Loading