Skip to content

Commit 397c94b

Browse files
authored
fix: capture some errors we were logging to console (#188)
We're losing visibility into user errors because we are catching and logging Errors to the console - replace a bunch of places where we do this with a new Sentry logging function that logs to the error console and then captures the error in Sentry.
1 parent 539f948 commit 397c94b

File tree

15 files changed

+49
-27
lines changed

15 files changed

+49
-27
lines changed

src/app/migration/create/page.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { DIDKey, useW3 } from '@w3ui/react'
99
import { DidIcon } from '@/components/DidIcon'
1010
import { MigrationConfiguration, DataSourceID } from '@/lib/migrations/api'
1111
import { dataSources } from '@/app/migration/data-sources'
12+
import { logAndCaptureError } from '@/sentry'
1213

1314
interface WizardProps {
1415
config: Partial<MigrationConfiguration>
@@ -91,7 +92,7 @@ function AddSourceToken ({ config, onNext, onPrev }: WizardProps) {
9192
try {
9293
await ds.source.checkToken(token)
9394
} catch (err: any) {
94-
console.error(err)
95+
logAndCaptureError(err)
9596
return setError(`Error using token: ${err.message}`)
9697
} finally {
9798
setChecking(false)

src/app/settings/page.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { GB, TB, filesize } from '@/lib'
1010
import DefaultLoader from '@/components/Loader'
1111
import { RefcodeLink, ReferralsList, RefcodeCreator } from '../referrals/page'
1212
import { useReferrals } from '@/lib/referrals/hooks'
13+
import { logAndCaptureError } from '@/sentry'
1314

1415
const Plans: Record<`did:${string}`, { name: string, limit: number }> = {
1516
'did:web:starter.web3.storage': { name: 'Starter', limit: 5 * GB },
@@ -51,13 +52,13 @@ export default function SettingsPage (): JSX.Element {
5152
}
5253
} catch (err) {
5354
// TODO: figure out why usage/report cannot be used on old spaces
54-
console.error(err)
55+
logAndCaptureError(err)
5556
}
5657
}
5758
}
5859
return usage
5960
},
60-
onError: err => console.error(err.message, err.cause)
61+
onError: logAndCaptureError
6162
})
6263

6364
const product = plan?.product

src/app/space/[did]/page.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import useSWR from 'swr'
66
import { useRouter, usePathname } from 'next/navigation'
77
import { createUploadsListKey } from '@/cache'
88
import { Breadcrumbs } from '@/components/Breadcrumbs'
9+
import { logAndCaptureError } from '@/sentry'
910

1011
const pageSize = 15
1112

@@ -39,7 +40,7 @@ export default function Page ({ params, searchParams }: PageProps): JSX.Element
3940
size: pageSize
4041
})
4142
},
42-
onError: err => console.error(err.message, err.cause),
43+
onError: logAndCaptureError,
4344
keepPreviousData: true
4445
})
4546

src/app/space/[did]/root/[cid]/page.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import CopyIcon from '@/components/CopyIcon'
1313
import { Breadcrumbs } from '@/components/Breadcrumbs'
1414
import { useRouter } from 'next/navigation'
1515
import { createUploadsListKey } from '@/cache'
16+
import { logAndCaptureError } from '@/sentry'
1617
import { ipfsGatewayURL } from '@/components/services'
1718

1819
interface PageProps {
@@ -39,7 +40,7 @@ export default function ItemPage ({ params }: PageProps): JSX.Element {
3940

4041
return await client.capability.upload.get(root)
4142
},
42-
onError: err => console.error(err.message, err.cause)
43+
onError: logAndCaptureError
4344
})
4445

4546
const [isRemoveConfirmModalOpen, setRemoveConfirmModalOpen] = useState(false)

src/app/space/[did]/root/[cid]/shard/[shard]/page.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import ExpandIcon from '@/components/ExpandIcon'
1818
import { useState } from 'react'
1919
import AggregateIcon from '@/components/AggregateIcon'
2020
import PieceIcon from '@/components/PieceIcon'
21+
import { logAndCaptureError } from '@/sentry'
2122

2223
type ProofStyle = 'mini'|'midi'|'maxi'
2324

@@ -67,7 +68,7 @@ export default function ItemPage ({ params }: PageProps): JSX.Element {
6768
}
6869
}
6970
},
70-
onError: err => console.error(err.message, err.cause)
71+
onError: logAndCaptureError
7172
})
7273

7374
const claimKey = `/assert/equals?content=${shard}`
@@ -80,7 +81,7 @@ export default function ItemPage ({ params }: PageProps): JSX.Element {
8081
}
8182
}
8283
},
83-
onError: err => console.error(err.message, err.cause)
84+
onError: logAndCaptureError
8485
})
8586

8687
const filecoinInfoKey = `/filecoin/info?piece=${claim.data?.equals}`
@@ -99,7 +100,7 @@ export default function ItemPage ({ params }: PageProps): JSX.Element {
99100

100101
return out.ok
101102
},
102-
onError: err => console.error(err.message, err.cause)
103+
onError: logAndCaptureError
103104
})
104105

105106
const [proofStyle, setProofStyle] = useState<ProofStyle>('mini')

src/components/MigrationsProvider.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useW3 } from '@w3ui/react'
66
import * as Migrations from '@/lib/migrations'
77
import { MigrationsStorage } from '@/lib/migrations/store'
88
import { serviceConnection } from './services'
9+
import { logAndCaptureError } from '@/sentry'
910

1011
const MAX_LOG_LINES = 1000
1112

@@ -126,7 +127,7 @@ export function Provider ({ children }: ProviderProps): ReactNode {
126127
setMigrations(() => migrationsStore.load())
127128
},
128129
onError: async (err, upload, shard) => {
129-
console.error(err)
130+
logAndCaptureError(err)
130131
log(id, `failed migration ${upload.root}${shard ? ` (shard: ${shard.link})` : ''}: ${err.stack}`)
131132
const migration = migrationsStore.read(id)
132133
migration.progress = migration.progress ?? await initProgress()

src/components/SpaceCreator.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import * as UcantoClient from '@ucanto/client'
1313
import { HTTP } from '@ucanto/transport'
1414
import * as CAR from '@ucanto/transport/car'
1515
import { gatewayHost } from './services'
16+
import { logAndCaptureError } from '@/sentry'
1617

1718
export function SpaceCreatorCreating(): JSX.Element {
1819
return (
@@ -99,7 +100,7 @@ export function SpaceCreatorForm({
99100
resetForm()
100101
} catch (error) {
101102
/* eslint-disable-next-line no-console */
102-
console.error(error)
103+
logAndCaptureError(error)
103104
throw new Error('failed to create space', { cause: error })
104105
}
105106
}

src/components/Uploader.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { ipfsGatewayURL } from '../components/services'
1616
import { useEffect, useState } from 'react'
1717
import { RadioGroup } from '@headlessui/react'
1818
import { H2 } from './Text'
19+
import { logAndCaptureError } from '@/sentry'
1920

2021
function StatusLoader ({ progressStatus }: { progressStatus: ProgressStatus }) {
2122
const { total, loaded, lengthComputable } = progressStatus
@@ -66,7 +67,7 @@ export const Errored = ({ error }: { error: any }): JSX.Element => {
6667
useEffect(() => {
6768
if (error != null) {
6869
// eslint-disable-next-line no-console
69-
console.error('Uploader Error:', error)
70+
logAndCaptureError(new Error('Uploader Error:', { cause: error }))
7071
}
7172
}, [error])
7273

@@ -256,7 +257,7 @@ const UploaderContents = (): JSX.Element => {
256257
</div>
257258
<div className='p-4'>
258259
<button type='submit' className='inline-block bg-hot-red border border-hot-red hover:bg-white hover:text-hot-red font-epilogue text-white uppercase text-sm px-6 py-2 rounded-full whitespace-nowrap' disabled={file === undefined}>
259-
<CloudArrowUpIcon className='h-5 w-5 inline-block mr-1 align-middle' style={{marginTop: -4}} /> Start Upload
260+
<CloudArrowUpIcon className='h-5 w-5 inline-block mr-1 align-middle' style={{ marginTop: -4 }} /> Start Upload
260261
</button>
261262
</div>
262263
</>

src/hooks.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Account, DID, PlanGetSuccess, PlanSetSuccess, PlanSetFailure, Result } from '@w3ui/react'
22
import useSWR, { SWRResponse, useSWRConfig } from 'swr'
3+
import { logAndCaptureError } from './sentry'
34

45
/**
56
* calculate the cache key for a plan's account
@@ -18,7 +19,7 @@ export const usePlan = (account: Account) => {
1819
if (result.error) throw new Error('getting plan', { cause: result.error })
1920
return result.ok
2021
},
21-
onError: err => console.error(err.message, err.cause)
22+
onError: logAndCaptureError
2223
})
2324
// @ts-ignore it's important to assign this into the existing object
2425
// to avoid calling the getters in SWRResponse when copying values over -

src/lib/migrations/nft-storage.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { CarBlockIterator } from '@ipld/car'
66
import { LinkIndexer } from 'linkdex'
77
import { DataSourceConfiguration, Shard, Upload } from './api'
88
import { carCode } from './constants'
9+
import { logAndCaptureError } from '@/sentry'
910

1011
export const id = 'classic.nft.storage'
1112

@@ -118,9 +119,9 @@ class Reader {
118119
}
119120
}
120121
} catch (err) {
121-
console.error(`failed to read content claims for PSA item: ${root}`, err)
122+
logAndCaptureError(new Error(`failed to read content claims for PSA item: ${root}`, { cause: err }))
122123
}
123-
}
124+
}
124125

125126
const shards: Shard[] = []
126127
for (const p of parts) {
@@ -163,7 +164,7 @@ class Reader {
163164
const link = Link.create(carCode, await sha256.digest(bytes))
164165
shards.push({ link, size: async () => bytes.length, bytes: async () => bytes })
165166
} catch (err) {
166-
console.error(`failed to download CAR for item: ${root}`, err)
167+
logAndCaptureError(new Error(`failed to download CAR for item: ${root}`, {cause: err}))
167168
}
168169
}
169170

@@ -191,7 +192,7 @@ async function * paginator (fn: (service: Service, opts: ListOptions) => Promise
191192
// Iterate through next pages
192193
while (body && body.value.length) {
193194
// Get before timestamp with less 1ms
194-
const before = (new Date((new Date(body.value[body.value.length-1].created)).getTime() - 1)).toISOString()
195+
const before = (new Date((new Date(body.value[body.value.length - 1].created)).getTime() - 1)).toISOString()
195196
res = await fn(service, { ...opts, before })
196197
body = await res.json()
197198
yield body

src/lib/migrations/store.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as dagJSON from '@ipld/dag-json'
22
import { Migration, MigrationConfiguration, MigrationID } from './api'
3+
import { logAndCaptureError } from '@/sentry'
34

45
export class MigrationsStorage {
56
load () {
@@ -13,7 +14,7 @@ export class MigrationsStorage {
1314
const migration: Migration = dagJSON.parse(localStorage.getItem(`migration.${id}`) ?? '')
1415
migrations.push(migration)
1516
} catch (err) {
16-
console.error(`failed to load migration: ${id}`, err)
17+
logAndCaptureError(new Error(`failed to load migration: ${id}`, {cause: err}))
1718
}
1819
}
1920
return migrations

src/lib/migrations/web3-storage-psa.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { DataSourceConfiguration, Shard, Upload } from './api'
55
import * as Gateway from './gateway'
66
import { Result, Failure } from '@ucanto/interface'
77
import { CARLink } from '@w3ui/react'
8+
import { logAndCaptureError } from '@/sentry'
89

910
export const id = 'psa.old.web3.storage'
1011

@@ -98,7 +99,7 @@ class Reader {
9899
}
99100
})
100101
} catch (err) {
101-
console.error(`determining CAR hash for root: ${root}`, err)
102+
logAndCaptureError(new Error(`determining CAR hash for root: ${root}`, {cause: err}))
102103
}
103104

104105
// Add a synthetic shard that is the entire DAG.
@@ -108,7 +109,7 @@ class Reader {
108109
const shard = await Gateway.fetchCAR(root)
109110
shards.push(shard)
110111
} catch (err) {
111-
console.error(`downloading CAR for root: ${root}`, err)
112+
logAndCaptureError(new Error(`downloading CAR for root: ${root}`, {cause: err}))
112113
}
113114
}
114115

src/lib/migrations/web3-storage.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
import { Web3Storage } from 'web3.storage'
22
import * as Link from 'multiformats/link'
3-
import { sha256 } from 'multiformats/hashes/sha2'
4-
import { CarBlockIterator } from '@ipld/car'
5-
import { LinkIndexer } from 'linkdex'
63
import { DataSourceConfiguration, Shard, Upload } from './api'
7-
import { carCode } from './constants'
84
import { fetchCAR } from './gateway'
5+
import { logAndCaptureError } from '@/sentry'
96

107
export const id = 'old.web3.storage'
118

@@ -80,7 +77,7 @@ class Reader {
8077
const shard = await fetchCAR(root)
8178
shards.push(shard)
8279
} catch (err) {
83-
console.error(`failed to download CAR for item: ${root}`, err)
80+
logAndCaptureError(new Error(`failed to download CAR for item: ${root}`, {cause: err}))
8481
}
8582
}
8683

src/sentry.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
import * as Sentry from '@sentry/nextjs'
3+
4+
/**
5+
* Log to the error console and capture the error in Sentry.
6+
*
7+
* @param err the error - typed as unknown to match catch(err)
8+
*/
9+
export function logAndCaptureError (err: unknown) {
10+
console.error(err)
11+
Sentry.captureException(err)
12+
}

src/share.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Tooltip from './components/Tooltip'
1010
import { ArrowDownOnSquareStackIcon, CloudArrowDownIcon, PaperAirplaneIcon, InformationCircleIcon } from '@heroicons/react/24/outline'
1111
import * as DIDMailTo from '@web3-storage/did-mailto'
1212
import { DID } from '@ucanto/core'
13+
import { logAndCaptureError } from './sentry'
1314

1415
function Header(props: PropsWithChildren): JSX.Element {
1516
return (
@@ -223,14 +224,14 @@ export function ImportSpace() {
223224
}
224225
delegation = res.ok
225226
} catch (err) {
226-
console.error(err)
227+
logAndCaptureError(err)
227228
return
228229
}
229230
try {
230231
await client.addSpace(delegation)
231232
setProof(delegation)
232233
} catch (err) {
233-
console.error(err)
234+
logAndCaptureError(err)
234235
}
235236
}
236237

0 commit comments

Comments
 (0)