feat(nc-desktop): add error reporting and monitoring setting (#3553)

This commit is contained in:
Pierre Dommerc
2023-06-15 19:23:23 +02:00
committed by pierre
parent dd33052f0e
commit 4071d30f3c
14 changed files with 252 additions and 21 deletions
+3 -1
View File
@@ -28,11 +28,13 @@
"@emotion/styled": "^11.6.0",
"@hookform/resolvers": "^2.8.0",
"@mui/icons-material": "^5.2.0",
"@mui/lab": "^5.0.0-alpha.72",
"@mui/material": "^5.2.2",
"@mui/styles": "^5.2.2",
"@mui/system": ">= 5",
"@mui/lab": "^5.0.0-alpha.72",
"@nymproject/react": "^1.0.0",
"@sentry/integrations": "^7.54.0",
"@sentry/react": "^7.54.0",
"@tauri-apps/api": "^1.2.0",
"@tauri-apps/tauri-forage": "^1.0.0-beta.2",
"clsx": "^1.1.1",
@@ -40,6 +40,7 @@ fn main() {
.invoke_handler(tauri::generate_handler![
crate::operations::config::get_config_file_location,
crate::operations::config::get_config_id,
crate::operations::common::get_env,
crate::operations::connection::connect::get_gateway,
crate::operations::connection::connect::get_service_provider,
crate::operations::connection::connect::set_gateway,
@@ -0,0 +1,9 @@
use std::env;
#[tauri::command]
pub async fn get_env(variable: String) -> Option<String> {
let var = env::var(&variable).ok();
log::trace!("get_env {variable} {:?}", var);
var
}
@@ -1,3 +1,4 @@
pub mod common;
pub mod config;
pub mod connection;
pub mod directory;
+31 -2
View File
@@ -9,9 +9,11 @@ import { getItemFromStorage, setItemInStorage } from 'src/utils';
import { ConnectionStatusKind, GatewayPerformance } from '../types';
import { ConnectionStatsItem } from '../components/ConnectionStats';
import { ServiceProvider } from '../types/directory';
import initSentry from '../sentry';
const FORAGE_GATEWAY_KEY = 'nym-connect-user-gateway';
const FORAGE_SP_KEY = 'nym-connect-user-sp';
const FORAGE_MONITORING_ENABLED = 'nym-connect-monitoring-enabled';
type ModeType = 'light' | 'dark';
@@ -30,6 +32,7 @@ export type TClientContext = {
serviceProviders?: ServiceProvider[];
setMode: (mode: ModeType) => void;
clearError: () => void;
monitoringEnabled: boolean;
setConnectionStatus: (connectionStatus: ConnectionStatusKind) => void;
setConnectionStats: (connectionStats: ConnectionStatsItem[] | undefined) => void;
setConnectedSince: (connectedSince: DateTime | undefined) => void;
@@ -39,6 +42,7 @@ export type TClientContext = {
startDisconnecting: () => Promise<void>;
setUserDefinedGateway: React.Dispatch<React.SetStateAction<UserDefinedGateway>>;
setUserDefinedSPAddress: React.Dispatch<React.SetStateAction<UserDefinedSPAddress>>;
setMonitoring: (value: boolean) => Promise<void>;
};
export const ClientContext = createContext({} as TClientContext);
@@ -62,12 +66,25 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
isActive: false,
address: undefined,
});
const [monitoringEnabled, setMonitoringEnabled] = useState(false);
const getAppVersion = async () => {
const version = await getVersion();
return version;
};
useEffect(() => {
const initSentryClient = async () => {
const monitoring = await getItemFromStorage({ key: FORAGE_MONITORING_ENABLED });
setMonitoringEnabled(Boolean(monitoring));
if (monitoring === true) {
await initSentry();
}
};
initSentryClient();
}, []);
useEffect(() => {
setItemInStorage({ key: FORAGE_GATEWAY_KEY, value: userDefinedGateway });
}, [userDefinedGateway]);
@@ -85,8 +102,12 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
setAppVersion(AppVersion);
setServiceProviders(services as ServiceProvider[]);
if (storedUserDefinedGateway) setUserDefinedGateway(storedUserDefinedGateway);
if (storedUserDefinedSP) setUserDefinedSPAddress(storedUserDefinedSP);
if (storedUserDefinedGateway) {
setUserDefinedGateway(storedUserDefinedGateway);
}
if (storedUserDefinedSP) {
setUserDefinedSPAddress(storedUserDefinedSP);
}
};
useEvents({
@@ -162,6 +183,11 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
const clearError = () => setError(undefined);
const setMonitoring = async (value: boolean) => {
setMonitoringEnabled(value);
await setItemInStorage({ key: FORAGE_MONITORING_ENABLED, value });
};
const contextValue = useMemo(
() => ({
mode,
@@ -177,6 +203,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
selectedProvider,
serviceProviders,
connectedSince,
monitoringEnabled,
setConnectedSince,
setSerivceProvider,
startConnecting,
@@ -187,6 +214,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
userDefinedGateway,
setUserDefinedGateway,
setUserDefinedSPAddress,
setMonitoring,
}),
[
mode,
@@ -202,6 +230,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
selectedProvider,
userDefinedGateway,
userDefinedSPAddress,
monitoringEnabled,
],
);
@@ -11,6 +11,7 @@ const mockValues: TClientContext = {
showInfoModal: false,
userDefinedGateway: { isActive: false, gateway: '' },
userDefinedSPAddress: { isActive: false, address: '' },
monitoringEnabled: false,
setShowInfoModal: () => {},
setMode: () => {},
clearError: () => {},
@@ -22,6 +23,7 @@ const mockValues: TClientContext = {
setSerivceProvider: () => {},
setUserDefinedGateway: () => {},
setUserDefinedSPAddress: () => {},
setMonitoring: async () => {},
};
export const MockProvider: FCWithChildren<{
@@ -0,0 +1,52 @@
import React, { ChangeEvent, useState } from 'react';
import { Warning as WarningIcon } from '@mui/icons-material';
import { Box, FormControl, FormControlLabel, FormHelperText, Stack, Switch, Typography } from '@mui/material';
import { useClientContext } from 'src/context/main';
export const MonitoringSettings = () => {
const { monitoringEnabled, setMonitoring } = useClientContext();
const [enabled, setEnabled] = useState(monitoringEnabled);
const [loading, setLoading] = useState(false);
const handleChange = async (e: ChangeEvent<HTMLInputElement>) => {
setLoading(true);
setEnabled(e.target.checked);
await setMonitoring(e.target.checked);
setLoading(false);
};
return (
<Box height="100%">
<Stack justifyContent="space-between" height="100%">
<Box>
<Typography fontWeight="bold" variant="body2" mb={2}>
Error reporting and performance monitoring
</Typography>
<FormControl fullWidth>
<FormControlLabel
control={
<Switch checked={enabled} onChange={handleChange} disabled={loading} size="small" sx={{ ml: 1 }} />
}
label="Enable"
/>
<FormHelperText sx={{ m: 0, my: 2 }}>
Help Nym developers to fix errors, crashes and improve the application by enabling this option. If errors
occur or if the app crashes, it will automatically send a report. Also it tracks various performance
metrics. We use sentry.io service to handle this.
</FormHelperText>
<FormHelperText sx={{ m: 0, mb: 2 }}>
Note: A report can include your external IP, this can be useful to catch issues related to IP location.
All recorded data is used by Nym developers and for app development purposes only.
</FormHelperText>
</FormControl>
<Stack direction="row" gap={1} alignItems="center">
<WarningIcon color="warning" fontSize="small" />
<Typography variant="caption" color="warning.main">
You must restart the application for the change to take effect.
</Typography>
</Stack>
</Box>
</Stack>
</Box>
);
};
@@ -7,6 +7,7 @@ import { toggleLogViewer } from 'src/utils';
const menuSchema = [
{ title: 'Select your gateway', path: 'gateway' },
{ title: 'Select a service provider', path: 'service-provider' },
{ title: 'Help us improve the app', path: 'monitoring' },
];
export const SettingsMenu = () => (
+26 -14
View File
@@ -1,5 +1,6 @@
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import * as Sentry from '@sentry/react';
import { ConnectionPage } from 'src/pages/connection';
import { Menu } from 'src/pages/menu';
import { CompatibleApps } from 'src/pages/menu/Apps';
@@ -7,19 +8,30 @@ import { HelpGuide } from 'src/pages/menu/Guide';
import { SettingsMenu } from 'src/pages/menu/settings';
import { GatewaySettings } from 'src/pages/menu/settings/GatewaySettings';
import { ServiceProviderSettings } from 'src/pages/menu/settings/ServiceProviderSettings';
import { MonitoringSettings } from '../pages/menu/settings/MonitoringSettings';
import { useClientContext } from '../context/main';
export const AppRoutes = () => (
<Routes>
<Route index path="/" element={<ConnectionPage />} />
<Route path="menu">
<Route index element={<Menu />} />
<Route path="apps" element={<CompatibleApps />} />
<Route path="guide" element={<HelpGuide />} />
<Route path="settings">
<Route index element={<SettingsMenu />} />
<Route path="gateway" element={<GatewaySettings />} />
<Route path="service-provider" element={<ServiceProviderSettings />} />
const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes);
export const AppRoutes = () => {
const { monitoringEnabled } = useClientContext();
const RoutesContainer = monitoringEnabled ? SentryRoutes : Routes;
return (
<RoutesContainer>
<Route index path="/" element={<ConnectionPage />} />
<Route path="menu">
<Route index element={<Menu />} />
<Route path="apps" element={<CompatibleApps />} />
<Route path="guide" element={<HelpGuide />} />
<Route path="settings">
<Route index element={<SettingsMenu />} />
<Route path="gateway" element={<GatewaySettings />} />
<Route path="service-provider" element={<ServiceProviderSettings />} />
<Route path="monitoring" element={<MonitoringSettings />} />
</Route>
</Route>
</Route>
</Routes>
);
</RoutesContainer>
);
};
+48
View File
@@ -0,0 +1,48 @@
import React from 'react';
import { createRoutesFromChildren, matchRoutes, useLocation, useNavigationType } from 'react-router-dom';
import * as Sentry from '@sentry/react';
import { CaptureConsole } from '@sentry/integrations';
import { getVersion } from '@tauri-apps/api/app';
const SENTRY_DSN = 'https://625e2658da4945a7a253f3ee04413a31@o967446.ingest.sentry.io/4505306292289536';
async function initSentry() {
console.log('⚠ performance monitoring and error reporting enabled');
console.log('initializing sentry');
Sentry.init({
dsn: SENTRY_DSN,
integrations: [
new Sentry.BrowserTracing({
// Set `tracePropagationTargets` to control for which URLs distributed tracing should be enabled
tracePropagationTargets: ['localhost'],
routingInstrumentation: Sentry.reactRouterV6Instrumentation(
React.useEffect,
useLocation,
useNavigationType,
createRoutesFromChildren,
matchRoutes,
),
}),
new Sentry.Replay(),
// captures Console API calls
new CaptureConsole({ levels: ['error', 'warn'] }),
],
// TODO adjust this in the future, 100% is not recommended for production
tracesSampleRate: 1.0,
// Capture Replay for 10% of all sessions,
// plus for 100% of sessions with an error
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
environment: process.env.NODE_ENV,
});
getVersion().then((version) => {
Sentry.setTag('app_version', version);
});
}
export default initSentry;
+1 -1
View File
@@ -121,4 +121,4 @@ dependencies {
debugImplementation 'androidx.compose.ui:ui-tooling'
debugImplementation 'androidx.compose.ui:ui-test-manifest'
implementation 'com.github.kittinunf.fuel:fuel:3.0.0-alpha1'
}
}
@@ -36,4 +36,4 @@
tools:node="remove" />
</application>
</manifest>
</manifest>
+1 -1
View File
@@ -122,4 +122,4 @@
"webpack-favicons": "^1.3.8",
"webpack-merge": "^5.8.0"
}
}
}
+75 -1
View File
@@ -3042,6 +3042,80 @@
resolved "https://registry.yarnpkg.com/@sapphire/utilities/-/utilities-3.11.1.tgz#75d068fe473d9cf8483156ae731bce92fa39a2ef"
integrity sha512-SO7WvlPc0QEB+bCT6Z/NGpYTpOtYAJtxpxKV4YU+agEGlMZ5hM8J/xkco4/yFWH7X2gAcG4Ckoi2shFr7CMS/A==
"@sentry-internal/tracing@7.54.0":
version "7.54.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.54.0.tgz#eeb10ee72426d08669a7706faa4264f1ec02c71d"
integrity sha512-JsyhZ0wWZ+VqbHJg+azqRGdYJDkcI5R9+pnkO6SzbzxrRewqMAIwzkpPee3oI7vG99uhMEkOkMjHu0nQGwkOQw==
dependencies:
"@sentry/core" "7.54.0"
"@sentry/types" "7.54.0"
"@sentry/utils" "7.54.0"
tslib "^1.9.3"
"@sentry/browser@7.54.0":
version "7.54.0"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.54.0.tgz#7fe331c776d02b5c902733aa41dcbfac7bef1ae6"
integrity sha512-EvLAw03N9WE2m1CMl2/1YMeIs1icw9IEOVJhWmf3uJEysNJOFWXu6ZzdtHEz1E6DiJYhc1HzDya0ExZeJxNARA==
dependencies:
"@sentry-internal/tracing" "7.54.0"
"@sentry/core" "7.54.0"
"@sentry/replay" "7.54.0"
"@sentry/types" "7.54.0"
"@sentry/utils" "7.54.0"
tslib "^1.9.3"
"@sentry/core@7.54.0":
version "7.54.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.54.0.tgz#8c4cb8800f8df708b3f3f6483026bb9a02820014"
integrity sha512-MAn0E2EwgNn1pFQn4qxhU+1kz6edullWg6VE5wCmtpXWOVw6sILBUsQpeIG5djBKMcneJCdOlz5jeqcKPrLvZQ==
dependencies:
"@sentry/types" "7.54.0"
"@sentry/utils" "7.54.0"
tslib "^1.9.3"
"@sentry/integrations@^7.54.0":
version "7.54.0"
resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.54.0.tgz#62c73013ca6040d0c9b045809fc5d6ecefda3339"
integrity sha512-RolGsQzJChJzjHTJcCKSZ1HanmY33floc5o13WgU9NoDqJbLGLNcOIrAu+WynqPe8P5VTVrVb8NiwhLqWrKp4g==
dependencies:
"@sentry/types" "7.54.0"
"@sentry/utils" "7.54.0"
localforage "^1.8.1"
tslib "^1.9.3"
"@sentry/react@^7.54.0":
version "7.54.0"
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.54.0.tgz#0d9e1b902fd9ded713ac46a623f6a490e4aa2c8a"
integrity sha512-qUbwmRRpTh05m2rbC8A2zAFQYsoHhwIpxT5UXxh0P64ZlA3cSg1/DmTTgwnd1l+7gzKrc31UikXQ4y0YDbMNKg==
dependencies:
"@sentry/browser" "7.54.0"
"@sentry/types" "7.54.0"
"@sentry/utils" "7.54.0"
hoist-non-react-statics "^3.3.2"
tslib "^1.9.3"
"@sentry/replay@7.54.0":
version "7.54.0"
resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.54.0.tgz#f0f44f9413ceefd1809bf1665e82315927ae08db"
integrity sha512-C0F0568ybphzGmKGe23duB6n5wJcgM7WLYhoeqW3o2bHeqpj1dGPSka/K3s9KzGaAgzn1zeOUYXJsOs+T/XdsA==
dependencies:
"@sentry/core" "7.54.0"
"@sentry/types" "7.54.0"
"@sentry/utils" "7.54.0"
"@sentry/types@7.54.0":
version "7.54.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.54.0.tgz#bfee18107a78e290e6c8ad41646e2b9d9dd95234"
integrity sha512-D+i9xogBeawvQi2r0NOrM7zYcUaPuijeME4O9eOTrDF20tj71hWtJLilK+KTGLYFtpGg1h+9bPaz7OHEIyVopg==
"@sentry/utils@7.54.0":
version "7.54.0"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.54.0.tgz#a3acb5e25a1409cbca7b46d6356d7417a253ea9a"
integrity sha512-3Yf5KlKjIcYLddOexSt2ovu2TWlR4Fi7M+aCK8yUTzwNzf/xwFSWOstHlD/WiDy9HvfhWAOB/ukNTuAeJmtasw==
dependencies:
"@sentry/types" "7.54.0"
tslib "^1.9.3"
"@sigstore/protobuf-specs@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz#957cb64ea2f5ce527cc9cf02a096baeb0d2b99b4"
@@ -12987,7 +13061,7 @@ loader-utils@^2.0.0, loader-utils@^2.0.4:
emojis-list "^3.0.0"
json5 "^2.1.2"
localforage@^1.7.3:
localforage@^1.7.3, localforage@^1.8.1:
version "1.10.0"
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4"
integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==