1
0
mirror of https://github.com/uetchy/namae.git synced 2025-08-20 09:58:13 +09:00

feat: add sentry

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

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);
});
}
});
}