Skip to content

Commit ad27f6e

Browse files
alwxantonislucas-zimerman
authored
feat(metrics): Updates Metrics code to correctly process envelops withapplication/vnd.sentry.items.trace-metric+json content type (#5440)
* feat(metrics): A fix for metrics * Updated tests * Fixes * Lint fiix * Wording fix --------- Co-authored-by: Antonis Lilis <antonis.lilis@sentry.io> Co-authored-by: LucasZF <lucas-zimerman1@hotmail.com>
1 parent f3b058c commit ad27f6e

File tree

5 files changed

+130
-12
lines changed

5 files changed

+130
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
### Features
1212

13-
- Adds Metrics Beta ([#5402](https://github.com/getsentry/sentry-react-native/pull/5402))
13+
- Adds Metrics Beta for iOS ([#5402](https://github.com/getsentry/sentry-react-native/pull/5402))
1414
- Improves Expo Router integration to optionally include full paths to components instead of just component names ([#5414](https://github.com/getsentry/sentry-react-native/pull/5414))
1515
- Report slow and frozen frames as TTID/TTFD span data ([#5419](https://github.com/getsentry/sentry-react-native/pull/5419))
1616
- Report slow and frozen frames on spans created through the API ([#5420](https://github.com/getsentry/sentry-react-native/issues/5420))

packages/core/src/js/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export type {
1313
UserFeedback,
1414
ErrorEvent,
1515
TransactionEvent,
16+
Metric,
1617
} from '@sentry/core';
1718

1819
export {
@@ -46,7 +47,6 @@ export {
4647
setCurrentClient,
4748
addEventProcessor,
4849
lastEventId,
49-
metrics,
5050
} from '@sentry/core';
5151

5252
export {
@@ -63,6 +63,7 @@ export {
6363
consoleLoggingIntegration,
6464
featureFlagsIntegration,
6565
type FeatureFlagsIntegration,
66+
metrics,
6667
} from '@sentry/browser';
6768

6869
export * from './integrations/exports';

packages/core/src/js/wrapper.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ export const NATIVE: SentryNativeWrapper = {
184184

185185
let bytesContentType: string;
186186
let bytesPayload: number[] | Uint8Array | undefined;
187+
187188
if (typeof itemPayload === 'string') {
188189
bytesContentType = 'text/plain';
189190
bytesPayload = encodeUTF8(itemPayload);
@@ -192,7 +193,7 @@ export const NATIVE: SentryNativeWrapper = {
192193
typeof itemHeader.content_type === 'string' ? itemHeader.content_type : 'application/octet-stream';
193194
bytesPayload = itemPayload;
194195
} else {
195-
bytesContentType = 'application/vnd.sentry.items.log+json';
196+
bytesContentType = typeof itemHeader.content_type === 'string' ? itemHeader.content_type : 'application/json';
196197
bytesPayload = encodeUTF8(JSON.stringify(itemPayload));
197198
if (!hardCrashed) {
198199
hardCrashed = isHardCrash(itemPayload);

packages/core/test/wrapper.test.ts

Lines changed: 123 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
import type { Event, EventEnvelope, EventItem, SeverityLevel } from '@sentry/core';
1+
import type {
2+
Event,
3+
EventEnvelope,
4+
EventItem,
5+
LogEnvelope,
6+
LogSeverityLevel,
7+
MetricEnvelope,
8+
MetricType,
9+
SeverityLevel,
10+
} from '@sentry/core';
211
import { createEnvelope, debug } from '@sentry/core';
312
import * as RN from 'react-native';
413
import type { Spec } from '../src/js/NativeRNSentry';
@@ -391,7 +400,7 @@ describe('Tests Native Wrapper', () => {
391400
base64StringFromByteArray(
392401
utf8ToBytes(
393402
'{"event_id":"event0","sent_at":"123"}\n' +
394-
'{"type":"event","content_type":"application/vnd.sentry.items.log+json","length":87}\n' +
403+
'{"type":"event","content_type":"application/json","length":87}\n' +
395404
'{"event_id":"event0","message":"test","sdk":{"name":"test-sdk-name","version":"2.1.3"}}\n',
396405
),
397406
),
@@ -423,7 +432,7 @@ describe('Tests Native Wrapper', () => {
423432
base64StringFromByteArray(
424433
utf8ToBytes(
425434
'{"event_id":"event0","sent_at":"123"}\n' +
426-
'{"type":"event","content_type":"application/vnd.sentry.items.log+json","length":93}\n' +
435+
'{"type":"event","content_type":"application/json","length":93}\n' +
427436
'{"event_id":"event0","sdk":{"name":"test-sdk-name","version":"2.1.3"},"instance":{"value":0}}\n',
428437
),
429438
),
@@ -466,7 +475,7 @@ describe('Tests Native Wrapper', () => {
466475
base64StringFromByteArray(
467476
utf8ToBytes(
468477
'{"event_id":"event0","sent_at":"123"}\n' +
469-
'{"type":"event","content_type":"application/vnd.sentry.items.log+json","length":50}\n' +
478+
'{"type":"event","content_type":"application/json","length":50}\n' +
470479
'{"event_id":"event0","message":{"message":"test"}}\n',
471480
),
472481
),
@@ -505,7 +514,7 @@ describe('Tests Native Wrapper', () => {
505514
base64StringFromByteArray(
506515
utf8ToBytes(
507516
'{"event_id":"event0","sent_at":"123"}\n' +
508-
'{"type":"event","content_type":"application/vnd.sentry.items.log+json","length":124}\n' +
517+
'{"type":"event","content_type":"application/json","length":124}\n' +
509518
'{"event_id":"event0","exception":{"values":[{"mechanism":{"handled":true,"type":""}}]},"breadcrumbs":[{"message":"crumb!"}]}\n',
510519
),
511520
),
@@ -534,7 +543,7 @@ describe('Tests Native Wrapper', () => {
534543
base64StringFromByteArray(
535544
utf8ToBytes(
536545
'{"event_id":"event0","sent_at":"123"}\n' +
537-
'{"type":"event","content_type":"application/vnd.sentry.items.log+json","length":58}\n' +
546+
'{"type":"event","content_type":"application/json","length":58}\n' +
538547
'{"event_id":"event0","breadcrumbs":[{"message":"crumb!"}]}\n',
539548
),
540549
),
@@ -573,13 +582,120 @@ describe('Tests Native Wrapper', () => {
573582
base64StringFromByteArray(
574583
utf8ToBytes(
575584
'{"event_id":"event0","sent_at":"123"}\n' +
576-
'{"type":"event","content_type":"application/vnd.sentry.items.log+json","length":132}\n' +
585+
'{"type":"event","content_type":"application/json","length":132}\n' +
577586
'{"event_id":"event0","exception":{"values":[{"mechanism":{"handled":false,"type":"onerror"}}]},"breadcrumbs":[{"message":"crumb!"}]}\n',
578587
),
579588
),
580589
{ hardCrashed: true },
581590
);
582591
});
592+
593+
test('preserves content_type for logs (application/vnd.sentry.items.log+json)', async () => {
594+
const logPayload = {
595+
timestamp: 1234567890,
596+
level: 'info' as LogSeverityLevel,
597+
body: 'test log message',
598+
logger: 'test-logger',
599+
};
600+
601+
const env = createEnvelope<LogEnvelope>({ event_id: 'log0', sent_at: '123' }, [
602+
[
603+
{
604+
type: 'log',
605+
content_type: 'application/vnd.sentry.items.log+json',
606+
item_count: 1,
607+
},
608+
{ items: [logPayload] },
609+
],
610+
]);
611+
612+
await NATIVE.sendEnvelope(env);
613+
614+
const expectedPayload = JSON.stringify({ items: [logPayload] });
615+
const expectedLength = utf8ToBytes(expectedPayload).length;
616+
617+
expect(RNSentry.captureEnvelope).toHaveBeenCalledWith(
618+
base64StringFromByteArray(
619+
utf8ToBytes(
620+
'{"event_id":"log0","sent_at":"123"}\n' +
621+
`{"type":"log","content_type":"application/vnd.sentry.items.log+json","item_count":1,"length":${expectedLength}}\n` +
622+
`${expectedPayload}\n`,
623+
),
624+
),
625+
{ hardCrashed: false },
626+
);
627+
});
628+
629+
test('preserves content_type for metrics (application/vnd.sentry.items.trace-metric+json)', async () => {
630+
const metricsPayload = {
631+
timestamp: 1234567890,
632+
trace_id: 'trace_id',
633+
name: 'metric.name',
634+
type: 'counter' as MetricType,
635+
value: 42,
636+
measurements: {
637+
'metric.name': { value: 42 },
638+
},
639+
};
640+
641+
const env = createEnvelope<MetricEnvelope>({ event_id: 'metric0', sent_at: '123' }, [
642+
[
643+
{
644+
type: 'trace_metric',
645+
content_type: 'application/vnd.sentry.items.trace-metric+json',
646+
item_count: 1,
647+
},
648+
{ items: [metricsPayload] },
649+
],
650+
]);
651+
652+
await NATIVE.sendEnvelope(env);
653+
654+
const expectedPayload = JSON.stringify({ items: [metricsPayload] });
655+
const expectedLength = utf8ToBytes(expectedPayload).length;
656+
657+
expect(RNSentry.captureEnvelope).toHaveBeenCalledWith(
658+
base64StringFromByteArray(
659+
utf8ToBytes(
660+
'{"event_id":"metric0","sent_at":"123"}\n' +
661+
`{"type":"trace_metric","content_type":"application/vnd.sentry.items.trace-metric+json","item_count":1,"length":${expectedLength}}\n` +
662+
`${expectedPayload}\n`,
663+
),
664+
),
665+
{ hardCrashed: false },
666+
);
667+
});
668+
669+
test('preserves custom content_type when provided', async () => {
670+
const payload = {
671+
event_id: 'event0',
672+
message: 'test',
673+
};
674+
const env = createEnvelope({ event_id: 'foo1', sent_at: '123' }, [
675+
[
676+
{
677+
type: 'event',
678+
content_type: 'application/custom-type',
679+
},
680+
payload,
681+
],
682+
]);
683+
684+
await NATIVE.sendEnvelope(env);
685+
686+
const expectedPayload = JSON.stringify(payload);
687+
const expectedLength = utf8ToBytes(expectedPayload).length;
688+
expect(RNSentry.captureEnvelope).toHaveBeenCalledWith(
689+
base64StringFromByteArray(
690+
utf8ToBytes(
691+
'{"event_id":"foo1","sent_at":"123"}\n' +
692+
`{"type":"event","content_type":"application/custom-type","length":${expectedLength}}\n` +
693+
`${expectedPayload}\n`,
694+
),
695+
),
696+
{ hardCrashed: false },
697+
);
698+
});
583699
});
584700

585701
describe('fetchRelease', () => {

samples/react-native/src/Screens/ErrorsScreen.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,9 @@ const ErrorsScreen = (_props: Props) => {
127127
}}
128128
/>
129129
<Button
130-
title="Send distribution metric"
130+
title="Send count metric with attributes"
131131
onPress={() => {
132-
Sentry.metrics.count('distribution_metric', 100);
132+
Sentry.metrics.count('count_metric', 100, { attributes: { from_test_app: true } });
133133
}}
134134
/>
135135
<Button

0 commit comments

Comments
 (0)