diff --git a/explorer/src/components/account/TokenAccountSection.tsx b/explorer/src/components/account/TokenAccountSection.tsx index 535e1a8ddf..44721cd3b7 100644 --- a/explorer/src/components/account/TokenAccountSection.tsx +++ b/explorer/src/components/account/TokenAccountSection.tsx @@ -1,5 +1,4 @@ import React from "react"; -import * as Sentry from "@sentry/react"; import { Account, useFetchAccountInfo } from "providers/accounts"; import { TokenAccount, @@ -15,6 +14,7 @@ import { TokenRegistry } from "tokenRegistry"; import { useCluster } from "providers/cluster"; import { normalizeTokenAmount } from "utils"; import { addressLabel } from "utils/tx"; +import { reportError } from "utils/sentry"; export function TokenAccountSection({ account, @@ -39,10 +39,8 @@ export function TokenAccountSection({ } } } catch (err) { - Sentry.captureException(err, { - tags: { - address: account.pubkey.toBase58(), - }, + reportError(err, { + address: account.pubkey.toBase58(), }); } return ; diff --git a/explorer/src/components/instruction/SerumDetailsCard.tsx b/explorer/src/components/instruction/SerumDetailsCard.tsx index 202c8e7aa8..5ed94524e5 100644 --- a/explorer/src/components/instruction/SerumDetailsCard.tsx +++ b/explorer/src/components/instruction/SerumDetailsCard.tsx @@ -1,9 +1,9 @@ import React from "react"; -import * as Sentry from "@sentry/react"; import { TransactionInstruction, SignatureResult } from "@solana/web3.js"; import { InstructionCard } from "./InstructionCard"; import { parseSerumInstructionTitle } from "utils/tx"; import { useCluster } from "providers/cluster"; +import { reportError } from "utils/sentry"; export function SerumDetailsCard({ ix, @@ -22,11 +22,9 @@ export function SerumDetailsCard({ try { title = parseSerumInstructionTitle(ix); } catch (error) { - Sentry.captureException(error, { - tags: { - url: url, - signature: signature, - }, + reportError(error, { + url: url, + signature: signature, }); } diff --git a/explorer/src/components/instruction/token/TokenDetailsCard.tsx b/explorer/src/components/instruction/token/TokenDetailsCard.tsx index 8f8b069498..4677a445e8 100644 --- a/explorer/src/components/instruction/token/TokenDetailsCard.tsx +++ b/explorer/src/components/instruction/token/TokenDetailsCard.tsx @@ -1,5 +1,4 @@ import React from "react"; -import * as Sentry from "@sentry/react"; import { coerce } from "superstruct"; import { SignatureResult, @@ -19,6 +18,7 @@ import { useFetchAccountInfo, } from "providers/accounts"; import { normalizeTokenAmount } from "utils"; +import { reportError } from "utils/sentry"; type DetailsProps = { tx: ParsedTransaction; @@ -36,10 +36,8 @@ export function TokenDetailsCard(props: DetailsProps) { const coerced = coerce(info, IX_STRUCTS[type] as any); return ; } catch (err) { - Sentry.captureException(err, { - tags: { - signature: props.tx.signatures[0], - }, + reportError(err, { + signature: props.tx.signatures[0], }); return ; } diff --git a/explorer/src/index.tsx b/explorer/src/index.tsx index 3bda904599..68f45863ed 100644 --- a/explorer/src/index.tsx +++ b/explorer/src/index.tsx @@ -12,10 +12,12 @@ import { AccountsProvider } from "./providers/accounts"; import { StatsProvider } from "providers/stats"; import { MintsProvider } from "providers/mints"; -Sentry.init({ - dsn: - "https://5efdc15b4828434fbe949b5daed472be@o434108.ingest.sentry.io/5390542", -}); +if (process.env.NODE_ENV === "production") { + Sentry.init({ + dsn: + "https://5efdc15b4828434fbe949b5daed472be@o434108.ingest.sentry.io/5390542", + }); +} ReactDOM.render( diff --git a/explorer/src/providers/accounts/history.tsx b/explorer/src/providers/accounts/history.tsx index 391d6b9364..14558d38ac 100644 --- a/explorer/src/providers/accounts/history.tsx +++ b/explorer/src/providers/accounts/history.tsx @@ -1,5 +1,4 @@ import React from "react"; -import * as Sentry from "@sentry/react"; import { PublicKey, ConfirmedSignatureInfo, @@ -9,6 +8,7 @@ import { import { useCluster, Cluster } from "../cluster"; import * as Cache from "providers/cache"; import { ActionType, FetchStatus } from "providers/cache"; +import { reportError } from "utils/sentry"; type AccountHistory = { fetched: ConfirmedSignatureInfo[]; @@ -104,7 +104,7 @@ async function fetchAccountHistory( status = FetchStatus.Fetched; } catch (error) { if (cluster !== Cluster.Custom) { - Sentry.captureException(error, { tags: { url } }); + reportError(error, { url }); } status = FetchStatus.FetchFailed; } diff --git a/explorer/src/providers/accounts/index.tsx b/explorer/src/providers/accounts/index.tsx index 78b4c92f72..8f72caef03 100644 --- a/explorer/src/providers/accounts/index.tsx +++ b/explorer/src/providers/accounts/index.tsx @@ -1,5 +1,4 @@ import React from "react"; -import * as Sentry from "@sentry/react"; import { StakeAccount as StakeAccountWasm } from "solana-sdk-wasm"; import { PublicKey, Connection, StakeProgram } from "@solana/web3.js"; import { useCluster, Cluster } from "../cluster"; @@ -15,6 +14,7 @@ import { } from "validators/accounts/token"; import * as Cache from "providers/cache"; import { ActionType, FetchStatus } from "providers/cache"; +import { reportError } from "utils/sentry"; export { useAccountHistory } from "./history"; export type StakeProgramData = { @@ -117,9 +117,7 @@ async function fetchAccountInfo( parsed, }; } catch (err) { - Sentry.captureException(err, { - tags: { url, address: pubkey.toBase58() }, - }); + reportError(err, { url, address: pubkey.toBase58() }); // TODO store error state in Account info } } else if ("parsed" in result.data) { @@ -133,9 +131,7 @@ async function fetchAccountInfo( parsed, }; } catch (err) { - Sentry.captureException(err, { - tags: { url, address: pubkey.toBase58() }, - }); + reportError(err, { url, address: pubkey.toBase58() }); // TODO store error state in Account info } } @@ -152,7 +148,7 @@ async function fetchAccountInfo( fetchStatus = FetchStatus.Fetched; } catch (error) { if (cluster !== Cluster.Custom) { - Sentry.captureException(error, { tags: { url } }); + reportError(error, { url }); } fetchStatus = FetchStatus.FetchFailed; } @@ -200,9 +196,7 @@ export function useMintAccountInfo( return coerce(data.parsed.info, MintAccountInfo); } catch (err) { - Sentry.captureException(err, { - tags: { address }, - }); + reportError(err, { address }); } } @@ -221,9 +215,7 @@ export function useTokenAccountInfo( return coerce(data.parsed.info, TokenAccountInfo); } catch (err) { - Sentry.captureException(err, { - tags: { address }, - }); + reportError(err, { address }); } } diff --git a/explorer/src/providers/accounts/tokens.tsx b/explorer/src/providers/accounts/tokens.tsx index 9932771a71..d25b87ebfe 100644 --- a/explorer/src/providers/accounts/tokens.tsx +++ b/explorer/src/providers/accounts/tokens.tsx @@ -1,11 +1,11 @@ import React from "react"; -import * as Sentry from "@sentry/react"; import { Connection, PublicKey } from "@solana/web3.js"; import * as Cache from "providers/cache"; import { ActionType, FetchStatus } from "providers/cache"; import { TokenAccountInfo } from "validators/accounts/token"; import { useCluster, Cluster } from "../cluster"; import { coerce } from "superstruct"; +import { reportError } from "utils/sentry"; export type TokenInfoWithPubkey = { info: TokenAccountInfo; @@ -75,7 +75,7 @@ async function fetchAccountTokens( status = FetchStatus.Fetched; } catch (error) { if (cluster !== Cluster.Custom) { - Sentry.captureException(error, { tags: { url } }); + reportError(error, { url }); } status = FetchStatus.FetchFailed; } diff --git a/explorer/src/providers/cluster.tsx b/explorer/src/providers/cluster.tsx index 99177df960..95c2416f9c 100644 --- a/explorer/src/providers/cluster.tsx +++ b/explorer/src/providers/cluster.tsx @@ -1,8 +1,8 @@ import React from "react"; -import * as Sentry from "@sentry/react"; import { clusterApiUrl, Connection } from "@solana/web3.js"; import { useQuery } from "../utils/url"; import { useHistory, useLocation } from "react-router-dom"; +import { reportError } from "utils/sentry"; export enum ClusterStatus { Connected, @@ -184,9 +184,7 @@ async function updateCluster( }); } catch (error) { if (cluster !== Cluster.Custom) { - Sentry.captureException(error, { - tags: { clusterUrl: clusterUrl(cluster, customUrl) }, - }); + reportError(error, { clusterUrl: clusterUrl(cluster, customUrl) }); } dispatch({ status: ClusterStatus.Failure, cluster, customUrl }); } diff --git a/explorer/src/providers/mints/largest.tsx b/explorer/src/providers/mints/largest.tsx index 00cae42692..2ef8f875cc 100644 --- a/explorer/src/providers/mints/largest.tsx +++ b/explorer/src/providers/mints/largest.tsx @@ -1,5 +1,4 @@ import React from "react"; -import * as Sentry from "@sentry/react"; import { useCluster, Cluster } from "providers/cluster"; import * as Cache from "providers/cache"; import { ActionType, FetchStatus } from "providers/cache"; @@ -12,6 +11,7 @@ import { import { TokenAccountInfo, TokenAccount } from "validators/accounts/token"; import { ParsedInfo } from "validators"; import { coerce } from "superstruct"; +import { reportError } from "utils/sentry"; type LargestAccounts = { largest: TokenAccountBalancePairWithOwner[]; @@ -89,7 +89,7 @@ async function fetchLargestAccounts( } } catch (error) { if (cluster !== Cluster.Custom) { - Sentry.captureException(error, { tags: { url } }); + reportError(error, { url }); } } return account; @@ -100,7 +100,7 @@ async function fetchLargestAccounts( fetchStatus = FetchStatus.Fetched; } catch (error) { if (cluster !== Cluster.Custom) { - Sentry.captureException(error, { tags: { url } }); + reportError(error, { url }); } fetchStatus = FetchStatus.FetchFailed; } diff --git a/explorer/src/providers/richList.tsx b/explorer/src/providers/richList.tsx index b3f59b94d8..8293d25884 100644 --- a/explorer/src/providers/richList.tsx +++ b/explorer/src/providers/richList.tsx @@ -1,8 +1,8 @@ import React from "react"; -import * as Sentry from "@sentry/react"; import { AccountBalancePair, Connection } from "@solana/web3.js"; import { useCluster, ClusterStatus, Cluster } from "./cluster"; +import { reportError } from "utils/sentry"; export enum Status { Idle, @@ -72,7 +72,7 @@ async function fetch(dispatch: Dispatch, cluster: Cluster, url: string) { }); } catch (err) { if (cluster !== Cluster.Custom) { - Sentry.captureException(err, { tags: { url } }); + reportError(err, { url }); } dispatch("Failed to fetch top accounts"); } diff --git a/explorer/src/providers/supply.tsx b/explorer/src/providers/supply.tsx index b456cac330..36e7361d89 100644 --- a/explorer/src/providers/supply.tsx +++ b/explorer/src/providers/supply.tsx @@ -1,8 +1,8 @@ import React from "react"; -import * as Sentry from "@sentry/react"; import { Supply, Connection } from "@solana/web3.js"; import { useCluster, ClusterStatus, Cluster } from "./cluster"; +import { reportError } from "utils/sentry"; export enum Status { Idle, @@ -53,7 +53,7 @@ async function fetch(dispatch: Dispatch, cluster: Cluster, url: string) { }); } catch (err) { if (cluster !== Cluster.Custom) { - Sentry.captureException(err, { tags: { url } }); + reportError(err, { url }); } dispatch("Failed to fetch supply"); } diff --git a/explorer/src/providers/transactions/details.tsx b/explorer/src/providers/transactions/details.tsx index 5fc1e66f83..6e65970530 100644 --- a/explorer/src/providers/transactions/details.tsx +++ b/explorer/src/providers/transactions/details.tsx @@ -1,5 +1,4 @@ import React from "react"; -import * as Sentry from "@sentry/react"; import { Connection, TransactionSignature, @@ -8,6 +7,7 @@ import { import { useCluster, Cluster } from "../cluster"; import * as Cache from "providers/cache"; import { ActionType, FetchStatus } from "providers/cache"; +import { reportError } from "utils/sentry"; export interface Details { transaction?: ParsedConfirmedTransaction | null; @@ -61,7 +61,7 @@ async function fetchDetails( fetchStatus = FetchStatus.Fetched; } catch (error) { if (cluster !== Cluster.Custom) { - Sentry.captureException(error, { tags: { url } }); + reportError(error, { url }); } fetchStatus = FetchStatus.FetchFailed; } diff --git a/explorer/src/providers/transactions/index.tsx b/explorer/src/providers/transactions/index.tsx index 44975a5a4c..13bc8c3d80 100644 --- a/explorer/src/providers/transactions/index.tsx +++ b/explorer/src/providers/transactions/index.tsx @@ -1,5 +1,4 @@ import React from "react"; -import * as Sentry from "@sentry/react"; import { TransactionSignature, Connection, @@ -9,6 +8,7 @@ import { useCluster, Cluster } from "../cluster"; import { DetailsProvider } from "./details"; import * as Cache from "providers/cache"; import { ActionType, FetchStatus } from "providers/cache"; +import { reportError } from "utils/sentry"; export { useTransactionDetails } from "./details"; export type Confirmations = number | "max"; @@ -80,9 +80,7 @@ export async function fetchTransactionStatus( blockTime = await connection.getBlockTime(value.slot); } catch (error) { if (cluster === Cluster.MainnetBeta) { - Sentry.captureException(error, { - tags: { slot: `${value.slot}` }, - }); + reportError(error, { slot: `${value.slot}` }); } } let timestamp: Timestamp = blockTime !== null ? blockTime : "unavailable"; @@ -105,7 +103,7 @@ export async function fetchTransactionStatus( fetchStatus = FetchStatus.Fetched; } catch (error) { if (cluster !== Cluster.Custom) { - Sentry.captureException(error, { tags: { url } }); + reportError(error, { url }); } fetchStatus = FetchStatus.FetchFailed; } diff --git a/explorer/src/utils/sentry.ts b/explorer/src/utils/sentry.ts new file mode 100644 index 0000000000..31c45bb054 --- /dev/null +++ b/explorer/src/utils/sentry.ts @@ -0,0 +1,18 @@ +import * as Sentry from "@sentry/react"; + +type Tags = + | { + [key: string]: string; + } + | undefined; + +export function reportError(err: Error, tags: Tags) { + console.error(err); + try { + Sentry.captureException(err, { + tags, + }); + } catch (err) { + // Sentry can fail if error rate limit is reached + } +}