Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Sentry.init({

### Features

- Add experimental Metric support for Web and iOS ([#1055](https://github.com/getsentry/sentry-capacitor/pull/1055))
- 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))

Expand Down
4 changes: 4 additions & 0 deletions example/ionic-angular-v6/src/app/tab1/tab1.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,9 @@
Clear Test Context
<ion-button (click)="clearTestContext()">Clear Test Context</ion-button>
</label>
<label>
Create Metric
<ion-button (click)="createMetric()">Create Metric</ion-button>
</label>
</section>
</ion-content>
7 changes: 7 additions & 0 deletions example/ionic-angular-v6/src/app/tab1/tab1.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,11 @@ export class Tab1Page {
public clearTestContext(): void {
Sentry.setContext('TEST-CONTEXT', null);
}

public createMetric(): void {
// Create a metric using Sentry metrics API
Sentry.metrics.count('test.metric.counter', 1,
{ attributes: { from_test_app: true } },
);
}
}
6 changes: 6 additions & 0 deletions example/ionic-angular-v7/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
4 changes: 4 additions & 0 deletions example/ionic-angular-v7/src/app/tab1/tab1.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,9 @@
Clear Test Context
<ion-button (click)="clearTestContext()">Clear Test Context</ion-button>
</label>
<label>
Create Metric
<ion-button (click)="createMetric()">Create Metric</ion-button>
</label>
</section>
</ion-content>
7 changes: 7 additions & 0 deletions example/ionic-angular-v7/src/app/tab1/tab1.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,11 @@ export class Tab1Page {
public clearTestContext(): void {
Sentry.setContext('TEST-CONTEXT', null);
}

public createMetric(): void {
// Create a metric using Sentry metrics API
Sentry.metrics.count('test.metric.counter', 1,
{ attributes: { from_test_app: true } },
);
}
}
59 changes: 40 additions & 19 deletions example/ionic-vue3/src/views/HomePage.vue
Original file line number Diff line number Diff line change
@@ -1,43 +1,64 @@
<!-- SentryCapacitorVue.vue -->
<script lang="ts">
import { IonButton, IonContent, IonFooter, IonHeader, IonTitle, IonToolbar, IonPage } from '@ionic/vue';
import {
IonButton,
IonContent,
IonFooter,
IonHeader,
IonTitle,
IonToolbar,
IonPage,
} from '@ionic/vue';

import * as Sentry from "@sentry/capacitor"
import * as Sentry from '@sentry/capacitor';

export default {
export default {
name: 'HomePageView',
components: { IonButton, IonContent, IonFooter, IonHeader, IonTitle, IonToolbar, IonPage },
components: {
IonButton,
IonContent,
IonFooter,
IonHeader,
IonTitle,
IonToolbar,
IonPage,
},
methods: {
createMessage() {
Sentry.captureMessage('Hello VUE');
},
handledError() {
try {
throw Error("A handled error");
throw Error('A handled error');
} catch (ex) {
Sentry.captureException(ex);
}
},
crash() {
Sentry.nativeCrash();
}
}
},
createMetric() {
// Create a metric using Sentry metrics API
Sentry.metrics.count('test.metric.counter', 1, {
attributes: { from_test_app: true },
});
},
},
};


</script>

<template>
<ion-page>
<ion-header :translucent="true">
<ion-toolbar>
<ion-title>Sentry Capacitor Vue</ion-title>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true" class="ion-padding">
<ion-button @click="createMessage">Create Message</ion-button>
<ion-button @click="handledError">Handled Error</ion-button>
<ion-button @click="crash">Crash</ion-button>
</ion-content>
<ion-header :translucent="true">
<ion-toolbar>
<ion-title>Sentry Capacitor Vue</ion-title>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true" class="ion-padding">
<ion-button @click="createMessage">Create Message</ion-button>
<ion-button @click="handledError">Handled Error</ion-button>
<ion-button @click="crash">Crash</ion-button>
<ion-button @click="createMetric">Create Metric</ion-button>
</ion-content>
</ion-page>
</template>
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type {
Stacktrace,
Thread,
User,
Metric
} from '@sentry/core';

export type { Scope } from '@sentry/core';
Expand Down Expand Up @@ -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';

Expand Down
37 changes: 34 additions & 3 deletions src/options.ts
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -95,19 +95,50 @@ 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<BrowserOptions, '_experiments'>,
extends Omit<BrowserOptions, '_experiments' | 'enableMetrics'>,
BaseCapacitorOptions { }

// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface CapacitorTransportOptions extends BrowserTransportOptions { }

// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface CapacitorClientOptions
extends ClientOptions<CapacitorTransportOptions>,
extends Omit<ClientOptions<CapacitorTransportOptions>, '_experiments' | 'enableMetrics'>,
BaseCapacitorOptions { }
2 changes: 2 additions & 0 deletions src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
2 changes: 1 addition & 1 deletion src/wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const NATIVE = {
: 'application/octet-stream';
bytesPayload = [...itemPayload];
} else {
bytesContentType = 'application/vnd.sentry.items.log+json';
bytesContentType = typeof itemHeader.content_type === 'string' ? itemHeader.content_type : 'application/json';
bytesPayload = utf8ToBytes(JSON.stringify(itemPayload));
}

Expand Down
122 changes: 122 additions & 0 deletions test/helper/envelopeHelper.ts
Original file line number Diff line number Diff line change
@@ -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<SerializedMetric>): 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<SerializedLog>): 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<SerializedLog>,
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<LogEnvelope>(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<SerializedMetric>,
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<MetricEnvelope>(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;
}

Loading
Loading