1
0
mirror of https://github.com/uetchy/namae.git synced 2025-03-17 04:30:31 +09:00

feat: add sentry

This commit is contained in:
uetchy 2019-09-24 13:55:07 +09:00
parent 85520b2417
commit a7efe886ab
7 changed files with 141 additions and 46 deletions

View File

@ -1,9 +1,5 @@
{
"folders": [
{
"name": "Root",
"path": "."
},
{
"name": "API",
"path": "api"

View File

@ -5,11 +5,12 @@
"build": "NODE_ENV=production react-scripts build",
"eject": "react-scripts eject",
"now-build": "yarn build",
"now-dev": "BROWSER=none react-scripts start",
"start": "react-scripts start",
"now-dev": "NODE_ENV=development BROWSER=none react-scripts start",
"start": "NODE_ENV=development react-scripts start",
"test": "react-scripts test"
},
"dependencies": {
"@sentry/browser": "^5.6.3",
"fetch-suspense": "^1.2.0",
"i18next": ">=17.0.12",
"i18next-browser-languagedetector": "^3.0.3",
@ -33,7 +34,7 @@
},
"devDependencies": {
"@testing-library/jest-dom": "^4.1.0",
"@testing-library/react": "^9.1.3",
"@testing-library/react": "^9.1.4",
"@types/i18next-node-fs-backend": "^2.1.0",
"@types/jest": "^24.0.18",
"@types/node": "^12.7.3",

View File

@ -1,7 +1,8 @@
import React, {useState} from 'react';
import React, {useState, useEffect} from 'react';
import styled, {createGlobalStyle} from 'styled-components';
import {Helmet} from 'react-helmet';
import {useTranslation} from 'react-i18next';
import {initGA, sendQueryStatistics} from './util/analytics';
import Welcome from './components/Welcome';
import Form from './components/Form';
@ -15,8 +16,13 @@ export default function App() {
const [query, setQuery] = useState('');
const {t} = useTranslation();
useEffect(() => {
initGA();
}, []);
function onQuery(query: string) {
setQuery(query);
sendQueryStatistics(query.length);
}
return (

View File

@ -6,6 +6,7 @@ import 'react-tippy/dist/tippy.css';
import BarLoader from 'react-spinners/BarLoader';
import {GoInfo} from 'react-icons/go';
import {useTranslation} from 'react-i18next';
import {sendError} from '../../util/analytics';
import {mobile} from '../../util/css';
import {ExternalLink} from '../Links';
@ -21,16 +22,7 @@ export const Card: React.FC<{title: string}> = ({title, children}) => {
<CardContainer>
<CardTitle>{title}</CardTitle>
<CardContent>
<ErrorBoundary>
<Suspense
fallback={
<ResultContainer>
<BarLoader />
</ResultContainer>
}>
{children}
</Suspense>
</ErrorBoundary>
<ErrorHandler>{children}</ErrorHandler>
</CardContent>
</CardContainer>
);
@ -55,12 +47,12 @@ export const Repeater: React.FC<{
return (
<>
{items.map((name) => (
<CellError key={name}>{children(name)}</CellError>
<ErrorHandler key={name}>{children(name)}</ErrorHandler>
))}
{revealAlternatives
? moreItems.map((name) => (
<CellError key={name}>{children(name)}</CellError>
<ErrorHandler key={name}>{children(name)}</ErrorHandler>
))
: null}
{moreItems.length > 0 && !revealAlternatives ? (
@ -208,24 +200,37 @@ export const Result: React.FC<{
);
};
// 1. getDerivedStateFromError
// 2. render()
// 3. componentDidCatch() send errorInfo to Sentry
// 4. render(), now with eventId provided from Sentry
class ErrorBoundary extends React.Component<
{},
{hasError: boolean; message: string}
{hasError: boolean; message: string; eventId?: string}
> {
constructor(props: {}) {
super(props);
this.state = {hasError: false, message: ''};
this.state = {hasError: false, message: '', eventId: undefined};
}
// used in SSR
static getDerivedStateFromError(error: Error) {
return {hasError: true, message: error.message};
}
componentDidCatch(error: Error, errorInfo: any) {
sendError(error, errorInfo).then((eventId) => {
this.setState({eventId});
});
}
render() {
if (this.state.hasError) {
return (
<Tooltip
title={this.state.message}
title={`${this.state.message}${
this.state.eventId ? ` (${this.state.eventId})` : ''
}`}
position="bottom"
arrow={true}
animation="shift"
@ -245,7 +250,7 @@ class ErrorBoundary extends React.Component<
}
}
const CellError: React.FC = ({children}) => (
const ErrorHandler: React.FC = ({children}) => (
<ErrorBoundary>
<Suspense
fallback={

View File

@ -3,22 +3,16 @@ import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {FullScreenSuspense} from './util/suspense';
import {initSentry} from './util/analytics';
import './util/i18n';
const Container = () => (
initSentry();
ReactDOM.render(
<FullScreenSuspense>
<App />
</FullScreenSuspense>
</FullScreenSuspense>,
document.getElementById('root'),
);
ReactDOM.render(<Container />, document.getElementById('root'));
// register Google Analytics
if (process.env.NODE_ENV !== 'development') {
import('react-ga').then((ReactGA) => {
ReactGA.initialize('UA-28919359-15');
ReactGA.pageview(window.location.pathname + window.location.search);
});
}
serviceWorker.register({});

41
web/src/util/analytics.ts Normal file
View File

@ -0,0 +1,41 @@
import ReactGA from 'react-ga';
import * as Sentry from '@sentry/browser';
const isProduction = process.env.NODE_ENV !== 'development';
export function initGA() {
if (isProduction) {
ReactGA.initialize('UA-28919359-15');
ReactGA.pageview(window.location.pathname + window.location.search);
}
}
export function sendQueryStatistics(queryLength: number) {
if (isProduction) {
ReactGA.event({
category: 'Search',
action: 'New search invoked',
value: queryLength,
});
}
}
export function initSentry() {
if (isProduction) {
Sentry.init({
dsn: 'https://7ab2df74aead499b950ebef190cc40b7@sentry.io/1759299',
});
}
}
export function sendError(error: Error, errorInfo: any): Promise<string> {
return new Promise((resolve) => {
if (isProduction) {
Sentry.withScope((scope) => {
scope.setExtras(errorInfo);
const eventId = Sentry.captureException(error);
resolve(eventId);
});
}
});
}

View File

@ -1091,6 +1091,58 @@
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
"@sentry/browser@^5.6.3":
version "5.6.3"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.6.3.tgz#5cc37b0443eba55ad13c13d34d6b95ff30dfbfe3"
integrity sha512-bP1LTbcKPOkkmfJOAM6c7WZ0Ov0ZEW6B9keVZ9wH9fw/lBPd9UyDMDCwJ+FAYKz9M9S5pxQeJ4Ebd7WUUrGVAQ==
dependencies:
"@sentry/core" "5.6.2"
"@sentry/types" "5.6.1"
"@sentry/utils" "5.6.1"
tslib "^1.9.3"
"@sentry/core@5.6.2":
version "5.6.2"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.6.2.tgz#8c5477654a83ebe41a72e86a79215deb5025e418"
integrity sha512-grbjvNmyxP5WSPR6UobN2q+Nss7Hvz+BClBT8QTr7VTEG5q89TwNddn6Ej3bGkaUVbct/GpVlI3XflWYDsnU6Q==
dependencies:
"@sentry/hub" "5.6.1"
"@sentry/minimal" "5.6.1"
"@sentry/types" "5.6.1"
"@sentry/utils" "5.6.1"
tslib "^1.9.3"
"@sentry/hub@5.6.1":
version "5.6.1"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.6.1.tgz#9f355c0abcc92327fbd10b9b939608aa4967bece"
integrity sha512-m+OhkIV5yTAL3R1+XfCwzUQka0UF/xG4py8sEfPXyYIcoOJ2ZTX+1kQJLy8QQJ4RzOBwZA+DzRKP0cgzPJ3+oQ==
dependencies:
"@sentry/types" "5.6.1"
"@sentry/utils" "5.6.1"
tslib "^1.9.3"
"@sentry/minimal@5.6.1":
version "5.6.1"
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.6.1.tgz#09d92b26de0b24555cd50c3c33ba4c3e566009a1"
integrity sha512-ercCKuBWHog6aS6SsJRuKhJwNdJ2oRQVWT2UAx1zqvsbHT9mSa8ZRjdPHYOtqY3DoXKk/pLUFW/fkmAnpdMqRw==
dependencies:
"@sentry/hub" "5.6.1"
"@sentry/types" "5.6.1"
tslib "^1.9.3"
"@sentry/types@5.6.1":
version "5.6.1"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.6.1.tgz#5915e1ee4b7a678da3ac260c356b1cb91139a299"
integrity sha512-Kub8TETefHpdhvtnDj3kKfhCj0u/xn3Zi2zIC7PB11NJHvvPXENx97tciz4roJGp7cLRCJsFqCg4tHXniqDSnQ==
"@sentry/utils@5.6.1":
version "5.6.1"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.6.1.tgz#69d9e151e50415bc91f2428e3bcca8beb9bc2815"
integrity sha512-rfgha+UsHW816GqlSRPlniKqAZylOmQWML2JsujoUP03nPu80zdN43DK9Poy/d9OxBxv0gd5K2n+bFdM2kqLQQ==
dependencies:
"@sentry/types" "5.6.1"
tslib "^1.9.3"
"@sheerun/mutationobserver-shim@^0.3.2":
version "0.3.2"
resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#8013f2af54a2b7d735f71560ff360d3a8176a87b"
@ -1199,10 +1251,10 @@
"@svgr/plugin-svgo" "^4.3.1"
loader-utils "^1.2.3"
"@testing-library/dom@^6.0.0":
version "6.1.0"
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-6.1.0.tgz#8d5a954158e81ecd7c994907f4ec240296ed823b"
integrity sha512-qivqFvnbVIH3DyArFofEU/jlOhkGIioIemOy9A9M/NQTpPyDDQmtVkAfoB18RKN581f0s/RJMRBbq9WfMIhFTw==
"@testing-library/dom@^6.1.0":
version "6.4.0"
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-6.4.0.tgz#aaf7fceba1272516fc7c5ac0716a24f63ea6cdaa"
integrity sha512-uQFwl+mIH9THk9Q9qVZKBgoL/6ahVEQu9bDeOmY5yB8uc62L2Z9eYs0g7zNTdMsg4I0bOdPPMs/sNETYP5+PEw==
dependencies:
"@babel/runtime" "^7.5.5"
"@sheerun/mutationobserver-shim" "^0.3.2"
@ -1226,13 +1278,13 @@
pretty-format "^24.0.0"
redent "^3.0.0"
"@testing-library/react@^9.1.3":
version "9.1.3"
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-9.1.3.tgz#3fb495227322ea36cd817532441dabb552e0d6ce"
integrity sha512-qFVo6TsEbpEFpOmKjIxMHDujOKVdvVpcYFcUfJeWBqMO8eja5pN9SZnt6W6AzW3a1MRvRfw3X0Fhx3eXnBJxjA==
"@testing-library/react@^9.1.4":
version "9.1.4"
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-9.1.4.tgz#4cc1a228a944c0f468ee501e7da1651d8bbd9902"
integrity sha512-fQ/PXZoLcmnS1W5ZiM3P7XBy2x6Hm9cJAT/ZDuZKzJ1fS1rN3j31p7ReAqUe3N1kJ46sNot0n1oiGbz7FPU+FA==
dependencies:
"@babel/runtime" "^7.5.5"
"@testing-library/dom" "^6.0.0"
"@testing-library/dom" "^6.1.0"
"@types/testing-library__react" "^9.1.0"
"@types/babel__core@^7.1.0":
@ -9829,7 +9881,7 @@ ts-pnp@^1.1.2:
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.4.tgz#ae27126960ebaefb874c6d7fa4729729ab200d90"
integrity sha512-1J/vefLC+BWSo+qe8OnJQfWTYRS6ingxjwqmHMqaMxXMj7kFtKLgAaYW3JeX3mktjgUL+etlU8/B4VUAUI9QGw==
tslib@^1.8.1, tslib@^1.9.0:
tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
version "1.10.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==