From ad97b7859cfd00f80c6bb150a31f1f5526dc4c5b Mon Sep 17 00:00:00 2001 From: Zeck Li Date: Fri, 26 Apr 2019 18:01:11 +0800 Subject: [PATCH 1/3] Inject Sentry tracker --- .env.example | 1 + components/ErrorBoundary/index.tsx | 4 ++++ components/GQL/error.tsx | 4 ++++ components/Viewer/index.tsx | 11 +++++++++++ next.config.js | 3 ++- package.json | 1 + pages/_app.tsx | 9 +++++++++ 7 files changed, 32 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 44dca9b079..7c59e7c948 100644 --- a/.env.example +++ b/.env.example @@ -4,3 +4,4 @@ API_URL=http://matters-server-develop.ap-southeast-1.elasticbeanstalk.com/ WS_URL=ws://matters-server-develop.ap-southeast-1.elasticbeanstalk.com/graphql SEGMENT_KEY=3gE20MjzN9qncFqlKV0pDvNO7Cp2gWU3 FB_APP_ID=823885921293850 +SENTRY_DSN= diff --git a/components/ErrorBoundary/index.tsx b/components/ErrorBoundary/index.tsx index 85b0d74bc3..1df7ef8ba4 100644 --- a/components/ErrorBoundary/index.tsx +++ b/components/ErrorBoundary/index.tsx @@ -1,3 +1,4 @@ +import * as Sentry from '@sentry/browser' import React from 'react' import { Error as ErrorComponent } from '~/components/Error' @@ -24,6 +25,9 @@ class ErrorBoundary extends React.Component { } componentDidCatch(error: Error, info: any): void { + // Add info to Sentry + Sentry.captureException(error) + const { onError } = this.props if (typeof onError === 'function') { diff --git a/components/GQL/error.tsx b/components/GQL/error.tsx index 68a8597b98..81b92ba638 100644 --- a/components/GQL/error.tsx +++ b/components/GQL/error.tsx @@ -1,3 +1,4 @@ +import * as Sentry from '@sentry/browser' import { ApolloError } from 'apollo-client' import _get from 'lodash/get' @@ -27,6 +28,9 @@ export const getErrorCodes = (error: any) => { } export const checkError = (error: ApolloError) => { + // Add info to Sentry + Sentry.captureException(error) + if (!process.browser) { throw error } diff --git a/components/Viewer/index.tsx b/components/Viewer/index.tsx index 243b493269..08e474eb9b 100644 --- a/components/Viewer/index.tsx +++ b/components/Viewer/index.tsx @@ -1,3 +1,4 @@ +import * as Sentry from '@sentry/browser' import gql from 'graphql-tag' import _get from 'lodash/get' import React from 'react' @@ -59,6 +60,16 @@ export const processViewer = (viewer: ViewerUser): Viewer => { const isInactive = isAuthed && (isFrozen || isBanned || isArchived) const isAdmin = role === 'admin' + // Add user info for Sentry + Sentry.configureScope((scope: any) => { + scope.setUser({ + id: viewer.id, + role, + language: _get(viewer, 'settings.language') + }) + scope.setTag('source', 'web') + }) + return { ...viewer, isAuthed, diff --git a/next.config.js b/next.config.js index 1e3bfb49f8..064958f7f1 100644 --- a/next.config.js +++ b/next.config.js @@ -33,7 +33,8 @@ const nextConfig = { API_URL: process.env.API_URL, WS_URL: process.env.WS_URL, SEGMENT_KEY: process.env.SEGMENT_KEY, - FB_APP_ID: process.env.FB_APP_ID + FB_APP_ID: process.env.FB_APP_ID, + SENTRY_DSN: process.env.SENTRY_DSN }, /** diff --git a/package.json b/package.json index d34826f399..af4d9436cd 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "gen:fragmentTypes": "node bin/buildFragmentTypes.js" }, "dependencies": { + "@sentry/browser": "^5.1.1", "@tippy.js/react": "^2.1.2", "apollo-cache-inmemory": "^1.5.1", "apollo-cache-persist": "^0.1.1", diff --git a/pages/_app.tsx b/pages/_app.tsx index ac0dc806b1..4cc317bafb 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,7 +1,9 @@ +import * as Sentry from '@sentry/browser' import { InMemoryCache } from 'apollo-cache-inmemory' import { ApolloClient } from 'apollo-client' import gql from 'graphql-tag' import App, { Container } from 'next/app' +import getConfig from 'next/config' import React from 'react' import { ApolloProvider, QueryResult } from 'react-apollo' @@ -18,6 +20,13 @@ import withApollo from '~/common/utils/withApollo' import { RootQuery } from './__generated__/RootQuery' +// start Sentry +const { + publicRuntimeConfig: { SENTRY_DSN } +} = getConfig() + +Sentry.init({ dsn: SENTRY_DSN || '' }) + class MattersApp extends App<{ apollo: ApolloClient }> { public query = gql` query RootQuery { From b07516fc79e9386058f0ffeb8ac79ef074a354c3 Mon Sep 17 00:00:00 2001 From: Zeck Li Date: Fri, 26 Apr 2019 19:22:38 +0800 Subject: [PATCH 2/3] Improve Sentry --- common/utils/index.ts | 1 + common/utils/random.ts | 2 ++ common/utils/withApollo.ts | 20 +++++++++++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 common/utils/random.ts diff --git a/common/utils/index.ts b/common/utils/index.ts index ae6a1e3db6..1b75a7088c 100644 --- a/common/utils/index.ts +++ b/common/utils/index.ts @@ -11,3 +11,4 @@ export * from './number' export * from './language' export * from './text' export * from './cache' +export * from './random' diff --git a/common/utils/random.ts b/common/utils/random.ts new file mode 100644 index 0000000000..fd1d389368 --- /dev/null +++ b/common/utils/random.ts @@ -0,0 +1,2 @@ +export const genSentryActionId = () => + Math.random().toString(36).substr(2, 9) diff --git a/common/utils/withApollo.ts b/common/utils/withApollo.ts index 2c50be2641..09a5972aa5 100644 --- a/common/utils/withApollo.ts +++ b/common/utils/withApollo.ts @@ -1,3 +1,4 @@ +import * as Sentry from '@sentry/browser' import { ApolloClient } from 'apollo-client' import { ApolloLink, split } from 'apollo-link' import { setContext } from 'apollo-link-context' @@ -10,6 +11,8 @@ import https from 'https' import withApollo from 'next-with-apollo' import getConfig from 'next/config' +import { genSentryActionId } from '~/common/utils' + import { inMemoryCache // setupPersistCache @@ -87,13 +90,28 @@ const authLink = setContext((_, { headers }) => { } }) +const sentryLink = setContext((_, { headers }) => { + // Add action id for Sentry + const actionId = genSentryActionId() + Sentry.configureScope((scope: any) => { + scope.setTag('action-id', actionId) + }) + + return { + headers: { + ...headers, + 'x-sentry-action-id': actionId + } + } +}) + export default withApollo(({ ctx, headers, initialState }) => { inMemoryCache.restore(initialState || {}) // setupPersistCache() return new ApolloClient({ - link: ApolloLink.from([errorLink, authLink, dataLink({ headers })]), + link: ApolloLink.from([errorLink, authLink, sentryLink, dataLink({ headers })]), cache: inMemoryCache }) }) From b49bc2607110c25193bddc8574dd1359268b45c2 Mon Sep 17 00:00:00 2001 From: Zeck Li Date: Fri, 26 Apr 2019 19:24:17 +0800 Subject: [PATCH 3/3] Improve Sentry --- common/utils/random.ts | 4 +++- common/utils/withApollo.ts | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/common/utils/random.ts b/common/utils/random.ts index fd1d389368..ee88f50b08 100644 --- a/common/utils/random.ts +++ b/common/utils/random.ts @@ -1,2 +1,4 @@ export const genSentryActionId = () => - Math.random().toString(36).substr(2, 9) + Math.random() + .toString(36) + .substr(2, 9) diff --git a/common/utils/withApollo.ts b/common/utils/withApollo.ts index 09a5972aa5..0feb1753a4 100644 --- a/common/utils/withApollo.ts +++ b/common/utils/withApollo.ts @@ -111,7 +111,12 @@ export default withApollo(({ ctx, headers, initialState }) => { // setupPersistCache() return new ApolloClient({ - link: ApolloLink.from([errorLink, authLink, sentryLink, dataLink({ headers })]), + link: ApolloLink.from([ + errorLink, + authLink, + sentryLink, + dataLink({ headers }) + ]), cache: inMemoryCache }) })