ืžืขืงื‘ ืื—ืจ ืฉื’ื™ืื•ืช ื‘ืืคืœื™ืงืฆื™ื™ืช React ื‘ืืžืฆืขื•ืช Sentry

ืžืขืงื‘ ืื—ืจ ืฉื’ื™ืื•ืช ื‘ืืคืœื™ืงืฆื™ื™ืช React ื‘ืืžืฆืขื•ืช Sentry

ื”ื™ื•ื ืืกืคืจ ืœื›ื ืขืœ ืžืขืงื‘ ืื—ืจ ืฉื’ื™ืื•ืช ื‘ื–ืžืŸ ืืžืช ื‘ืืคืœื™ืงืฆื™ื™ืช React. ื™ื™ืฉื•ื ื—ื–ื™ืชื™ ืื™ื ื• ืžืฉืžืฉ ื‘ื“ืจืš ื›ืœืœ ืœืžืขืงื‘ ืื—ืจ ืฉื’ื™ืื•ืช. ื—ื‘ืจื•ืช ืžืกื•ื™ืžื•ืช ื“ื•ื—ื•ืช ืœืขืชื™ื ืงืจื•ื‘ื•ืช ืžืขืงื‘ ืื—ืจ ื‘ืื’ื™ื, ื—ื•ื–ืจื•ืช ืืœื™ื• ืœืื—ืจ ืชื™ืขื•ื“, ื‘ื“ื™ืงื•ืช ื•ื›ื•'. ืขื ื–ืืช, ืื ืืชื” ื™ื›ื•ืœ ืœืฉื ื•ืช ืืช ื”ืžื•ืฆืจ ืฉืœืš ืœื˜ื•ื‘ื”, ืื– ืคืฉื•ื˜ ืขืฉื” ื–ืืช!

1. ืœืžื” ืืชื” ืฆืจื™ืš Sentry?

ืื ื™ ืžื ื™ื— ืฉืืชื” ืžืขื•ื ื™ื™ืŸ ื‘ืžืขืงื‘ ืื—ืจ ื‘ืื’ื™ื ื‘ืžื”ืœืš ื”ื™ื™ืฆื•ืจ

ืืชื” ื—ื•ืฉื‘ ืฉื–ื” ืœื ืžืกืคื™ืง?

ืื•ืงื™ื™, ื‘ื•ืื• ื ืกืชื›ืœ ืขืœ ื”ืคืจื˜ื™ื.

ื”ืกื™ื‘ื•ืช ื”ืขื™ืงืจื™ื•ืช ืœืžืคืชื—ื™ื ืœื”ืฉืชืžืฉ ื‘-Sentry:

  • ืžืืคืฉืจ ืœืš ืœื”ื™ืžื ืข ืžืกื™ื›ื•ื ื™ื ื‘ืขืช ืคืจื™ืกืช ืงื•ื“ ืขื ืฉื’ื™ืื•ืช
  • ืขื–ืจื” QA ืขื ื‘ื“ื™ืงืช ืงื•ื“
  • ืงื‘ืœ ื”ื•ื“ืขื•ืช ืžื”ื™ืจื•ืช ืขืœ ื‘ืขื™ื•ืช
  • ื™ื›ื•ืœืช ืชื™ืงื•ืŸ ืžื”ื™ืจ ืฉืœ ืฉื’ื™ืื•ืช
  • ืงื‘ืœืช ืชืฆื•ื’ื” ื ื•ื—ื” ืฉืœ ืฉื’ื™ืื•ืช ื‘ืคืื ืœ ื”ื ื™ื”ื•ืœ
  • ืžื™ื™ืŸ ืฉื’ื™ืื•ืช ืœืคื™ ืคืœื— ืžืฉืชืžืฉ/ื“ืคื“ืคืŸ

ื”ืกื™ื‘ื•ืช ื”ืขื™ืงืจื™ื•ืช ืœืคืจื•ื™ืงื˜ ืžื ื›"ืœ/ืœื™ื“

  • ื—ืกื•ืš ื›ืกืฃ (ื ื™ืชืŸ ืœื”ืชืงื™ืŸ ืืช Sentry ื‘ืฉืจืชื™ื ืฉืœืš)
  • ืงื‘ืœืช ืžืฉื•ื‘ ืžื”ืžืฉืชืžืฉื™ื
  • ื”ื‘ื ืช ืžื” ืœื ื‘ืกื“ืจ ื‘ืคืจื•ื™ืงื˜ ืฉืœืš ื‘ื–ืžืŸ ืืžืช
  • ื”ื‘ื ืช ืžืกืคืจ ื”ื‘ืขื™ื•ืช ืฉื™ืฉ ืœืื ืฉื™ื ืขื ื”ืืคืœื™ืงืฆื™ื” ืฉืœืš
  • ืขื–ื•ืจ ืœืš ืœืžืฆื•ื ืžืงื•ืžื•ืช ืฉื‘ื”ื ื”ืžืคืชื—ื™ื ืฉืœืš ืขืฉื• ื˜ืขื•ื™ื•ืช

ืื ื™ ื—ื•ืฉื‘ ืฉื”ืžืคืชื—ื™ื ื™ืชืขื ื™ื™ื ื• ืงื•ื“ื ื›ืœ ื‘ืžืืžืจ ื–ื”. ืืชื” ื™ื›ื•ืœ ื’ื ืœื”ืฉืชืžืฉ ื‘ืจืฉื™ืžืช ื”ืกื™ื‘ื•ืช ื”ื–ื• ื›ื“ื™ ืœืฉื›ื ืข ืืช ื”ื‘ื•ืก ืฉืœืš ืœืฉืœื‘ ืืช Sentry.

ื”ื™ื–ื”ืจ ืขื ื”ืคืจื™ื˜ ื”ืื—ืจื•ืŸ ื‘ืจืฉื™ืžืช ื”ืขืกืงื™ื.

ืืชื” ื›ื‘ืจ ืžืชืขื ื™ื™ืŸ?

ืžืขืงื‘ ืื—ืจ ืฉื’ื™ืื•ืช ื‘ืืคืœื™ืงืฆื™ื™ืช React ื‘ืืžืฆืขื•ืช Sentry

ืžื” ื–ื” Sentry?

Sentry ื”ื•ื ื™ื™ืฉื•ื ืžืขืงื‘ ืื—ืจ ื‘ืื’ื™ื ื‘ืงื•ื“ ืคืชื•ื— ื”ืžืกื™ื™ืข ืœืžืคืชื—ื™ื ืœืขืงื•ื‘ ื•ืœืชืงืŸ ืงืจื™ืกื•ืช ื‘ื–ืžืŸ ืืžืช. ืืœ ืชืฉื›ื— ืฉื”ืืคืœื™ืงืฆื™ื” ืžืืคืฉืจืช ืœืš ืœื”ื’ื‘ื™ืจ ืืช ื”ื™ืขื™ืœื•ืช ื•ืœืฉืคืจ ืืช ื—ื•ื•ื™ืช ื”ืžืฉืชืžืฉ. Sentry ืชื•ืžืš ื‘-JavaScript, Node, Python, PHP, Ruby, Java ื•ืฉืคื•ืช ืชื›ื ื•ืช ืื—ืจื•ืช.

ืžืขืงื‘ ืื—ืจ ืฉื’ื™ืื•ืช ื‘ืืคืœื™ืงืฆื™ื™ืช React ื‘ืืžืฆืขื•ืช Sentry

2. ื”ืชื—ื‘ืจ ื•ืฆื•ืจ ืคืจื•ื™ืงื˜

  • ืคืชื— ืืช ื—ืฉื‘ื•ืŸ Sentry ืฉืœืš. ื™ื™ืชื›ืŸ ืฉืชืฆื˜ืจืš ืœื”ืชื—ื‘ืจ. (ืฉื™ืžื• ืœื‘ ืฉื ื™ืชืŸ ืœื”ืชืงื™ืŸ ืืช Sentry ื‘ืฉืจืชื™ื ืฉืœื›ื)
  • ื”ืฉืœื‘ ื”ื‘ื ื”ื•ื ื™ืฆื™ืจืช ืคืจื•ื™ืงื˜
  • ื‘ื—ืจ ืืช ื”ืฉืคื” ืฉืœืš ืžื”ืจืฉื™ืžื”. (ืื ื—ื ื• ื”ื•ืœื›ื™ื ืœื‘ื—ื•ืจ React. ืœื—ืฅ ืขืœ "ืฆื•ืจ ืคืจื•ื™ืงื˜")

ืžืขืงื‘ ืื—ืจ ืฉื’ื™ืื•ืช ื‘ืืคืœื™ืงืฆื™ื™ืช React ื‘ืืžืฆืขื•ืช Sentry

ื”ืชืื ืื™ืฉื™ืช ืืช ื”ืืคืœื™ืงืฆื™ื” ืฉืœืš. ื“ื•ื’ืžื” ื‘ืกื™ืกื™ืช ื›ื™ืฆื“ ืœืฉืœื‘ ืืช Sentry ื‘ืžื™ื›ืœ ื ื™ืชืŸ ืœืจืื•ืช ืœื”ืœืŸ:

import * as Sentry from '@sentry/browser';
// Sentry.init({
//  dsn: "<https://[email protected]/1432138>"
// });
// should have been called before using it here
// ideally before even rendering your react app 

class ExampleBoundary extends Component {
    constructor(props) {
        super(props);
        this.state = { error: null };
    }

    componentDidCatch(error, errorInfo) {
      this.setState({ error });
      Sentry.withScope(scope => {
        Object.keys(errorInfo).forEach(key => {
          scope.setExtra(key, errorInfo[key]);
        });
        Sentry.captureException(error);
      });
    }

    render() {
        if (this.state.error) {
            //render fallback UI
            return (
              <a onClick={() => Sentry.showReportDialog()}>Report feedback</a>
            );
        } else {
            //when there's not an error, render children untouched
            return this.props.children;
        }
    }
}

ืœ-Sentry ื™ืฉ ืืฉืฃ ืžื•ืขื™ืœ ืฉื™ืขื–ื•ืจ ืœืš ืœื”ื‘ื™ืŸ ืžื” ืขืœื™ืš ืœืขืฉื•ืช ื”ืœืื”. ืืชื” ื™ื›ื•ืœ ืœื‘ืฆืข ืืช ื”ืฉืœื‘ื™ื ื”ื‘ืื™ื. ืื ื™ ืจื•ืฆื” ืœื”ืจืื•ืช ืœืš ืื™ืš ืœื™ืฆื•ืจ ืืช ืžื˜ืคืœ ื”ืฉื’ื™ืื•ืช ื”ืจืืฉื•ืŸ ืฉืœืš. ื ื”ื“ืจ, ื™ืฆืจื ื• ืคืจื•ื™ืงื˜! ื‘ื•ืื• ื ืขื‘ื•ืจ ืœืฉืœื‘ ื”ื‘ื

3. ืื™ื ื˜ื’ืจืฆื™ื” ืฉืœ React ื•- Sentry

ืขืœื™ืš ืœื”ืชืงื™ืŸ ืืช ื—ื‘ื™ืœืช npm ื‘ืคืจื•ื™ืงื˜ ืฉืœืš.

npm i @sentry/browser

ืืชื—ื•ืœ Sentry ื‘ืžื™ื›ืœ ืฉืœืš:

Sentry.init({
 // dsn: #dsnUrl,
});

ื”-DSN ืžืžื•ืงื ื‘ืคืจื•ื™ืงื˜ื™ื -> ื”ื’ื“ืจื•ืช -> ืžืคืชื—ื•ืช ืœืงื•ื—. ืืชื” ื™ื›ื•ืœ ืœืžืฆื•ื ืžืคืชื—ื•ืช ืœืงื•ื— ื‘ืฉื•ืจืช ื”ื—ื™ืคื•ืฉ.

ืžืขืงื‘ ืื—ืจ ืฉื’ื™ืื•ืช ื‘ืืคืœื™ืงืฆื™ื™ืช React ื‘ืืžืฆืขื•ืช Sentry

componentDidCatch(error, errorInfo) {
  Sentry.withScope(scope => {
    Object.keys(errorInfo).forEach(key => {
      scope.setExtra(key, errorInfo[key]);
    });
    Sentry.captureException(error);
 });
}

4. ืžืขืงื‘ ืื—ืจ ื”ืฉื’ื™ืื” ื”ืจืืฉื•ื ื”

ืœื“ื•ื’ืžื”, ื”ืฉืชืžืฉืชื™ ื‘ื™ื™ืฉื•ื ืžื•ื–ื™ืงื” ืคืฉื•ื˜ ืขื ื”-API ืฉืœ Deezer. ืืชื” ื™ื›ื•ืœ ืœืจืื•ืช ืืช ื–ื” ื›ืืŸ. ืื ื—ื ื• ืฆืจื™ื›ื™ื ืœื™ืฆื•ืจ ืฉื’ื™ืื”. ื“ืจืš ืื—ืช ื”ื™ื ืœื’ืฉืช ืœืžืืคื™ื™ืŸ "ืœื ืžื•ื’ื“ืจ".

ืื ื—ื ื• ืฆืจื™ื›ื™ื ืœื™ืฆื•ืจ ื›ืคืชื•ืจ ืฉืžืชืงืฉืจ console.log ั user.email. ืœืื—ืจ ืคืขื•ืœื” ื–ื• ืื ื• ืืžื•ืจื™ื ืœืงื‘ืœ ื”ื•ื“ืขืช ืฉื’ื™ืื”: Uncaught TypeError (ืœื ื ื™ืชืŸ ืœืงืจื•ื ืžืืคื™ื™ืŸ ืž-Undefined email) ืขืงื‘ ื—ืกืจ ืื•ื‘ื™ื™ืงื˜ ืžืฉืชืžืฉ. ืืชื” ื™ื›ื•ืœ ื’ื ืœื”ืฉืชืžืฉ ื—ืจื™ื’ Javascript.

<button type="button" onClick={() => console.log(user.email)}>   
  Test Error button 
</button>

ื”ืžื™ื›ืœ ื›ื•ืœื• ื ืจืื” ื›ืš:

import React, { Component } from "react";
import { connect } from "react-redux";
import { Input, List, Skeleton, Avatar } from "antd";
import * as Sentry from "@sentry/browser";
import getList from "../store/actions/getList";

const Search = Input.Search;

const mapState = state => ({
  list: state.root.list,
  loading: state.root.loading
});

const mapDispatch = {
  getList
};

class Container extends Component {
  constructor(props) {
    super(props);

    Sentry.init({
      dsn: "https://[email protected]/1417586",
    });
  }

  componentDidCatch(error, errorInfo) {
    Sentry.withScope(scope => {
      Object.keys(errorInfo).forEach(key => {
        scope.setExtra(key, errorInfo[key]);
      });
      Sentry.captureException(error);
    });
  }
  render() {
    const { list, loading, getList } = this.props;
    const user = undefined;
    return (
      <div className="App">
        <button
          type="button"
          onClick={() => console.log(user.email)}
        >
          test error1
        </button>
        <div onClick={() => Sentry.showReportDialog()}>Report feedback1</div>
        <h1>Music Finder</h1>
        <br />
        <Search onSearch={value => getList(value)} enterButton />
        {loading && <Skeleton avatar title={false} loading={true} active />}
        {!loading && (
          <List
            itemLayout="horizontal"
            dataSource={list}
            locale={{ emptyText: <div /> }}
            renderItem={item => (
              <List.Item>
                <List.Item.Meta
                  avatar={<Avatar src={item.artist.picture} />}
                  title={item.title}
                  description={item.artist.name}
                />
              </List.Item>
            )}
          />
        )}
      </div>
    );
  }
}

export default connect(
  mapState,
  mapDispatch
)(Container);

ืœืื—ืจ ืฉื™ืœื•ื‘ ื›ืคืชื•ืจ ื–ื”, ืขืœื™ืš ืœื‘ื“ื•ืง ืื•ืชื• ื‘ื“ืคื“ืคืŸ.

ืžืขืงื‘ ืื—ืจ ืฉื’ื™ืื•ืช ื‘ืืคืœื™ืงืฆื™ื™ืช React ื‘ืืžืฆืขื•ืช Sentry

ื™ืฉ ืœื ื• ืฉื’ื™ืื” ืจืืฉื•ื ื”

ืžืขืงื‘ ืื—ืจ ืฉื’ื™ืื•ืช ื‘ืืคืœื™ืงืฆื™ื™ืช React ื‘ืืžืฆืขื•ืช Sentry

ื”ื•-ื”ื•!

ืžืขืงื‘ ืื—ืจ ืฉื’ื™ืื•ืช ื‘ืืคืœื™ืงืฆื™ื™ืช React ื‘ืืžืฆืขื•ืช Sentry

ืื ืชืœื—ืฅ ืขืœ ืฉื’ื™ืืช ื”ื›ื•ืชืจืช, ืชืจืื” ืžืขืงื‘ ืžื—ืกื ื™ืช.

ืžืขืงื‘ ืื—ืจ ืฉื’ื™ืื•ืช ื‘ืืคืœื™ืงืฆื™ื™ืช React ื‘ืืžืฆืขื•ืช Sentry

ื”ื”ื•ื“ืขื•ืช ื ืจืื•ืช ืจืข. ื›ืžื•ื‘ืŸ ืฉืจืื™ื ื• ื”ื•ื“ืขื•ืช ืฉื’ื™ืื” ืžื‘ืœื™ ืœื”ื‘ื™ืŸ ื”ื™ื›ืŸ ื ืžืฆื ื”ืงื•ื“. ื›ื‘ืจื™ืจืช ืžื—ื“ืœ ืื ื—ื ื• ืžื“ื‘ืจื™ื ืขืœ ืžืคืช ื”ืžืงื•ืจ ื‘-ReactJS ืžื›ื™ื•ื•ืŸ ืฉื”ื ืœื ืžื•ื’ื“ืจื™ื.

ืื ื™ ื’ื ืจื•ืฆื” ืœืกืคืง ื”ื•ืจืื•ืช ืœื”ื’ื“ืจืช ืžืคืช ื”ืžืงื•ืจ, ืื‘ืœ ื–ื” ื™ื’ืจื•ื ืœืžืืžืจ ื–ื” ืœื”ื™ื•ืช ืืจื•ืš ื‘ื”ืจื‘ื” ืžืžื” ืฉื”ืชื›ื•ื•ื ืชื™.

ืืชื” ื™ื›ื•ืœ ืœืœืžื•ื“ ืืช ื”ื ื•ืฉื ื”ื–ื” ื›ืืŸ. ืื ืืชื” ืžืขื•ื ื™ื™ืŸ ื‘ืžืืžืจ ื–ื”, ื“ืžื™ื˜ืจื™ ื ื•ื–'ื ืงื• ื™ืคืจืกื ืืช ื”ื—ืœืง ื”ืฉื ื™ ืขืœ ืฉื™ืœื•ื‘ ืžืคื•ืช ืžืงื•ืจ. ืื–, ืชืœื—ืฅ ืขืœ ืขื•ื“ ืœื™ื™ืงื™ื ื•ื”ื™ืจืฉื ืœ ื“ืžื™ื˜ืจื™ ื ื•ื–'ื ืงื•ื›ื“ื™ ืœื ืœืคืกืคืก ืืช ื”ื—ืœืง ื”ืฉื ื™.

5. ื”ืฉืชืžืฉ Sentry ืขื ื ืงื•ื“ืช ืกื™ื•ื API

ื‘ืกื“ืจ. ื›ื™ืกื™ื ื• ืืช ื—ืจื™ื’ ื”-javascript ื‘ืคืกืงืื•ืช ื”ืงื•ื“ืžื•ืช. ืขื ื–ืืช, ืžื” ื ืขืฉื” ืœื’ื‘ื™ ืฉื’ื™ืื•ืช XHR?

ืœ- Sentry ื™ืฉ ื’ื ื˜ื™ืคื•ืœ ื‘ืฉื’ื™ืื•ืช ืžื•ืชืื ืื™ืฉื™ืช. ื”ืฉืชืžืฉืชื™ ื‘ื• ื›ื“ื™ ืœืขืงื•ื‘ ืื—ืจ ืฉื’ื™ืื•ืช API.

Sentry.captureException(err)

ืืชื” ื™ื›ื•ืœ ืœื”ืชืื™ื ืื™ืฉื™ืช ืืช ืฉื ื”ืฉื’ื™ืื”, ื”ืจืžื”, ืœื”ื•ืกื™ืฃ ื ืชื•ื ื™ื, ื ืชื•ื ื™ ืžืฉืชืžืฉ ื™ื™ื—ื•ื“ื™ื™ื ื‘ืืžืฆืขื•ืช ื”ืืคืœื™ืงืฆื™ื”, ื”ื“ื•ื"ืœ ืฉืœืš ื•ื›ื•'.

superagent
  .get(`https://deezerdevs-deezer.p.rapidapi.com/search?q=${query}`)
  .set("X-RapidAPI-Key", #id_key)
  .end((err, response) => {
    if (err) {
      Sentry.configureScope(
        scope => scope
          .setUser({"email": "[email protected]"})
          .setLevel("Error")
      );
      return Sentry.captureException(err);
    }

    if (response) {
      return dispatch(setList(response.body.data));
    }
  });

ืื ื™ ืจื•ืฆื” ืœื”ืฉืชืžืฉ ื‘ืคื•ื ืงืฆื™ื” ื’ื ืจื™ืช ืขื‘ื•ืจ ื”-Cats API.

import * as Sentry from "@sentry/browser";

export const apiCatch = (error, getState) => {
  const store = getState();
  const storeStringify = JSON.stringify(store);
  const { root: { user: { email } } } = store;

  Sentry.configureScope(
    scope => scope
      .setLevel("Error")
      .setUser({ email })
      .setExtra("store", storeStringify)
  );
    // Sentry.showReportDialog(); - If you want get users feedback on error
  return Sentry.captureException(error);
};

ื™ื™ื‘ื ืืช ื”ืคื•ื ืงืฆื™ื” ื”ื–ื• ื‘ืฉื™ื—ืช ื”-API ืฉืœืš.

export default query => (dispatch, getState) => {
  superagent
    .get(`https://deezerdevs-deezer.p.rapidapi.com/search?q=${query}`)
    .set("X-RapidAPI-Key", #id_key)
    .end((error, response) => {
      if (error) {
        return apiCatch(error, getState)
      }

      if (response) {
        return dispatch(setList(response.body.data));
      }
    });
};

ื‘ื•ืื• ื ื‘ื“ื•ืง ืืช ื”ืฉื™ื˜ื•ืช:

  • setLevel ืžืืคืฉืจ ืœืš ืœื”ื›ื ื™ืก ืฉื’ื™ืืช ืจืžื” ืœืœื•ื— ื”ืžื—ื•ื•ื ื™ื ืฉืœ ื”ื–ืงื™ืฃ. ื™ืฉ ืœื• ืžืืคื™ื™ื ื™ื - 'ืคื˜ืืœื™', 'ืฉื’ื™ืื”', 'ืื–ื”ืจื”', 'ื™ื•ืžืŸ', 'ืžื™ื“ืข, 'ื ื™ืคื•ื™ ื‘ืื’ื™ื', 'ืงืจื™ื˜ื™').
  • setUser ืขื•ื–ืจ ืœืฉืžื•ืจ ื›ืœ ื ืชื•ื ื™ ืžืฉืชืžืฉ (ืžื–ื”ื”, ื›ืชื•ื‘ืช ื“ื•ื"ืœ, ืชื•ื›ื ื™ืช ืชืฉืœื•ื ื•ื›ื•').
  • setExtra ืžืืคืฉืจ ืœืš ืœืฆื™ื™ืŸ ื›ืœ ื ืชื•ื ื™ื ืฉืืชื” ืฆืจื™ืš, ืœืžืฉืœ, ืœืื—ืกืŸ.

ืื ืืชื” ืจื•ืฆื” ืœืงื‘ืœ ืžืฉื•ื‘ ืžืžืฉืชืžืฉื™ื ืขืœ ื‘ืื’, ืขืœื™ืš ืœื”ืฉืชืžืฉ ื‘ืคื•ื ืงืฆื™ื” showReportDialog.

Sentry.showReportDialog();

ืžืกืงื ื”:

ื”ื™ื•ื ืชื™ืืจื ื• ื“ืจืš ืื—ืช ืœืฉื™ืœื•ื‘ Sentry ื‘ืืคืœื™ืงืฆื™ื™ืช React.

โ†’ ืฆ'ืื˜ ื‘ื˜ืœื’ืจื ืžืืช Sentry

ืžืงื•ืจ: www.habr.com

ื”ื•ืกืคืช ืชื’ื•ื‘ื”