如何發送 redux-observable 的 catch error 至 Sentry
我們團隊目前使用 Sentry 這個服務作 error tracking,JavaScript 或 React 的基本安裝方法在 官方文件 都可以找到,這裡就不贅述。
同時我們也有在使用 redux-observable 這個 RxJS middleware 來處理帶有副作用的 Redux action。
根據 redux-observable 這篇 Error Handling 文件的介紹,一般處理 async 錯誤的寫法大概會是:
import { createAction } from 'redux-actions';
import { Observable } from 'rxjs/Observable';
const fetchUserEpic = action$ =>
action$.ofType(FETCH_USER)
.mergeMap(action =>
Observable.fromPromise(fetch(`/api/users/${action.payload}`))
.map(response => createAction('FETCH_USER_FULFILLED')(response))
.catch(error => Observable.of(createAction('FETCH_USER_REJECTED')(error.message)))
);
但是因為這裡並非正規的錯誤拋出方式,導致 Sentry 無法攔截到。
所以根據 Sentry 的這篇 Rich Error Reports with Redux Middleware 文件介紹,我們需要另外為它寫一個 Redux middleware 來處理。
策略是利用 redux-actions 的 Flux Standard Action 特性,將錯誤用 JavaScript 的 Error object 封裝至 action payload:
...
.catch(error => Observable.of(createAction('FETCH_USER_REJECTED')(new Error(error.message))))
// => {
// type: 'FETCH_USER_REJECTED',
// payload: new Error(error.message),
// error: true,
// }
...
然後寫一個 Redux middleware 去攔截這類 actions:
import Raven from 'raven-js';
Raven.config(process.env.REACT_APP_RAVEN_DSN).install();
const errorReporter = store => next => action => {
if (action.error) {
const error = action.payload;
Raven.captureException(error, {
extra: {
action,
state: store.getState(),
},
});
throw error;
}
return next(action);
};
export default errorReporter;
最後將這個 middleware 載入 Redux store:
// store.js
import { createStore, combineReducers, applyMiddleware } from 'redux';
import * as reducers from './reducers';
import errorReporter from './errorReporter';
const rootReducer = combineReducers(reducers);
const middleware = applyMiddleware(
errorReporter, // 注意:errorReporter 要放在其它 middlewares 之前
// epicMiddleware, routerMiddleware, etc...
);
export default (initialState = {}) =>
createStore(
rootReducer,
initialState,
middleware,
);
留言
張貼留言