-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(browser): Add browser metrics sdk #9794
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
Changes from 20 commits
5d95eca
f1cd311
33395d6
b48096f
e698dfa
f5d6333
43ca415
9f19dec
979f51a
b05729e
4e4cb42
2c2b715
5ead644
261cd27
682fec3
60e8989
365cd32
ecd54ec
69be4b1
e693382
f6c8bbf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
export const COUNTER_METRIC_TYPE = 'c' as const; | ||
export const GAUGE_METRIC_TYPE = 'g' as const; | ||
export const SET_METRIC_TYPE = 's' as const; | ||
export const DISTRIBUTION_METRIC_TYPE = 'd' as const; | ||
|
||
/** | ||
* Normalization regex for metric names and metric tag names. | ||
* | ||
* This enforces that names and tag keys only contain alphanumeric characters, | ||
* underscores, forward slashes, periods, and dashes. | ||
* | ||
* See: https://develop.sentry.dev/sdk/metrics/#normalization | ||
*/ | ||
export const NAME_AND_TAG_KEY_NORMALIZATION_REGEX = /[^a-zA-Z0-9_/.-]+/g; | ||
|
||
/** | ||
* Normalization regex for metric tag values. | ||
* | ||
* This enforces that values only contain words, digits, or the following | ||
* special characters: _:/@.{}[\]$- | ||
* | ||
* See: https://develop.sentry.dev/sdk/metrics/#normalization | ||
*/ | ||
export const TAG_VALUE_NORMALIZATION_REGEX = /[^\w\d_:/@.{}[\]$-]+/g; | ||
|
||
/** | ||
* This does not match spec in https://develop.sentry.dev/sdk/metrics | ||
* but was chosen to optimize for the most common case in browser environments. | ||
*/ | ||
export const DEFAULT_FLUSH_INTERVAL = 5000; | ||
AbhiPrasad marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import type { DsnComponents, MetricBucketItem, SdkMetadata, StatsdEnvelope, StatsdItem } from '@sentry/types'; | ||
import { createEnvelope, dsnToString } from '@sentry/utils'; | ||
import { serializeMetricBuckets } from './utils'; | ||
|
||
/** | ||
* Create envelope from a metric aggregate. | ||
*/ | ||
export function createMetricEnvelope( | ||
metricBucketItems: Array<MetricBucketItem>, | ||
dsn?: DsnComponents, | ||
metadata?: SdkMetadata, | ||
tunnel?: string, | ||
): StatsdEnvelope { | ||
const headers: StatsdEnvelope[0] = { | ||
sent_at: new Date().toISOString(), | ||
}; | ||
|
||
if (metadata && metadata.sdk) { | ||
headers.sdk = { | ||
name: metadata.sdk.name, | ||
version: metadata.sdk.version, | ||
}; | ||
} | ||
|
||
if (!!tunnel && dsn) { | ||
headers.dsn = dsnToString(dsn); | ||
} | ||
|
||
const item = createMetricEnvelopeItem(metricBucketItems); | ||
return createEnvelope<StatsdEnvelope>(headers, [item]); | ||
} | ||
|
||
function createMetricEnvelopeItem(metricBucketItems: Array<MetricBucketItem>): StatsdItem { | ||
const payload = serializeMetricBuckets(metricBucketItems); | ||
const metricHeaders: StatsdItem[0] = { | ||
type: 'statsd', | ||
length: payload.length, | ||
}; | ||
return [metricHeaders, payload]; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import type { ClientOptions, MeasurementUnit, Primitive } from '@sentry/types'; | ||
import { logger } from '@sentry/utils'; | ||
import type { BaseClient } from '../baseclient'; | ||
import { DEBUG_BUILD } from '../debug-build'; | ||
import { getCurrentHub } from '../hub'; | ||
import { COUNTER_METRIC_TYPE, DISTRIBUTION_METRIC_TYPE, GAUGE_METRIC_TYPE, SET_METRIC_TYPE } from './constants'; | ||
import type { MetricType } from './types'; | ||
|
||
interface MetricData { | ||
unit?: MeasurementUnit; | ||
tags?: Record<string, Primitive>; | ||
timestamp?: number; | ||
} | ||
|
||
function addToMetricsAggregator( | ||
metricType: MetricType, | ||
name: string, | ||
value: number | string, | ||
data: MetricData = {}, | ||
): void { | ||
const hub = getCurrentHub(); | ||
const client = hub.getClient() as BaseClient<ClientOptions>; | ||
const scope = hub.getScope(); | ||
if (client) { | ||
if (!client.metricsAggregator) { | ||
DEBUG_BUILD && | ||
logger.warn('No metrics aggregator enabled. Please add the Metrics integration to use metrics APIs'); | ||
return; | ||
} | ||
const { unit, tags, timestamp } = data; | ||
const { release, environment } = client.getOptions(); | ||
const transaction = scope.getTransaction(); | ||
AbhiPrasad marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const metricTags: Record<string, string> = {}; | ||
if (release) { | ||
metricTags.release = release; | ||
} | ||
if (environment) { | ||
metricTags.environment = environment; | ||
} | ||
if (transaction) { | ||
metricTags.transaction = transaction.name; | ||
} | ||
Comment on lines
+35
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. l/m: Should we think about applying these before we spread the tags so that people can override them? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know if we want users to override them. Let me start a slack thread. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note that this is also somewhat like this for general events 🤔 e.g. // in scope applyToEvent
if (this._level) {
event.level = this._level;
}
if (this._transactionName) {
event.transaction = this._transactionName;
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ... but only for some things, for others (e.g. tags etc.) event data takes precedence. |
||
|
||
DEBUG_BUILD && logger.log(`Adding value of ${value} to ${metricType} metric ${name}`); | ||
client.metricsAggregator.add(metricType, name, value, unit, { ...metricTags, ...tags }, timestamp); | ||
} | ||
} | ||
|
||
/** | ||
* Adds a value to a counter metric | ||
* | ||
* @experimental This API is experimental and might having breaking changes in the future. | ||
*/ | ||
export function increment(name: string, value: number = 1, data?: MetricData): void { | ||
addToMetricsAggregator(COUNTER_METRIC_TYPE, name, value, data); | ||
} | ||
|
||
/** | ||
* Adds a value to a distribution metric | ||
* | ||
* @experimental This API is experimental and might having breaking changes in the future. | ||
*/ | ||
export function distribution(name: string, value: number, data?: MetricData): void { | ||
addToMetricsAggregator(DISTRIBUTION_METRIC_TYPE, name, value, data); | ||
} | ||
|
||
/** | ||
* Adds a value to a set metric. Value must be a string or integer. | ||
* | ||
* @experimental This API is experimental and might having breaking changes in the future. | ||
*/ | ||
export function set(name: string, value: number | string, data?: MetricData): void { | ||
addToMetricsAggregator(SET_METRIC_TYPE, name, value, data); | ||
} | ||
|
||
/** | ||
* Adds a value to a gauge metric | ||
* | ||
* @experimental This API is experimental and might having breaking changes in the future. | ||
*/ | ||
export function gauge(name: string, value: number, data?: MetricData): void { | ||
addToMetricsAggregator(GAUGE_METRIC_TYPE, name, value, data); | ||
} | ||
|
||
export const metrics = { | ||
increment, | ||
distribution, | ||
set, | ||
gauge, | ||
}; |
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.