如何發送 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-actionsFlux 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,
  );

參考資料