-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(metrics): initialise MULTIPLEXED_METRIC_ROUTING_KEY for routing #19096
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
base: develop
Are you sure you want to change the base?
Changes from all commits
97bafa8
ab12a06
b77918f
9b635b1
a7632a9
37d54c2
00f559f
c2faff6
8a82bfd
d989887
d9854cb
f845c7b
1c836c5
350f662
d38b5f5
cb71961
222973e
6baf564
33c0d25
23800bf
b861ed2
e95310b
35c776a
58fccc9
cf7811c
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 |
|---|---|---|
| @@ -1,7 +1,10 @@ | ||
| import { getEnvelopeEndpointWithUrlEncodedAuth } from '../api'; | ||
| import type { Envelope, EnvelopeItemType, EventItem } from '../types-hoist/envelope'; | ||
| import { DEBUG_BUILD } from '../debug-build'; | ||
| import type { Envelope, EnvelopeItemType, EventItem, MetricContainerItem } from '../types-hoist/envelope'; | ||
| import type { Event } from '../types-hoist/event'; | ||
| import type { SerializedMetric, SerializedMetricContainer } from '../types-hoist/metric'; | ||
| import type { BaseTransportOptions, Transport, TransportMakeRequestResponse } from '../types-hoist/transport'; | ||
| import { debug } from '../utils/debug-logger'; | ||
| import { dsnFromString } from '../utils/dsn'; | ||
| import { createEnvelope, forEachEnvelopeItem } from '../utils/envelope'; | ||
|
|
||
|
|
@@ -16,6 +19,7 @@ interface MatchParam { | |
| * @param types Defaults to ['event'] | ||
| */ | ||
| getEvent(types?: EnvelopeItemType[]): Event | undefined; | ||
| getMetric(): SerializedMetric | undefined; | ||
| } | ||
|
|
||
| type RouteTo = { dsn: string; release: string }; | ||
|
|
@@ -27,6 +31,8 @@ type Matcher = (param: MatchParam) => (string | RouteTo)[]; | |
| */ | ||
| export const MULTIPLEXED_TRANSPORT_EXTRA_KEY = 'MULTIPLEXED_TRANSPORT_EXTRA_KEY'; | ||
|
|
||
| export const MULTIPLEXED_METRIC_ROUTING_KEY = 'sentry.routing'; | ||
|
|
||
| /** | ||
| * Gets an event from an envelope. | ||
| * | ||
|
|
@@ -47,7 +53,79 @@ export function eventFromEnvelope(env: Envelope, types: EnvelopeItemType[]): Eve | |
| } | ||
|
|
||
| /** | ||
| * Creates a transport that overrides the release on all events. | ||
| * It iterates over metric containers in an envelope. | ||
| */ | ||
| function forEachMetricContainer( | ||
| envelope: Envelope, | ||
| callback: (container: SerializedMetricContainer, metrics: SerializedMetric[]) => void | boolean, | ||
| ): void { | ||
| forEachEnvelopeItem(envelope, (item, type) => { | ||
| if (type === 'trace_metric') { | ||
| const container = Array.isArray(item) ? (item[1] as SerializedMetricContainer) : undefined; | ||
| if (container?.items) { | ||
| return callback(container, container.items); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Gets a metric from an envelope. | ||
| * | ||
| * This is only exported for use in tests and advanced use cases. | ||
| */ | ||
| export function metricFromEnvelope(envelope: Envelope): SerializedMetric | undefined { | ||
| let metric: SerializedMetric | undefined; | ||
|
|
||
| forEachEnvelopeItem(envelope, (item, type) => { | ||
| if (type === 'trace_metric') { | ||
| const container = Array.isArray(item) ? (item[1] as SerializedMetricContainer) : undefined; | ||
| const containerItems = container?.items; | ||
| if (containerItems) { | ||
| metric = containerItems[0]; | ||
|
Member
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. Wouldn't this mean we always just pull the first metric?
Contributor
Author
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. yes it would mean we pull the first metric. What I'm thinking now after your comment is that I can add a logic which will check if all metrics and route to same or multiple destinations. Whats your opinion ? |
||
| } | ||
| } | ||
| return !!metric; | ||
| }); | ||
|
|
||
| return metric; | ||
| } | ||
|
|
||
| /** | ||
| * Applies the release to all metrics in an envelope. | ||
| */ | ||
| function applyReleaseToMetrics(envelope: Envelope, release: string): void { | ||
| forEachMetricContainer(envelope, (container, metrics) => { | ||
| container.items = metrics.map(metric => ({ | ||
| ...metric, | ||
| attributes: { | ||
| ...metric.attributes, | ||
| 'sentry.release': { type: 'string', value: release }, | ||
| }, | ||
| })); | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * It strips routing attributes from all metrics in an envelope. | ||
| * This prevents the routing information from being sent to Sentry. | ||
| */ | ||
| function stripRoutingAttributesFromMetrics(envelope: Envelope): void { | ||
| let strippedCount = 0; | ||
| forEachMetricContainer(envelope, (_container, metrics) => { | ||
| for (const metric of metrics) { | ||
| if (metric.attributes && MULTIPLEXED_METRIC_ROUTING_KEY in metric.attributes) { | ||
| DEBUG_BUILD && strippedCount++; | ||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
| const { [MULTIPLEXED_METRIC_ROUTING_KEY]: _routing, ...restAttributes } = metric.attributes; | ||
| metric.attributes = restAttributes; | ||
| } | ||
| } | ||
| }); | ||
| } | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
harshit078 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Creates a transport that overrides the release on all events and metrics. | ||
| */ | ||
| function makeOverrideReleaseTransport<TO extends BaseTransportOptions>( | ||
| createTransport: (options: TO) => Transport, | ||
|
|
@@ -64,6 +142,9 @@ function makeOverrideReleaseTransport<TO extends BaseTransportOptions>( | |
| if (event) { | ||
| event.release = release; | ||
| } | ||
|
|
||
| applyReleaseToMetrics(envelope, release); | ||
|
|
||
| return transport.send(envelope); | ||
| }, | ||
| }; | ||
|
|
@@ -72,14 +153,28 @@ function makeOverrideReleaseTransport<TO extends BaseTransportOptions>( | |
|
|
||
| /** Overrides the DSN in the envelope header */ | ||
| function overrideDsn(envelope: Envelope, dsn: string): Envelope { | ||
| const clonedItems = envelope[1].map(item => { | ||
| if (Array.isArray(item) && item[0]?.type === 'trace_metric') { | ||
| const [header, container] = item as MetricContainerItem; | ||
| return [ | ||
| { ...header }, | ||
| { | ||
| ...container, | ||
| items: container.items ? [...container.items] : [], | ||
| }, | ||
| ]; | ||
| } | ||
| return item; | ||
| }); | ||
|
|
||
| return createEnvelope( | ||
| dsn | ||
| ? { | ||
| ...envelope[0], | ||
| dsn, | ||
| } | ||
| : envelope[0], | ||
| envelope[1], | ||
| clonedItems as (typeof envelope)[1], | ||
| ); | ||
| } | ||
|
|
||
|
|
@@ -109,6 +204,38 @@ export function makeMultiplexedTransport<TO extends BaseTransportOptions>( | |
| ) { | ||
| return event.extra[MULTIPLEXED_TRANSPORT_EXTRA_KEY]; | ||
| } | ||
| const metric = args.getMetric(); | ||
| if (metric?.attributes?.[MULTIPLEXED_METRIC_ROUTING_KEY]) { | ||
| const routingAttr = metric.attributes[MULTIPLEXED_METRIC_ROUTING_KEY]; | ||
| DEBUG_BUILD && debug.log('[Multiplexed Transport] Found metric routing attribute:', routingAttr); | ||
|
|
||
| let routingValue: unknown; | ||
| if (typeof routingAttr === 'object' && routingAttr !== null && 'value' in routingAttr) { | ||
| routingValue = routingAttr.value; | ||
| if (typeof routingValue === 'string') { | ||
| try { | ||
| routingValue = JSON.parse(routingValue); | ||
| DEBUG_BUILD && debug.log('[Multiplexed Transport] Parsed routing value:', routingValue); | ||
| } catch (e) { | ||
| DEBUG_BUILD && debug.warn('[Multiplexed Transport] Failed to parse routing JSON:', e); | ||
| return []; | ||
harshit078 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| } else { | ||
| routingValue = routingAttr; | ||
| } | ||
|
|
||
| if (Array.isArray(routingValue)) { | ||
| const validRoutes = routingValue.filter( | ||
| (route): route is string | RouteTo => | ||
| route !== null && | ||
| route !== undefined && | ||
| (typeof route === 'string' || (typeof route === 'object' && 'dsn' in route)), | ||
| ); | ||
| DEBUG_BUILD && debug.log('[Multiplexed Transport] Valid routes:', validRoutes); | ||
| return validRoutes; | ||
| } | ||
| } | ||
| return []; | ||
| }); | ||
|
|
||
|
|
@@ -142,7 +269,11 @@ export function makeMultiplexedTransport<TO extends BaseTransportOptions>( | |
| return eventFromEnvelope(envelope, eventTypes); | ||
| } | ||
|
|
||
| const transports = actualMatcher({ envelope, getEvent }) | ||
| function getMetric(): SerializedMetric | undefined { | ||
| return metricFromEnvelope(envelope); | ||
| } | ||
|
|
||
| const transports = actualMatcher({ envelope, getEvent, getMetric }) | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| .map(result => { | ||
sentry[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (typeof result === 'string') { | ||
| return getTransport(result, undefined); | ||
|
|
@@ -152,6 +283,8 @@ export function makeMultiplexedTransport<TO extends BaseTransportOptions>( | |
| }) | ||
| .filter((t): t is [string, Transport] => !!t); | ||
|
|
||
| stripRoutingAttributesFromMetrics(envelope); | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // If we have no transports to send to, use the fallback transport | ||
| // Don't override the DSN in the header for the fallback transport. '' is falsy | ||
| const transportsWithFallback: [string, Transport][] = transports.length ? transports : [['', fallbackTransport]]; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -72,10 +72,23 @@ export interface SerializedMetric { | |
|
|
||
| /** | ||
| * Arbitrary structured data that stores information about the metric. | ||
| * This can contain routing information via the `MULTIPLEXED_METRIC_ROUTING_KEY` key. | ||
| */ | ||
| attributes?: Attributes; | ||
| } | ||
|
|
||
| export type SerializedMetricContainer = { | ||
| items: Array<SerializedMetric>; | ||
| }; | ||
|
|
||
| export interface MetricRoutingInfo { | ||
| /** | ||
| * The DSN of the Sentry project to send the metric to. | ||
| */ | ||
| dsn: string; | ||
|
|
||
| /** | ||
| * The release of the Sentry project to send the metric to. | ||
| */ | ||
| release?: string; | ||
| } | ||
|
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. New
|
||


Uh oh!
There was an error while loading. Please reload this page.