Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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