1+ import { safeSetAttribute , serializeAttributes } from '../attributes' ;
12import { getGlobalSingleton } from '../carrier' ;
23import type { Client } from '../client' ;
3- import { getClient , getCurrentScope , getGlobalScope , getIsolationScope } from '../currentScopes' ;
4+ import { getClient , getCurrentScope } from '../currentScopes' ;
45import { DEBUG_BUILD } from '../debug-build' ;
5- import type { Scope , ScopeData } from '../scope' ;
6+ import type { Scope } from '../scope' ;
67import type { Integration } from '../types-hoist/integration' ;
7- import type { Metric , SerializedMetric , SerializedMetricAttributeValue } from '../types-hoist/metric' ;
8- import { mergeScopeData } from '../utils/applyScopeDataToEvent ' ;
8+ import type { Metric , SerializedMetric } from '../types-hoist/metric' ;
9+ import type { User } from '../types-hoist/user ' ;
910import { debug } from '../utils/debug-logger' ;
11+ import { getFinalScopeData } from '../utils/scope-utils' ;
1012import { _getSpanForScope } from '../utils/spanOnScope' ;
1113import { timestampInSeconds } from '../utils/time' ;
1214import { _getTraceInfoFromScope } from '../utils/trace-info' ;
1315import { createMetricEnvelope } from './envelope' ;
1416
1517const MAX_METRIC_BUFFER_SIZE = 1000 ;
1618
17- /**
18- * Converts a metric attribute to a serialized metric attribute.
19- *
20- * @param value - The value of the metric attribute.
21- * @returns The serialized metric attribute.
22- */
23- export function metricAttributeToSerializedMetricAttribute ( value : unknown ) : SerializedMetricAttributeValue {
24- switch ( typeof value ) {
25- case 'number' :
26- if ( Number . isInteger ( value ) ) {
27- return {
28- value,
29- type : 'integer' ,
30- } ;
31- }
32- return {
33- value,
34- type : 'double' ,
35- } ;
36- case 'boolean' :
37- return {
38- value,
39- type : 'boolean' ,
40- } ;
41- case 'string' :
42- return {
43- value,
44- type : 'string' ,
45- } ;
46- default : {
47- let stringValue = '' ;
48- try {
49- stringValue = JSON . stringify ( value ) ?? '' ;
50- } catch {
51- // Do nothing
52- }
53- return {
54- value : stringValue ,
55- type : 'string' ,
56- } ;
57- }
58- }
59- }
60-
61- /**
62- * Sets a metric attribute if the value exists and the attribute key is not already present.
63- *
64- * @param metricAttributes - The metric attributes object to modify.
65- * @param key - The attribute key to set.
66- * @param value - The value to set (only sets if truthy and key not present).
67- * @param setEvenIfPresent - Whether to set the attribute if it is present. Defaults to true.
68- */
69- function setMetricAttribute (
70- metricAttributes : Record < string , unknown > ,
71- key : string ,
72- value : unknown ,
73- setEvenIfPresent = true ,
74- ) : void {
75- if ( value && ( setEvenIfPresent || ! ( key in metricAttributes ) ) ) {
76- metricAttributes [ key ] = value ;
77- }
78- }
79-
8019/**
8120 * Captures a serialized metric event and adds it to the metric buffer for the given client.
8221 *
@@ -120,29 +59,26 @@ export interface InternalCaptureMetricOptions {
12059/**
12160 * Enriches metric with all contextual attributes (user, SDK metadata, replay, etc.)
12261 */
123- function _enrichMetricAttributes ( beforeMetric : Metric , client : Client , currentScope : Scope ) : Metric {
62+ function _enrichMetricAttributes ( beforeMetric : Metric , client : Client , user : User ) : Metric {
12463 const { release, environment } = client . getOptions ( ) ;
12564
12665 const processedMetricAttributes = {
12766 ...beforeMetric . attributes ,
12867 } ;
12968
130- // Add user attributes
131- const {
132- user : { id, email, username } ,
133- } = getMergedScopeData ( currentScope ) ;
134- setMetricAttribute ( processedMetricAttributes , 'user.id' , id , false ) ;
135- setMetricAttribute ( processedMetricAttributes , 'user.email' , email , false ) ;
136- setMetricAttribute ( processedMetricAttributes , 'user.name' , username , false ) ;
69+ const { id, email, username } = user ;
70+ safeSetAttribute ( processedMetricAttributes , 'user.id' , id , false ) ;
71+ safeSetAttribute ( processedMetricAttributes , 'user.email' , email , false ) ;
72+ safeSetAttribute ( processedMetricAttributes , 'user.name' , username , false ) ;
13773
13874 // Add Sentry metadata
139- setMetricAttribute ( processedMetricAttributes , 'sentry.release' , release ) ;
140- setMetricAttribute ( processedMetricAttributes , 'sentry.environment' , environment ) ;
75+ safeSetAttribute ( processedMetricAttributes , 'sentry.release' , release ) ;
76+ safeSetAttribute ( processedMetricAttributes , 'sentry.environment' , environment ) ;
14177
14278 // Add SDK metadata
14379 const { name, version } = client . getSdkMetadata ( ) ?. sdk ?? { } ;
144- setMetricAttribute ( processedMetricAttributes , 'sentry.sdk.name' , name ) ;
145- setMetricAttribute ( processedMetricAttributes , 'sentry.sdk.version' , version ) ;
80+ safeSetAttribute ( processedMetricAttributes , 'sentry.sdk.name' , name ) ;
81+ safeSetAttribute ( processedMetricAttributes , 'sentry.sdk.version' , version ) ;
14682
14783 // Add replay metadata
14884 const replay = client . getIntegrationByName <
@@ -153,10 +89,10 @@ function _enrichMetricAttributes(beforeMetric: Metric, client: Client, currentSc
15389 > ( 'Replay' ) ;
15490
15591 const replayId = replay ?. getReplayId ( true ) ;
156- setMetricAttribute ( processedMetricAttributes , 'sentry.replay_id' , replayId ) ;
92+ safeSetAttribute ( processedMetricAttributes , 'sentry.replay_id' , replayId ) ;
15793
15894 if ( replayId && replay ?. getRecordingMode ( ) === 'buffer' ) {
159- setMetricAttribute ( processedMetricAttributes , 'sentry._internal.replay_is_buffering' , true ) ;
95+ safeSetAttribute ( processedMetricAttributes , 'sentry._internal.replay_is_buffering' , true ) ;
16096 }
16197
16298 return {
@@ -165,36 +101,6 @@ function _enrichMetricAttributes(beforeMetric: Metric, client: Client, currentSc
165101 } ;
166102}
167103
168- /**
169- * Creates a serialized metric ready to be sent to Sentry.
170- */
171- function _buildSerializedMetric ( metric : Metric , client : Client , currentScope : Scope ) : SerializedMetric {
172- // Serialize attributes
173- const serializedAttributes : Record < string , SerializedMetricAttributeValue > = { } ;
174- for ( const key in metric . attributes ) {
175- if ( metric . attributes [ key ] !== undefined ) {
176- serializedAttributes [ key ] = metricAttributeToSerializedMetricAttribute ( metric . attributes [ key ] ) ;
177- }
178- }
179-
180- // Get trace context
181- const [ , traceContext ] = _getTraceInfoFromScope ( client , currentScope ) ;
182- const span = _getSpanForScope ( currentScope ) ;
183- const traceId = span ? span . spanContext ( ) . traceId : traceContext ?. trace_id ;
184- const spanId = span ? span . spanContext ( ) . spanId : undefined ;
185-
186- return {
187- timestamp : timestampInSeconds ( ) ,
188- trace_id : traceId ?? '' ,
189- span_id : spanId ,
190- name : metric . name ,
191- type : metric . type ,
192- unit : metric . unit ,
193- value : metric . value ,
194- attributes : serializedAttributes ,
195- } ;
196- }
197-
198104/**
199105 * Captures a metric event and sends it to Sentry.
200106 *
@@ -224,8 +130,10 @@ export function _INTERNAL_captureMetric(beforeMetric: Metric, options?: Internal
224130 return ;
225131 }
226132
133+ const { user, attributes : scopeAttributes } = getFinalScopeData ( currentScope ) ;
134+
227135 // Enrich metric with contextual attributes
228- const enrichedMetric = _enrichMetricAttributes ( beforeMetric , client , currentScope ) ;
136+ const enrichedMetric = _enrichMetricAttributes ( beforeMetric , client , user ) ;
229137
230138 client . emit ( 'processMetric' , enrichedMetric ) ;
231139
@@ -239,7 +147,25 @@ export function _INTERNAL_captureMetric(beforeMetric: Metric, options?: Internal
239147 return ;
240148 }
241149
242- const serializedMetric = _buildSerializedMetric ( processedMetric , client , currentScope ) ;
150+ const [ , traceContext ] = _getTraceInfoFromScope ( client , currentScope ) ;
151+ const span = _getSpanForScope ( currentScope ) ;
152+ const traceId = span ? span . spanContext ( ) . traceId : traceContext ?. trace_id ;
153+ const spanId = span ? span . spanContext ( ) . spanId : undefined ;
154+
155+ const { name, type, unit, value, attributes : metricAttributes } = processedMetric ;
156+ const serializedMetric = {
157+ timestamp : timestampInSeconds ( ) ,
158+ trace_id : traceId ?? '' ,
159+ span_id : spanId ,
160+ name,
161+ type,
162+ unit,
163+ value,
164+ attributes : {
165+ ...serializeAttributes ( metricAttributes , true ) ,
166+ ...serializeAttributes ( scopeAttributes ) ,
167+ } ,
168+ } ;
243169
244170 DEBUG_BUILD && debug . log ( '[Metric]' , serializedMetric ) ;
245171
@@ -288,20 +214,6 @@ export function _INTERNAL_getMetricBuffer(client: Client): Array<SerializedMetri
288214 return _getBufferMap ( ) . get ( client ) ;
289215}
290216
291- /**
292- * Get the scope data for the current scope after merging with the
293- * global scope and isolation scope.
294- *
295- * @param currentScope - The current scope.
296- * @returns The scope data.
297- */
298- function getMergedScopeData ( currentScope : Scope ) : ScopeData {
299- const scopeData = getGlobalScope ( ) . getScopeData ( ) ;
300- mergeScopeData ( scopeData , getIsolationScope ( ) . getScopeData ( ) ) ;
301- mergeScopeData ( scopeData , currentScope . getScopeData ( ) ) ;
302- return scopeData ;
303- }
304-
305217function _getBufferMap ( ) : WeakMap < Client , Array < SerializedMetric > > {
306218 // The reference to the Client <> MetricBuffer map is stored on the carrier to ensure it's always the same
307219 return getGlobalSingleton ( 'clientToMetricBufferMap' , ( ) => new WeakMap < Client , Array < SerializedMetric > > ( ) ) ;
0 commit comments