From 8d821ee3dcc2305a77d222db3be1810acb39c25f Mon Sep 17 00:00:00 2001 From: lucas Date: Mon, 8 Dec 2025 15:03:41 +0000 Subject: [PATCH 1/6] add sample for metrics/ update code and tests --- .../src/app/tab1/tab1.page.html | 4 + .../src/app/tab1/tab1.page.ts | 23 ++++ .../src/app/tab1/tab1.page.html | 4 + .../src/app/tab1/tab1.page.ts | 23 ++++ example/ionic-vue3/src/views/HomePage.vue | 75 ++++++++--- src/wrapper.ts | 7 +- test/helper/envelopeHelper.ts | 122 ++++++++++++++++++ test/wrapper.test.ts | 118 ++++++++++++++++- 8 files changed, 354 insertions(+), 22 deletions(-) create mode 100644 test/helper/envelopeHelper.ts diff --git a/example/ionic-angular-v6/src/app/tab1/tab1.page.html b/example/ionic-angular-v6/src/app/tab1/tab1.page.html index c926bf90..c5271097 100644 --- a/example/ionic-angular-v6/src/app/tab1/tab1.page.html +++ b/example/ionic-angular-v6/src/app/tab1/tab1.page.html @@ -72,5 +72,9 @@ Clear Test Context Clear Test Context + diff --git a/example/ionic-angular-v6/src/app/tab1/tab1.page.ts b/example/ionic-angular-v6/src/app/tab1/tab1.page.ts index 1141ad85..a1991732 100644 --- a/example/ionic-angular-v6/src/app/tab1/tab1.page.ts +++ b/example/ionic-angular-v6/src/app/tab1/tab1.page.ts @@ -138,4 +138,27 @@ export class Tab1Page { public clearTestContext(): void { Sentry.setContext('TEST-CONTEXT', null); } + + public createMetric(): void { + // Create a metric using Sentry metrics API + // @ts-ignore - metrics API may not be fully typed + if (Sentry.metrics && typeof Sentry.metrics.increment === 'function') { + Sentry.metrics.increment('test.metric.counter', 1, { + tags: { source: 'capacitor-sample-app' }, + }); + } else { + // Fallback: create a custom metric using captureMessage with metric context + Sentry.captureMessage('Metric created', { + level: 'info', + tags: { + metric_type: 'counter', + metric_name: 'test.metric.counter', + }, + extra: { + value: 1, + timestamp: Date.now(), + }, + }); + } + } } diff --git a/example/ionic-angular-v7/src/app/tab1/tab1.page.html b/example/ionic-angular-v7/src/app/tab1/tab1.page.html index c926bf90..c5271097 100644 --- a/example/ionic-angular-v7/src/app/tab1/tab1.page.html +++ b/example/ionic-angular-v7/src/app/tab1/tab1.page.html @@ -72,5 +72,9 @@ Clear Test Context Clear Test Context + diff --git a/example/ionic-angular-v7/src/app/tab1/tab1.page.ts b/example/ionic-angular-v7/src/app/tab1/tab1.page.ts index 1141ad85..a1991732 100644 --- a/example/ionic-angular-v7/src/app/tab1/tab1.page.ts +++ b/example/ionic-angular-v7/src/app/tab1/tab1.page.ts @@ -138,4 +138,27 @@ export class Tab1Page { public clearTestContext(): void { Sentry.setContext('TEST-CONTEXT', null); } + + public createMetric(): void { + // Create a metric using Sentry metrics API + // @ts-ignore - metrics API may not be fully typed + if (Sentry.metrics && typeof Sentry.metrics.increment === 'function') { + Sentry.metrics.increment('test.metric.counter', 1, { + tags: { source: 'capacitor-sample-app' }, + }); + } else { + // Fallback: create a custom metric using captureMessage with metric context + Sentry.captureMessage('Metric created', { + level: 'info', + tags: { + metric_type: 'counter', + metric_name: 'test.metric.counter', + }, + extra: { + value: 1, + timestamp: Date.now(), + }, + }); + } + } } diff --git a/example/ionic-vue3/src/views/HomePage.vue b/example/ionic-vue3/src/views/HomePage.vue index 2fe86bd6..0f735185 100644 --- a/example/ionic-vue3/src/views/HomePage.vue +++ b/example/ionic-vue3/src/views/HomePage.vue @@ -1,43 +1,80 @@ diff --git a/src/wrapper.ts b/src/wrapper.ts index 18580729..6cd9348d 100644 --- a/src/wrapper.ts +++ b/src/wrapper.ts @@ -59,7 +59,12 @@ export const NATIVE = { : 'application/octet-stream'; bytesPayload = [...itemPayload]; } else { - bytesContentType = 'application/vnd.sentry.items.log+json'; + // Check if the item header already has a content_type set + if (typeof itemHeader.content_type === 'string') { + bytesContentType = itemHeader.content_type; + } else { + bytesContentType = 'application/json'; + } bytesPayload = utf8ToBytes(JSON.stringify(itemPayload)); } diff --git a/test/helper/envelopeHelper.ts b/test/helper/envelopeHelper.ts new file mode 100644 index 00000000..2ba15373 --- /dev/null +++ b/test/helper/envelopeHelper.ts @@ -0,0 +1,122 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import type { DsnComponents, LogEnvelope, MetricEnvelope, SdkMetadata, SerializedLog, SerializedMetric } from '@sentry/core'; +import { createEnvelope, dsnToString } from '@sentry/core'; +import type { LogContainerItem, MetricContainerItem } from '@sentry/core/build/types/types-hoist/envelope'; + + +/** + * Based on packages/core/src/metrics/envelope.ts + * Creates a metric container envelope item for a list of metrics. + * + * @param items - The metrics to include in the envelope. + * @returns The created metric container envelope item. + */ +export function createMetricContainerEnvelopeItem(items: Array): MetricContainerItem { + return [ + { + type: 'trace_metric', + item_count: items.length, + content_type: 'application/vnd.sentry.items.trace-metric+json', + } as MetricContainerItem[0], + { + items, + }, + ]; +} + + +/** + * Based on getsentry/sentry-javascript/packages/core/src/logs/envelope.ts + * Creates a log container envelope item for a list of logs. + * + * @param items - The logs to include in the envelope. + * @returns The created log container envelope item. + */ +export function createLogContainerEnvelopeItem(items: Array): LogContainerItem { + return [ + { + type: 'log', + item_count: items.length, + content_type: 'application/vnd.sentry.items.log+json', + }, + { + items, + }, + ]; +} + +/** + * Based on getsentry/sentry-javascript/packages/core/src/logs/envelope.ts + * Creates an envelope for a list of logs. + * + * Logs from multiple traces can be included in the same envelope. + * + * @param logs - The logs to include in the envelope. + * @param metadata - The metadata to include in the envelope. + * @param tunnel - The tunnel to include in the envelope. + * @param dsn - The DSN to include in the envelope. + * @returns The created envelope. + */ +export function createLogEnvelopeHelper(logs: Array, + metadata?: SdkMetadata, + tunnel?: string, + dsn?: DsnComponents, +): LogEnvelope { + const headers: LogEnvelope[0] = {}; + + if (metadata?.sdk) { + headers.sdk = { + name: metadata.sdk.name, + version: metadata.sdk.version, + }; + } + + if (!!tunnel && !!dsn) { + headers.dsn = dsnToString(dsn); + } + + return createEnvelope(headers, [createLogContainerEnvelopeItem(logs)]); +} + +/** + * Based on packages/core/src/metrics/envelope.ts + * Creates an envelope for a list of metrics. + * + * Metrics from multiple traces can be included in the same envelope. + * + * @param metrics - The metrics to include in the envelope. + * @param metadata - The metadata to include in the envelope. + * @param tunnel - The tunnel to include in the envelope. + * @param dsn - The DSN to include in the envelope. + * @returns The created envelope. + */ +export function createMetricEnvelopeHelper(metrics: Array, + metadata?: SdkMetadata, + tunnel?: string, + dsn?: DsnComponents): MetricEnvelope { + const headers: MetricEnvelope[0] = {}; + + if (metadata?.sdk) { + headers.sdk = { + name: metadata.sdk.name, + version: metadata.sdk.version, + }; + } + + if (!!tunnel && !!dsn) { + headers.dsn = dsnToString(dsn); + } + + return createEnvelope(headers, [createMetricContainerEnvelopeItem(metrics)]); +} + + +export function base64EnvelopeToString(envelope: string): string | undefined { + if (!envelope) { + return undefined; + } + + const decoded = Buffer.from(envelope, 'base64').toString('utf-8'); + return decoded; +} + diff --git a/test/wrapper.test.ts b/test/wrapper.test.ts index 2f7a1acd..557eea67 100644 --- a/test/wrapper.test.ts +++ b/test/wrapper.test.ts @@ -2,6 +2,7 @@ import type { Envelope, EventEnvelope, EventItem, SeverityLevel, TransportMakeRequestResponse } from '@sentry/core'; import { createEnvelope, debug, dropUndefinedKeys } from '@sentry/core'; import { utf8ToBytes } from '../src/vendor'; +import { base64EnvelopeToString, createLogEnvelopeHelper, createMetricEnvelopeHelper } from './helper/envelopeHelper'; let getStringBytesLengthValue = 1; @@ -182,7 +183,7 @@ describe('Tests Native Wrapper', () => { }); const expectedItem = JSON.stringify({ type: 'event', - content_type: 'application/vnd.sentry.items.log+json', + content_type: 'application/json', length: expectedNativeLength, }); const expectedPayload = JSON.stringify({ @@ -282,7 +283,120 @@ describe('Tests Native Wrapper', () => { NATIVE.enableNative = true; const result = await NATIVE.sendEnvelope(env); expect(result).toMatchObject(expectedReturn); - }) + }); + + test('uses correct content type for metrics with content_type set', async () => { + // Create a SerializedMetric array as expected by createMetricEnvelope + const serializedMetrics = [ + { + timestamp: 1765200319.505, + name: 'test.metric.counter', + value: 1, + type: 'counter' as const, + unit: 'none', + trace_id: '', + }, + ]; + + // Use createMetricEnvelopeHelper to create a properly formatted metric envelope + const env = createMetricEnvelopeHelper(serializedMetrics); + + await NATIVE.sendEnvelope(env); + + expect(SentryCapacitor.captureEnvelope).toHaveBeenCalled(); + const base64Envelope = (SentryCapacitor.captureEnvelope as jest.Mock).mock.calls[0]?.[0]?.envelope; + expect(base64Envelope).toBeDefined(); + expect(base64EnvelopeToString(base64Envelope)).toEqual( +`{} +{"type":"trace_metric","item_count":1,"content_type":"application/vnd.sentry.items.trace-metric+json","length":124} +{"items":[{"timestamp":1765200319.505,"name":"test.metric.counter","value":1,"type":"counter","unit":"none","trace_id":""}]} +`); + }); + + test('uses correct content type for logs with content_type set', async () => { + // Create a SerializedLog array as expected by createLogEnvelope + const serializedLogs = [ + { + timestamp: 1765200551.692, + level: 'info' as const, + body: 'test log message', + severity_number: 9, + attributes: {}, + }, + ]; + + // Use createLogEnvelopeHelper to create a properly formatted log envelope + const env = createLogEnvelopeHelper(serializedLogs); + + await NATIVE.sendEnvelope(env); + + expect(SentryCapacitor.captureEnvelope).toHaveBeenCalled(); + const base64Envelope = (SentryCapacitor.captureEnvelope as jest.Mock).mock.calls[0]?.[0]?.envelope; + expect(base64Envelope).toBeDefined(); + expect(base64EnvelopeToString(base64Envelope)).toEqual( +`{} +{"type":"log","item_count":1,"content_type":"application/vnd.sentry.items.log+json","length":117} +{"items":[{"timestamp":1765200551.692,"level":"info","body":"test log message","severity_number":9,"attributes":{}}]} +`); + }); + + test('respects existing content_type in item header', async () => { + const expectedNativeLength = 50; + getStringBytesLengthValue = expectedNativeLength; + + const customItem = { + custom: 'data', + }; + + // Test with custom content_type - using any to bypass strict typing for test + const env = [ + { event_id: 'custom0', sent_at: '123' }, + [[{ type: 'log', content_type: 'application/vnd.sentry.items.custom+json' }, customItem]], + ] as any as Envelope; + + await NATIVE.sendEnvelope(env); + + expect(SentryCapacitor.captureEnvelope).toHaveBeenCalled(); + const base64Envelope = (SentryCapacitor.captureEnvelope as jest.Mock).mock.calls[0]?.[0]?.envelope; + expect(base64Envelope).toBeDefined(); + expect(base64EnvelopeToString(base64Envelope)).toEqual( +`{"event_id":"custom0","sent_at":"123"} +{"type":"log","content_type":"application/vnd.sentry.items.custom+json","length":17} +{"custom":"data"} +`); + }); + + test('defaults to application/json when content_type is not set', async () => { + const expectedNativeLength = 15; + getStringBytesLengthValue = expectedNativeLength; + + const itemWithoutContentType = { + some: 'data', + }; + + const expectedHeader = JSON.stringify({ + event_id: 'default0', + sent_at: '123' + }); + const expectedItem = JSON.stringify({ + type: 'event', + content_type: 'application/json', + length: expectedNativeLength, + }); + const expectedPayload = JSON.stringify(itemWithoutContentType); + + const expectedEnvelope = `${expectedHeader}\n${expectedItem}\n${expectedPayload}\n`; + + // Test with item that doesn't have content_type set - should default to application/json + const env = [ + { event_id: 'default0', sent_at: '123' }, + [[{ type: 'event' }, itemWithoutContentType]], + ] as any as Envelope; + + await NATIVE.sendEnvelope(env); + + expect(SentryCapacitor.captureEnvelope).toHaveBeenCalledWith({ envelope: base64StringFromByteArray(utf8ToBytes(expectedEnvelope)) }); + }); }); // TODO add this in when fetchRelease method is in progress From e5525dddb0b52ee35487a116276d4771b3d63d7a Mon Sep 17 00:00:00 2001 From: lucas Date: Mon, 8 Dec 2025 19:17:58 +0000 Subject: [PATCH 2/6] initial changelog/ sample app nits --- CHANGELOG.md | 1 + .../src/app/tab1/tab1.page.ts | 22 +++---------------- .../src/app/tab1/tab1.page.ts | 22 +++---------------- example/ionic-vue3/src/views/HomePage.vue | 22 +++---------------- src/index.ts | 3 ++- 5 files changed, 12 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59dcdf43..cfc49d48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ Sentry.init({ ### Features +- Add metric support! - Add Fallback to JavaScript SDK when Native SDK fails to initialize ([#1043](https://github.com/getsentry/sentry-capacitor/pull/1043)) - Add spotlight integration `spotlightIntegration`. ([#1039](https://github.com/getsentry/sentry-capacitor/pull/1039)) diff --git a/example/ionic-angular-v6/src/app/tab1/tab1.page.ts b/example/ionic-angular-v6/src/app/tab1/tab1.page.ts index a1991732..469cac3f 100644 --- a/example/ionic-angular-v6/src/app/tab1/tab1.page.ts +++ b/example/ionic-angular-v6/src/app/tab1/tab1.page.ts @@ -141,24 +141,8 @@ export class Tab1Page { public createMetric(): void { // Create a metric using Sentry metrics API - // @ts-ignore - metrics API may not be fully typed - if (Sentry.metrics && typeof Sentry.metrics.increment === 'function') { - Sentry.metrics.increment('test.metric.counter', 1, { - tags: { source: 'capacitor-sample-app' }, - }); - } else { - // Fallback: create a custom metric using captureMessage with metric context - Sentry.captureMessage('Metric created', { - level: 'info', - tags: { - metric_type: 'counter', - metric_name: 'test.metric.counter', - }, - extra: { - value: 1, - timestamp: Date.now(), - }, - }); + Sentry.metrics.count('test.metric.counter', 1, + { attributes: { from_test_app: true } }, + ); } - } } diff --git a/example/ionic-angular-v7/src/app/tab1/tab1.page.ts b/example/ionic-angular-v7/src/app/tab1/tab1.page.ts index a1991732..09ecc1a6 100644 --- a/example/ionic-angular-v7/src/app/tab1/tab1.page.ts +++ b/example/ionic-angular-v7/src/app/tab1/tab1.page.ts @@ -141,24 +141,8 @@ export class Tab1Page { public createMetric(): void { // Create a metric using Sentry metrics API - // @ts-ignore - metrics API may not be fully typed - if (Sentry.metrics && typeof Sentry.metrics.increment === 'function') { - Sentry.metrics.increment('test.metric.counter', 1, { - tags: { source: 'capacitor-sample-app' }, - }); - } else { - // Fallback: create a custom metric using captureMessage with metric context - Sentry.captureMessage('Metric created', { - level: 'info', - tags: { - metric_type: 'counter', - metric_name: 'test.metric.counter', - }, - extra: { - value: 1, - timestamp: Date.now(), - }, - }); - } + Sentry.metrics.count('test.metric.counter', 1, + { attributes: { from_test_app: true } }, + ); } } diff --git a/example/ionic-vue3/src/views/HomePage.vue b/example/ionic-vue3/src/views/HomePage.vue index 0f735185..9060004b 100644 --- a/example/ionic-vue3/src/views/HomePage.vue +++ b/example/ionic-vue3/src/views/HomePage.vue @@ -39,25 +39,9 @@ export default { }, createMetric() { // Create a metric using Sentry metrics API - // @ts-ignore - metrics API may not be fully typed - if (Sentry.metrics && typeof Sentry.metrics.increment === 'function') { - Sentry.metrics.increment('test.metric.counter', 1, { - tags: { source: 'capacitor-sample-app' }, - }); - } else { - // Fallback: create a custom metric using captureMessage with metric context - Sentry.captureMessage('Metric created', { - level: 'info', - tags: { - metric_type: 'counter', - metric_name: 'test.metric.counter', - }, - extra: { - value: 1, - timestamp: Date.now(), - }, - }); - } + Sentry.metrics.count('test.metric.counter', 1, { + attributes: { from_test_app: true }, + }); }, }, }; diff --git a/src/index.ts b/src/index.ts index 16d6c83e..f9bc7fb5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ export type { Stacktrace, Thread, User, + Metric } from '@sentry/core'; export type { Scope } from '@sentry/core'; @@ -49,7 +50,7 @@ export { startIdleSpan, } from '@sentry/core'; -export { replayIntegration, browserTracingIntegration, registerSpanErrorInstrumentation, logger } from '@sentry/browser'; +export { metrics, replayIntegration, browserTracingIntegration, registerSpanErrorInstrumentation, logger } from '@sentry/browser'; export { pauseAppHangTracking, resumeAppHangTracking } from './wrapper'; From 90ef27f6185e63f5b526b17a92f101d2cb686807 Mon Sep 17 00:00:00 2001 From: lucas Date: Mon, 15 Dec 2025 12:54:33 +0000 Subject: [PATCH 3/6] metrics moved to experimental --- CHANGELOG.md | 3 +- .../ionic-angular-v7/src/app/app.module.ts | 6 +++ src/options.ts | 37 +++++++++++++++++-- src/sdk.ts | 2 + src/wrapper.ts | 7 +--- test/sdk.test.ts | 28 ++++++++++++++ 6 files changed, 73 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75e06c0f..f7ab19ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,7 +53,8 @@ Sentry.init({ ### Features -- Add metric support! +- Add experimental Metric support for Web and iOS. + for more information see doc page: - Add Fallback to JavaScript SDK when Native SDK fails to initialize ([#1043](https://github.com/getsentry/sentry-capacitor/pull/1043)) - Add spotlight integration `spotlightIntegration`. ([#1039](https://github.com/getsentry/sentry-capacitor/pull/1039)) diff --git a/example/ionic-angular-v7/src/app/app.module.ts b/example/ionic-angular-v7/src/app/app.module.ts index 8aeb2593..660c8d22 100644 --- a/example/ionic-angular-v7/src/app/app.module.ts +++ b/example/ionic-angular-v7/src/app/app.module.ts @@ -23,6 +23,12 @@ Sentry.init( // Whether SDK should be enabled or not enabled: true, // Use the tracing integration to see traces and add performance monitoring + _experiments: { + enableMetrics: true, + }, + beforeSendMetric: (metric) => { + return metric; + }, integrations: [ Sentry.browserTracingIntegration(), Sentry.replayIntegration({ diff --git a/src/options.ts b/src/options.ts index 595c71d8..2f074c4c 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,5 +1,5 @@ import type { BrowserOptions, makeFetchTransport } from '@sentry/browser'; -import type { ClientOptions } from '@sentry/core'; +import type { ClientOptions, Metric } from '@sentry/core'; import type { NuxtOptions, VueOptions } from './siblingOptions'; // Direct reference of BrowserTransportOptions is not compatible with strict builds of latest versions of Typescript 5. @@ -95,13 +95,44 @@ export interface BaseCapacitorOptions { */ nuxtClientOptions?: NuxtOptions; }; + + /** + * Options which are in beta, or otherwise not guaranteed to be stable. + */ + _experiments?: { + + /** + * If metrics support should be enabled. + * + * @default false + * @experimental + * @platforms web and iOS only. On the future it will be enabled on Android. + */ + enableMetrics?: boolean; + + /** + * An event-processing callback for metrics, guaranteed to be invoked after all other metric + * processors. This allows a metric to be modified or dropped before it's sent. + * + * Note that you must return a valid metric from this callback. If you do not wish to modify the metric, simply return + * it at the end. Returning `null` will cause the metric to be dropped. + * + * @default undefined + * @experimental + * @platforms web and iOS only. On the future it will be enabled on Android. + * + * @param metric The metric generated by the SDK. + * @returns A new metric that will be sent | null. + */ + beforeSendMetric?: (metric: Metric) => Metric | null; + }; } /** * Configuration options for the Sentry Capacitor SDK. */ export interface CapacitorOptions - extends Omit, + extends Omit, BaseCapacitorOptions { } // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -109,5 +140,5 @@ export interface CapacitorTransportOptions extends BrowserTransportOptions { } // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface CapacitorClientOptions - extends ClientOptions, + extends Omit, '_experiments' | 'enableMetrics'>, BaseCapacitorOptions { } diff --git a/src/sdk.ts b/src/sdk.ts index 03774c69..cd1475dd 100644 --- a/src/sdk.ts +++ b/src/sdk.ts @@ -80,6 +80,8 @@ finalOptions.transport = passedOptions.transport || makeNativeTransport; ...finalOptions, autoSessionTracking: NATIVE.platform === 'web' && finalOptions.enableAutoSessionTracking, + enableMetrics: finalOptions._experiments?.enableMetrics, + beforeSendMetric: finalOptions._experiments?.beforeSendMetric, } as BrowserOptions; const mobileOptions = { diff --git a/src/wrapper.ts b/src/wrapper.ts index 6cd9348d..997aea57 100644 --- a/src/wrapper.ts +++ b/src/wrapper.ts @@ -59,12 +59,7 @@ export const NATIVE = { : 'application/octet-stream'; bytesPayload = [...itemPayload]; } else { - // Check if the item header already has a content_type set - if (typeof itemHeader.content_type === 'string') { - bytesContentType = itemHeader.content_type; - } else { - bytesContentType = 'application/json'; - } + bytesContentType = typeof itemHeader.content_type === 'string' ? itemHeader.content_type : 'application/json'; bytesPayload = utf8ToBytes(JSON.stringify(itemPayload)); } diff --git a/test/sdk.test.ts b/test/sdk.test.ts index 6756969e..bf14bc47 100644 --- a/test/sdk.test.ts +++ b/test/sdk.test.ts @@ -143,6 +143,34 @@ describe('SDK Init', () => { }); }); + test('passes metrics experiments to browser options', () => { + NATIVE.platform = 'web'; + const mockOriginalInit = jest.fn(); + const beforeSendMetric = jest.fn(metric => metric); + + init({ + dsn: 'test-dsn', + enabled: true, + _experiments: { + enableMetrics: true, + beforeSendMetric, + }, + }, mockOriginalInit); + + // Wait for async operations + return new Promise(resolve => { + setTimeout(() => { + expect(mockOriginalInit).toHaveBeenCalled(); + const browserOptions = mockOriginalInit.mock.calls[0][0]; + + expect(browserOptions.enableMetrics).toBe(true); + expect(browserOptions.beforeSendMetric).toBe(beforeSendMetric); + + resolve(); + }, 10); + }); + }); + test('RewriteFrames to be added by default', async () => { NATIVE.platform = 'web'; init({ enabled: true }, async (capacitorOptions: CapacitorOptions) => { From cbf90426e3daebcdec3c295daefcfe5623be0ad5 Mon Sep 17 00:00:00 2001 From: lucas Date: Mon, 15 Dec 2025 13:03:44 +0000 Subject: [PATCH 4/6] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7ab19ce..8a5be76c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,7 +53,7 @@ Sentry.init({ ### Features -- Add experimental Metric support for Web and iOS. +- Add experimental Metric support for Web and iOS ([#1055](https://github.com/getsentry/sentry-capacitor/pull/1055)) for more information see doc page: - Add Fallback to JavaScript SDK when Native SDK fails to initialize ([#1043](https://github.com/getsentry/sentry-capacitor/pull/1043)) - Add spotlight integration `spotlightIntegration`. ([#1039](https://github.com/getsentry/sentry-capacitor/pull/1039)) From e83c389c6a084a58033dcff87d2eee94be6fb31c Mon Sep 17 00:00:00 2001 From: LucasZF Date: Mon, 15 Dec 2025 13:04:17 +0000 Subject: [PATCH 5/6] Update CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a5be76c..4a69a089 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,7 +54,6 @@ Sentry.init({ ### Features - Add experimental Metric support for Web and iOS ([#1055](https://github.com/getsentry/sentry-capacitor/pull/1055)) - for more information see doc page: - Add Fallback to JavaScript SDK when Native SDK fails to initialize ([#1043](https://github.com/getsentry/sentry-capacitor/pull/1043)) - Add spotlight integration `spotlightIntegration`. ([#1039](https://github.com/getsentry/sentry-capacitor/pull/1039)) From 81cb5ab78ac081993d010ee29862675320ec1355 Mon Sep 17 00:00:00 2001 From: lucas Date: Mon, 15 Dec 2025 15:24:14 +0000 Subject: [PATCH 6/6] move sample inside experimental --- example/ionic-angular-v7/src/app/app.module.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/example/ionic-angular-v7/src/app/app.module.ts b/example/ionic-angular-v7/src/app/app.module.ts index 660c8d22..37ef33d6 100644 --- a/example/ionic-angular-v7/src/app/app.module.ts +++ b/example/ionic-angular-v7/src/app/app.module.ts @@ -25,9 +25,9 @@ Sentry.init( // Use the tracing integration to see traces and add performance monitoring _experiments: { enableMetrics: true, - }, - beforeSendMetric: (metric) => { - return metric; + beforeSendMetric: (metric) => { + return metric; + }, }, integrations: [ Sentry.browserTracingIntegration(),