Skip to content

Commit

Permalink
feat: enable setting logging level in the application
Browse files Browse the repository at this point in the history
- create `DEFAULT_LOG_LEVEL` env variable for centralize logging level selection
- ensure logging level persist across session using background storage
- add logging level to global state and created `useLogger` to easily share the state
  • Loading branch information
greatertomi committed Jan 30, 2025
1 parent 87b1005 commit 002e672
Show file tree
Hide file tree
Showing 22 changed files with 266 additions and 24 deletions.
2 changes: 2 additions & 0 deletions apps/browser-extension-wallet/.env.defaults
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ ADA_PRICE_POLLING_IN_SEC=60
TOKEN_PRICE_POLLING_IN_SEC=300
SAVED_PRICE_DURATION_IN_MINUTES=720
WALLET_POLLING_INTERVAL_IN_SEC=45
# options for log level: trace, debug, info, warn, error
DEFAULT_LOG_LEVEL=info

# Feature Flags
USE_PASSWORD_VERIFICATION=false
Expand Down
2 changes: 2 additions & 0 deletions apps/browser-extension-wallet/.env.developerpreview
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ AVAILABLE_CHAINS=Sanchonet
ADA_PRICE_POLLING_IN_SEC=60
TOKEN_PRICE_POLLING_IN_SEC=300
SAVED_PRICE_DURATION_IN_MINUTES=720
# options for log level: trace, debug, info, warn, error
DEFAULT_LOG_LEVEL=info

# Feature Flags
USE_PASSWORD_VERIFICATION=false
Expand Down
2 changes: 2 additions & 0 deletions apps/browser-extension-wallet/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ ADA_PRICE_POLLING_IN_SEC=60
TOKEN_PRICE_POLLING_IN_SEC=300
SAVED_PRICE_DURATION_IN_MINUTES=720
WALLET_POLLING_INTERVAL_IN_SEC=45
# options for log level: trace, debug, info, warn, error
DEFAULT_LOG_LEVEL=info

# Feature Flags
USE_PASSWORD_VERIFICATION=false
Expand Down
2 changes: 1 addition & 1 deletion apps/browser-extension-wallet/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "$WALLET_MANIFEST_NAME",
"description": "One fast, accessible, and secure platform for digital assets, DApps, NFTs, and DeFi.",
"version": "1.19.1",
"version": "1.19.2",
"manifest_version": 3,
"key": "$LACE_EXTENSION_KEY",
"icons": {
Expand Down
5 changes: 4 additions & 1 deletion apps/browser-extension-wallet/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BlockfrostClientConfig } from '@cardano-sdk/cardano-services-client';
import { Milliseconds } from '@cardano-sdk/core';
import { Wallet } from '@lace/cardano';
import { EnvironmentTypes } from '@stores';
import { LogLevelString } from '@lace/common';

type ByNetwork<T> = {
[key in Wallet.ChainName]: T;
Expand Down Expand Up @@ -34,6 +35,7 @@ export type Config = {
DEFAULT_SUBMIT_API: string;
GOV_TOOLS_URLS: Record<EnvironmentTypes, string>;
SESSION_TIMEOUT: Milliseconds;
DEFAULT_LOG_LEVEL: LogLevelString;
};

// eslint-disable-next-line complexity
Expand Down Expand Up @@ -150,6 +152,7 @@ export const config = (): Config => {
!Number.isNaN(Number.parseInt(process.env.SESSION_TIMEOUT))
? Number.parseInt(process.env.SESSION_TIMEOUT)
: 1000 * 60 * 5
)
),
DEFAULT_LOG_LEVEL: process.env.DEFAULT_LOG_LEVEL as LogLevelString
};
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import React from 'react';
import { ContentLayout } from '@components/Layout';
import { useTranslation } from 'react-i18next';
import { SettingsWallet, SettingsSecurity, SettingsHelp, SettingsLegal, SettingsPreferences } from '..';
import {
SettingsWallet,
SettingsSecurity,
SettingsHelp,
SettingsLegal,
SettingsPreferences,
SettingsLogging
} from '..';
import { SettingsRemoveWallet } from '@src/views/browser-view/features/settings/components/SettingsRemoveWallet';
import { SettingsSwitchToNami } from '@src/views/browser-view/features/settings/components/SettingsSwitchToNami';
import { usePostHogClientContext } from '@providers/PostHogClientProvider';
Expand Down Expand Up @@ -30,6 +37,7 @@ export const Settings = ({ defaultPassphraseVisible, defaultMnemonic }: Settings
/>
<SettingsHelp popupView />
<SettingsLegal />
<SettingsLogging popupView />
{useSwitchToNamiMode && !isSharedWallet && <SettingsSwitchToNami popupView />}
<SettingsRemoveWallet popupView />
</div>
Expand Down
1 change: 1 addition & 0 deletions apps/browser-extension-wallet/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ export * from './useActionExecution';
export * from './useCustomSubmitApi';
export * from './useSharedWalletData';
export * from './useHasEnoughCollateral';
export * from './useLogger';
28 changes: 28 additions & 0 deletions apps/browser-extension-wallet/src/hooks/useLogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useEffect } from 'react';
import { LogLevelString } from '@lace/common';
import { getBackgroundStorage, setBackgroundStorage } from '@lib/scripts/background/storage';
import { useWalletStore } from '@stores';

type UseLoggerReturnType = {
currentLogLevel: LogLevelString;
updateLogLevel: (logLevel: LogLevelString) => Promise<void>;
};

export const useLogger = (): UseLoggerReturnType => {
const { currentLogLevel, setCurrentLogLevel } = useWalletStore();

useEffect(() => {
(async () => {
await getBackgroundStorage().then((res) => {
setCurrentLogLevel(res.logLevel);
});
})();
}, [setCurrentLogLevel]);

const updateLogLevel = async (logLevel: LogLevelString) => {
setCurrentLogLevel(logLevel);
await setBackgroundStorage({ logLevel });
};

return { currentLogLevel, updateLogLevel };
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getBaseUrlForChain, getMagicForChain } from '@src/utils/chain';
import { BackgroundService, UserIdService as UserIdServiceInterface } from '../types';
import { getBackgroundStorage } from '@lib/scripts/background/storage';
import { ExperimentName } from '@providers/ExperimentsProvider/types';
import { logger } from '@lace/common';
import { LogLevelString, StringifyLogger } from '@lace/common';
import { config } from '@src/config';
import Bottleneck from 'bottleneck';
import { RateLimiter } from '@cardano-sdk/cardano-services-client';
Expand Down Expand Up @@ -40,10 +40,14 @@ export const rateLimiter: RateLimiter = new Bottleneck({
reservoirIncreaseMaximum: BLOCKFROST_RATE_LIMIT_CONFIG.size
});

export const getProviders = async (chainName: Wallet.ChainName): Promise<Wallet.WalletProvidersDependencies> => {
export const getProviders = async (
chainName: Wallet.ChainName,
logLevel: LogLevelString
): Promise<Wallet.WalletProvidersDependencies> => {
const baseCardanoServicesUrl = getBaseUrlForChain(chainName);
const magic = getMagicForChain(chainName);
const { customSubmitTxUrl, featureFlags } = await getBackgroundStorage();
const logger = new StringifyLogger(logLevel);

const isExperimentEnabled = (experimentName: ExperimentName) => !!(featureFlags?.[magic]?.[experimentName] ?? false);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { createBackgroundMessenger } from '@cardano-sdk/web-extension';
import { logger } from '@lace/common';
import { LogLevelString, StringifyLogger } from '@lace/common';
import { TRACK_POPUP_CHANNEL } from '@src/utils/constants';
import { distinctUntilChanged, map, share } from 'rxjs';
import { runtime } from 'webextension-polyfill';

const logger = new StringifyLogger(process.env.DEFAULT_LOG_LEVEL as LogLevelString);

const channel = createBackgroundMessenger({ logger, runtime }).getChannel(TRACK_POPUP_CHANNEL);
export const isLacePopupOpen$ = channel.ports$.pipe(
map((ports) => ports.size > 0),
Expand Down
25 changes: 23 additions & 2 deletions apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ import axiosFetchAdapter from '@shiroyasha9/axios-fetch-adapter';
import { SharedWalletScriptKind } from '@lace/core';
import { getBaseUrlForChain, getMagicForChain } from '@utils/chain';
import { cacheNamiMetadataSubscription } from './cache-nami-metadata';
import { logger } from '@lace/common';
import { getBackgroundStorage } from '@lib/scripts/background/storage';
import { ExperimentName } from '@providers/ExperimentsProvider/types';
import { requestMessage$ } from './services/utilityServices';
Expand All @@ -48,9 +47,12 @@ import { ExtensionDocumentStore } from './storage/extension-document-store';
import { ExtensionBlobKeyValueStore } from './storage/extension-blob-key-value-store';
import { ExtensionBlobCollectionStore } from './storage/extension-blob-collection-store';
import { migrateCollectionStore, migrateWalletStores, shouldAttemptWalletStoresMigration } from './storage/migrations';
import { StringifyLogger, LogLevelString } from '@lace/common';
import { isLacePopupOpen$, createUserSessionTracker, isLaceTabActive$ } from './session';
import { TrackerSubject } from '@cardano-sdk/util-rxjs';

const logger = new StringifyLogger(process.env.DEFAULT_LOG_LEVEL as LogLevelString);

export const dAppConnectorActivity$ = new Subject<void>();
const pollController$ = new TrackerSubject(
createUserSessionTracker(isLacePopupOpen$, isLaceTabActive$, dAppConnectorActivity$, SESSION_TIMEOUT).pipe(
Expand Down Expand Up @@ -123,7 +125,17 @@ const walletFactory: WalletFactory<Wallet.WalletMetadata, Wallet.AccountMetadata
// eslint-disable-next-line complexity, max-statements
create: async ({ chainId, accountIndex }, wallet, { stores, witnesser }) => {
const chainName: Wallet.ChainName = networkMagicToChainName(chainId.networkMagic);
let providers = await getProviders(chainName);
webStorage.onChanged.addListener((changes) => {
const oldLogLevelValue = changes.BACKGROUND_STORAGE?.oldValue?.logLevel;
const newLogLevelValue = changes.BACKGROUND_STORAGE?.newValue?.logLevel;
if (oldLogLevelValue !== newLogLevelValue) {
logger.setLogLevel(newLogLevelValue);
}
});

const backgroundStorage = await getBackgroundStorage();
const logLevel = backgroundStorage?.logLevel || (process.env.DEFAULT_LOG_LEVEL as LogLevelString);
let providers = await getProviders(chainName, logLevel);

const baseUrl = getBaseUrlForChain(chainName);

Expand Down Expand Up @@ -327,6 +339,15 @@ export const extensionStorageStoresFactory: StoresFactory = {

const storesFactory: StoresFactory = {
async create(props) {
// eslint-disable-next-line sonarjs/no-identical-functions
webStorage.onChanged.addListener((changes) => {
const oldLogLevelValue = changes.BACKGROUND_STORAGE?.oldValue?.logLevel;
const newLogLevelValue = changes.BACKGROUND_STORAGE?.newValue?.logLevel;
if (oldLogLevelValue !== newLogLevelValue) {
logger.setLogLevel(newLogLevelValue);
}
});

const extensionStores = await extensionStorageStoresFactory.create(props);
if (await shouldAttemptWalletStoresMigration(extensionStores)) {
const pouchdbStores = await pouchdbStoresFactory.create(props);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AuthorizedDappStorage } from '@src/types/dappConnector';
import type { Message } from './background-service';
import { ADAPrices } from './prices';
import { ExperimentName } from '@providers/ExperimentsProvider/types';
import { LogLevelString } from '@lace/common';

export interface PendingMigrationState {
from: string;
Expand Down Expand Up @@ -37,6 +38,7 @@ export interface BackgroundStorage {
};
dappInjectCompatibilityMode?: boolean;
optedInBeta?: boolean;
logLevel?: LogLevelString;
}

export type BackgroundStorageKeys = keyof BackgroundStorage;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Wallet } from '@lace/cardano';
import { ObservableWalletState } from '@hooks/useWalletState';
import { isSharedWallet } from '@lace/core';
import { isNamiWallet } from '@src/views/nami-mode/utils';
import { LogLevelString } from '@lace/common';

/**
* has all wallet info related actions and states
Expand Down Expand Up @@ -49,5 +50,7 @@ export const walletInfoSlice: SliceCreator<WalletInfoSlice & BlockchainProviderS
get().setBlockchainProvider(chain);
},
setDeletingWallet: (deletingWallet: boolean) => set({ deletingWallet }),
setStayOnAllDonePage: (stayOnAllDonePage: boolean) => set({ stayOnAllDonePage })
setStayOnAllDonePage: (stayOnAllDonePage: boolean) => set({ stayOnAllDonePage }),
currentLogLevel: undefined,
setCurrentLogLevel: (logLevel: LogLevelString) => set({ currentLogLevel: logLevel })
});
3 changes: 3 additions & 0 deletions apps/browser-extension-wallet/src/stores/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { AddressesDiscoveryStatus } from '@lib/communication/addresses-discovere
import { Cardano, Reward } from '@cardano-sdk/core';
import { StakePoolSortOptions } from '@lace/staking';
import { ObservableWalletState } from '@hooks/useWalletState';
import { LogLevelString } from '@lace/common';

export enum StateStatus {
IDLE = 'idle',
Expand Down Expand Up @@ -122,6 +123,8 @@ export interface WalletInfoSlice {
stayOnAllDonePage?: boolean;
setDeletingWallet: (deletingWallet: boolean) => void;
setStayOnAllDonePage: (deletingWallet: boolean) => void;
currentLogLevel: LogLevelString;
setCurrentLogLevel: (logLevel: string) => void;
}

export interface LockSlice {
Expand Down
2 changes: 2 additions & 0 deletions apps/browser-extension-wallet/src/utils/mocks/store.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ export const walletStoreMock = async (
setDeletingWallet: jest.fn(),
hdDiscoveryStatus: AddressesDiscoveryStatus.Idle,
setHdDiscoveryStatus: jest.fn(),
currentLogLevel: 'info',
setCurrentLogLevel: jest.fn(),
...customStore
};
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import React from 'react';
import { SettingsWallet, SettingsSecurity, SettingsLegal, SettingsAbout, SettingsHelp, SettingsPreferences } from './';
import {
SettingsWallet,
SettingsSecurity,
SettingsLegal,
SettingsAbout,
SettingsHelp,
SettingsPreferences,
SettingsLogging
} from './';
import { PageTitle } from '@components/Layout';
import { useTranslation } from 'react-i18next';
import { Layout, SectionLayout } from '@src/views/browser-view/components/Layout';
Expand Down Expand Up @@ -45,6 +53,7 @@ export const SettingsLayout = ({
<SettingsSecurity defaultPassphraseVisible={defaultPassphraseVisible} defaultMnemonic={defaultMnemonic} />
<SettingsHelp />
<SettingsLegal />
<SettingsLogging />
{useSwitchToNamiMode && !isSharedWallet && <SettingsSwitchToNami />}
<SettingsRemoveWallet />
</SectionLayout>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React from 'react';
import { Radio, RadioChangeEvent, Typography } from 'antd';
import { useTranslation } from 'react-i18next';
import { Drawer, DrawerHeader, DrawerNavigation, LogLevel, LogLevelString, toast } from '@lace/common';
import styles from '../SettingsLayout.module.scss';
import SwitchIcon from '@assets/icons/edit.component.svg';
import capitalize from 'lodash/capitalize';
import { useLogger } from '@hooks';

const { Text } = Typography;

interface LoggingDrawerProps {
visible: boolean;
onClose: () => void;
popupView?: boolean;
}

const logLevels: LogLevelString[] = Object.keys(LogLevel).filter((key) =>
Number.isNaN(Number(key))
) as LogLevelString[];

export const LoggingDrawer = ({ visible, onClose, popupView = false }: LoggingDrawerProps): React.ReactElement => {
const { t } = useTranslation();
const { currentLogLevel, updateLogLevel } = useLogger();

const handleLoggingChange = async (event: RadioChangeEvent) => {
const value = event.target.value as LogLevelString;
await updateLogLevel(value);
toast.notify({
text: 'Logging level has been updated',
withProgressBar: true,
icon: SwitchIcon
});
};

return (
<Drawer
visible={visible}
onClose={onClose}
title={
<DrawerHeader
title="Logging Level"
subtitle={!popupView ? 'This is the logging level for the extension' : undefined}
popupView={popupView}
/>
}
navigation={
<DrawerNavigation
title={t('browserView.settings.heading')}
onCloseIconClick={!popupView ? onClose : undefined}
onArrowIconClick={popupView ? onClose : undefined}
/>
}
popupView={popupView}
>
<div className={popupView ? styles.popupContainer : undefined}>
{popupView && <Text className={styles.drawerDescription}>{'This is the logging level for the extension'}</Text>}
<div className={styles.radios}>
<Radio.Group
className={styles.radioGroup}
onChange={handleLoggingChange}
value={currentLogLevel}
data-testid="logging-radio-group"
>
{logLevels.map((level) => (
<a className={styles.radio} key={level}>
<Radio
value={level}
className={styles.radioLabel}
data-testid={`logging-${level.toLowerCase()}-radio-button`}
>
<span>{capitalize(level)}</span>
</Radio>
</a>
))}
</Radio.Group>
</div>
</div>
</Drawer>
);
};
Loading

0 comments on commit 002e672

Please sign in to comment.