Sentry-javascript: рдЧреИрд░-рддреНрд░реБрдЯрд┐ рдЕрдкрд╡рд╛рдж рдХреБрдВрдЬрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рдХреИрдкреНрдЪрд░ рдХрд┐рдпрд╛ рдЧрдпрд╛: рддреНрд░реБрдЯрд┐, рд╢реАрд░реНрд╖рд▓реЗрдЦ, рд╕рдВрджреЗрд╢, рдирд╛рдо, рдареАрдХ

рдХреЛ рдирд┐рд░реНрдорд┐рдд 28 рдЕрдХреНрддреВре░ 2019  ┬╖  48рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: getsentry/sentry-javascript

  • [x] рджрд╕реНрддрд╛рд╡реЗрдЬрд╝реАрдХрд░рдг рдХреА рд╕рдореАрдХреНрд╖рд╛ рдХрд░реЗрдВ: https://docs.sentry.io/
  • [x] рдореМрдЬреВрджрд╛ рдореБрджреНрджреЛрдВ рдХреА рдЦреЛрдЬ рдХрд░реЗрдВ :
  • [x] рдирд╡реАрдирддрдо рд░рд┐рд▓реАрдЬрд╝ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ: https://github.com/getsentry/sentry-javascript/releases
  • [x] рдЕрдкрдиреЗ рд╕рдВрддрд░реА рдЦрд╛рддреЗ рд╕реЗ рдкреНрд░рднрд╛рд╡рд┐рдд рдШрдЯрдирд╛ рдХрд╛ рд▓рд┐рдВрдХ рдкреНрд░рджрд╛рди рдХрд░реЗрдВ (https://sentry.io/share/issue/0f0aeecaa08746389b64945abd4544fd/)

рдкреИрдХреЗрдЬ + рд╕рдВрд╕реНрдХрд░рдг

  • [рдПрдХреНрд╕] @sentry/browser
  • [ ] @sentry/node
  • [ ] raven-js
  • [ ] raven-node _(рдиреЛрдб рдХреЗ рд▓рд┐рдП рд░реЗрд╡реЗрди)_
  • [ ] рдЕрдиреНрдп:
  • [рдПрдХреНрд╕] @angular/core

рд╕рдВрд╕реНрдХрд░рдг:

5.7.1 (@sentry/browser)
8.2.11 (@angular/core)

рд╡рд┐рд╡рд░рдг

рдореЗрд░реЗ рдкрд╛рд╕ рд╡реИрд╢реНрд╡рд┐рдХ рддреНрд░реБрдЯрд┐рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯрд░ рдХреЗ рд╕рд╛рде рдХреЛрдгреАрдп рдРрдк рдХреЗ рд▓рд┐рдП рдкреНрд░рд╛рд░рдВрднрд┐рдХ рд╕реЗрдЯрдЕрдк рд╣реИ
рдпрд╣рд╛рдБ рдореИрдВ рдЗрд╕реЗ рдХреИрд╕реЗ рднреЗрдЬрддрд╛ рд╣реВрдБ

    const eventId = Sentry.captureException(error.originalError || error);
    Sentry.showReportDialog({ eventId });

рдФрд░ рдореБрдЭреЗ рдпрд╣ рдорд┐рд▓рддрд╛ рд╣реИ (рдХреБрдВрдЬреА рдХреЗ рд╕рд╛рде рдХрдмреНрдЬрд╛ рдХрд░ рд▓рд┐рдпрд╛ рдЧрдпрд╛ рдЧреИрд░-рддреНрд░реБрдЯрд┐ рдЕрдкрд╡рд╛рдж: рддреНрд░реБрдЯрд┐, рд╢реАрд░реНрд╖рд▓реЗрдЦ, рд╕рдВрджреЗрд╢, рдирд╛рдо, рдареАрдХ) рддреНрд░реБрдЯрд┐ рдмрд╛рд░-рдмрд╛рд░, рдФрд░ рдпрд╣ рдирд╣реАрдВ рд╕рдордЭ рд╕рдХрддрд╛ рдХрд┐ рд╡рд┐рд╡рд░рдг рд╕реЗ рдХреНрдпрд╛ рдЧрд▓рдд рд╣реИ рдФрд░ рдЗрд╕реЗ рдкреБрди: рдЙрддреНрдкрдиреНрди рдХреИрд╕реЗ рдХрд░реЗрдВред

рд╕рдмрд╕реЗ рдЙрдкрдпреЛрдЧреА рдЯрд┐рдкреНрдкрдгреА

рд╡рд░реНрддрдорд╛рди рдореЗрдВ рд╣рдо рдЗрд╕реЗ рдЕрдирджреЗрдЦрд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣реЗ рд╣реИрдВред

Sentry.init({
  ignoreErrors: [
    'Non-Error exception captured'
  ]
});

рд╕рднреА 48 рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рдЖрдкрдХреЗ рджреНрд╡рд╛рд░рд╛ рдкреНрд░рджрд╛рди рдХреА рдЬрд╛рдиреЗ рд╡рд╛рд▓реА рд╡рд╕реНрддреБ Error рдХрд╛ рдЙрджрд╛рд╣рд░рдг рдирд╣реАрдВ рд╣реИ рдЬреЛ рдХреЛрдгреАрдп рдРрдк рдореЗрдВ рд╕реНрдЯреИрдХрдЯреНрд░реЗрд╕ рд░рдЦрддреА рд╣реИред

рдЖрдкрдХреЛ (рдЬреИрд╕рд╛ рдХрд┐ рд╕рдВрджреЗрд╢ рдЗрдВрдЧрд┐рдд рдХрд░рддрд╛ рд╣реИ) рдЕрдкрдиреА рдЖрд╡рд╢реНрдпрдХрддрд╛рдУрдВ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ Sentry.captureException(error.error) рдпрд╛ Sentry.captureException(error.message) рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред

@kamilogorek рдУрд╣ рдареАрдХ рд╣реИ) tnx

@kamilogorek рдЕрднреА рднреА рдпрд╣ рддреНрд░реБрдЯрд┐ рдкреНрд░рд╛рдкреНрдд рдХрд░ рд░рд╣рд╛ рд╣реИ, рдпрд╣рд╛рдВ рддрдХ тАЛтАЛтАЛтАЛрдХрд┐ рдЗрд╕рдХреЗ рд╕рд╛рде рднреА

    const exception = error.error || error.message || error.originalError || error;
    const eventId = Sentry.captureException(exception);

+1, рдЕрднреА рднреА рдпрд╣ рддреНрд░реБрдЯрд┐ рд╣реЛ рд░рд╣реА рд╣реИ

рд╕рдВрддрд░реА.рддреНрд░реБрдЯрд┐-рд╣реИрдВрдбрд▓рд░.ts

export class SentryErrorHandler extends GeneralErrorHandler {
  ...
  handleError(error) {
    ...
    const exception = error.originalError || error.error || error
    Sentry.captureException(exception)
  }
}

рдкреИрдХреЗрдЬ.рдЬреЗрд╕рди

{
  ...
  "@angular/core": "^8.2.11",
  "@sentry/browser": "^5.7.1",
  ...
}

рдореИрдВрдиреЗ рдРрд╕рд╛ рдХрд░рдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛

Sentry.init({
            dsn: environment.sentryUrl,
            beforeSend(event, hint) {
                /* tslint:disable:no-string-literal only-arrow-functions */
                const isNonErrorException =
                    event.exception.values[0].value.startsWith('Non-Error exception captured') ||
                    hint.originalException['message'].startsWith('Non-Error exception captured');
                /* tslint:enable:no-string-literal only-arrow-functions */

                if (isNonErrorException) {
                    // We want to ignore those kind of errors
                    return null;
                }
                return event;
            }
        });

рд╡рд░реНрдХрдЕрд░рд╛рдЙрдВрдб @gchronos рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рдж! рдореБрдЭреЗ рдЙрдореНрдореАрдж рд╣реИ рдХрд┐ рдЬрд▓реНрдж рд╣реА рдХрд┐рд╕реА рднреА рд╕рдордп рд╕рдВрднрд╛рд╡рд┐рдд рд╕реБрдзрд╛рд░ рд╣реЛрдЧрд╛ред

рдмрд╕ рдпрд╣ рдХрд╣рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐ рдореБрдЭреЗ рднреА рдпреЗ рддреНрд░реБрдЯрд┐рдпрд╛рдВ рдорд┐рд▓ рд░рд╣реА рд╣реИрдВред рдореИрдВ рдХреЛрдгреАрдп 8 рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣рд╛ рд╣реВрдВ рдФрд░ рдЗрд╕ рдкрд░ рдХреБрдЫ рдореБрджреНрджреЛрдВ рдХреЛ рдкрдврд╝рдиреЗ рдХреЗ рдмрд╛рдж, рдореБрдЭреЗ рдПрд╣рд╕рд╛рд╕ рд╣реБрдЖ рдХрд┐ рдпрд╣ рдореЗрд░реЗ рддреНрд░реБрдЯрд┐ рдкреНрд░рдмрдВрдзрди рдШрдЯрдХ рдореЗрдВ рддреНрд░реБрдЯрд┐ рдХреЛ рдареАрдХ рд╕реЗ рд╕рдВрднрд╛рд▓рдиреЗ рдХрд╛ рдорд╛рдорд▓рд╛ рдирд╣реАрдВ рд╣реЛ рд╕рдХрддрд╛ рд╣реИред рдореИрдВрдиреЗ рдХреИрдкреНрдЪрд░ рдЕрдкрд╡рд╛рдж рдХреЛ рдкрд╛рд░рд┐рдд рдХрд░рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ рдЕрдкрд╡рд╛рдж рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдЕрдиреНрдп рд▓реЛрдЧреЛрдВ рджреНрд╡рд╛рд░рд╛ рд╕реБрдЭрд╛рдП рдЧрдП рдХрд╛рдордХрд╛рдЬ рдХреА рдХреЛрд╢рд┐рд╢ рдХреА рд╣реИ рд▓реЗрдХрд┐рди рдЗрд╕рд╕реЗ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдореЗрдВ рдХрдореА рдирд╣реАрдВ рдЖрдИ рд╣реИред рдпрд╣ рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рд╣реЛрдЧрд╛ рдЕрдЧрд░ рдХреЛрдИ рдЗрд╕ рдкрд░ рдХреБрдЫ рдФрд░ рдЗрдирдкреБрдЯ рджреЗ рд╕рдХрддрд╛ рд╣реИ рдпрд╛ рдореБрдЭреЗ рдмрд╕ рдЬреАрдХреНрд░реЛрдиреЛрд╕ (рдзрдиреНрдпрд╡рд╛рдж!) рд╕рдорд╛рдзрд╛рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред

+1

+1

рдХреНрдпрд╛ рдХреЛрдИ рд░реЗрдкреНрд░реЛ рдкреНрд░рджрд╛рди рдХрд░ рд╕рдХрддрд╛ рд╣реИ рдЬрд┐рд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдореИрдВ рдЗрд╕реЗ рдбреАрдмрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ? рдореИрдВрдиреЗ рдмреЗрд╕ рдХреЛрдгреАрдп-рдХреНрд▓реА рдРрдк рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХреА рдФрд░ рдореИрдВ рдЗрд╕ рд╡реНрдпрд╡рд╣рд╛рд░ рдХреЛ рдкреБрди: рдкреЗрд╢ рдирд╣реАрдВ рдХрд░ рд╕рдХрддрд╛ред рдзрдиреНрдпрд╡рд╛рдж!

@kamilogorek рдореЗрд░рд╛ рдорд╛рдирдирд╛ тАЛтАЛтАЛтАЛрд╣реИ рдХрд┐ рдЖрдк рдПрдХ рдирдИ рдХреЛрдгреАрдп 8 рдкрд░рд┐рдпреЛрдЬрдирд╛ рдореЗрдВ рдкреНрд░рд╡реЗрд╢ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдФрд░ рдПрдХ рд╕рдорд╛рдкрди рдмрд┐рдВрджреБ 500 рдХреА рдУрд░ рдПрдХ http рдЕрдиреБрд░реЛрдз рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ http рд╕реЗрд╡рд╛ рдореЗрдВ рддреНрд░реБрдЯрд┐ рдХреЛ рдирд╣реАрдВ рдкрдХрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рдпрд╣ рд╕рдВрддрд░реА рдХреЛ рдкреНрд░рдЪрд╛рд░рд┐рдд рдХрд░рддрд╛ рд╣реИред

рдореИрдВ рдХреБрдЫ рдФрд░ рдЬрд╛рдирдХрд╛рд░реА рдХреЗ рд╕рд╛рде рдЬреБрдбрд╝ рд╕рдХрддрд╛ рд╣реВрдВ, рдпрд╣ рд╣рдорд╛рд░рд╛ рдПрд░рд░рд╣реИрдВрдбрд▓рд░ рд╣реИ:

@Injectable()
export class SentryErrorHandler implements ErrorHandler {
  constructor() { }
  handleError(error) {
    Sentry.captureException(error.originalError || error.error || error);
  }
}

рд╣рдорд╛рд░реЗ рд╕рдВрддрд░реА рдЯреНрд░реИрдХрд┐рдВрдЧ рдореЗрдВ рд╣рдореЗрдВ рд╣рдореЗрд╢рд╛ рдЗрдирдХреЗ рд▓рд┐рдП рдбреБрдкреНрд▓рд┐рдХреЗрдЯ рдИрд╡реЗрдВрдЯ рдорд┐рд▓рддреЗ рд╣реИрдВ:

  • Object.a рдЧреИрд░-рддреНрд░реБрдЯрд┐ рдЕрдкрд╡рд╛рдж рдХреБрдВрдЬрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рдХреИрдкреНрдЪрд░ рдХрд┐рдпрд╛ рдЧрдпрд╛: ...
  • captureException рдЧреИрд░ рддреНрд░реБрдЯрд┐ рдЕрдкрд╡рд╛рдж рдХреБрдВрдЬреА рдХреЗ рд╕рд╛рде рдХрдмреНрдЬрд╛ рдХрд░ рд▓рд┐рдпрд╛: ...

(рдЕрдиреНрдп рдШрдЯрдирд╛рдУрдВ рдХреЛ рд╕реВрдЪреА рдореЗрдВ рдПрдХ рдЖрдЗрдЯрдо рдХреЗ рд░реВрдк рдореЗрдВ рдареАрдХ рдмрддрд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ)

рд╣рдордиреЗ рдЗрдирдореЗрдВ рд╕реЗ рд╕реИрдХрдбрд╝реЛрдВ "рдЧреИрд░-рддреНрд░реБрдЯрд┐" -рдШрдЯрдирд╛рдУрдВ рдХреЛ рдПрдХрддреНрд░ рдХрд┐рдпрд╛ рд╣реИ, рдФрд░ рджрд┐рд▓рдЪрд╕реНрдк рдмрд╛рдд рдпрд╣ рд╣реИ рдХрд┐ рдЗрди рд╕рднреА рдореЗрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╕рдорд╛рди рд╣реИрдВ:

  • рдмреНрд░рд╛рдЙрдЬрд╝рд░.рдирд╛рдо: рд╕реИрдорд╕рдВрдЧ рдЗрдВрдЯрд░рдиреЗрдЯ 100%
  • рдмреНрд░рд╛рдЙрдЬрд╝рд░: рд╕реИрдорд╕рдВрдЧ рдЗрдВрдЯрд░рдиреЗрдЯ 9.4 100%

рдФрд░ рддреНрд░реБрдЯрд┐рдпрд╛рдВ рдЬреНрдпрд╛рджрд╛рддрд░ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╣реИрдВ:

{
  error: [Object], 
  headers: [Object], 
  message: Http failure during parsing for https://foo.bar/baz, 
  name: HttpErrorResponse, 
  ok: False, 
  status: 200, 
  statusText: OK, 
  url: https://foo.bar/baz
}

рдареАрдХ рд╣реИ, рдореИрдВрдиреЗ SentryErrorHandler рдХреЛ рдЗрдВрдЬреЗрдХреНрд╢рди рдпреЛрдЧреНрдп рдкреНрд░рджрд╛рди рдХрд┐рдпрд╛ рдФрд░ ErrorHandler рдХреЛ рд╡рд┐рд╕реНрддрд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рдмрдЬрд╛рдп рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдФрд░ рдореЗрд░реЗ рд▓рд┐рдП рдЗрд╕ рддрд░рд╣ рдХреА рдХреЛрдИ рдФрд░ рд╕рдорд╕реНрдпрд╛ рдирд╣реАрдВ рд╣реИред
рддреЛ рдореИрдВ . рд╕реЗ рдмрджрд▓ рдЧрдпрд╛

export class SentryErrorHandler extends ErrorHandler {

 constructor() {
     super();
 }

 handleError(err: any): void {
     if (environment.production === true || environment.preprod === true) {
         Sentry.captureMessage(err.originalError || err);
     }
     throw err;
 }
}

рдкреНрд░рддрд┐

@Injectable()
export class SentryErrorHandler implements ErrorHandler {

 constructor() { }

 handleError(err: any): void {
     if (environment.production === true || environment.preprod === true) {
         Sentry.captureException(err.originalError || err);
     }
     throw err;
 }
}

рдореЗрд░реЗ рдкрд╛рд╕ рдмрд┐рдирд╛ рдХрд┐рд╕реА рд╕рдорд╕реНрдпрд╛ рдХреЗ 2 рд╕рд╛рд▓ рдХреЗ рд▓рд┐рдП рдкрд╣рд▓рд╛ рдХреЙрдиреНрдлрд┐рдЧрд░ рдерд╛ рд▓реЗрдХрд┐рди рдПрдВрдЧреБрд▓рд░ 8 рдореЗрдВ рдЕрдкрдЧреНрд░реЗрдб рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж рд╕реЗ рдореЗрд░реЗ рдкрд╛рд╕ рдХрдИ рд╕рдВрддрд░реА рдЕрдкрд╡рд╛рдж рд╣реИрдВред

@ рдЬреЛрдирд╛рдерди-рдкрд╛рдпрд┐рдХ рдХреНрдпрд╛ рдЖрдк рдпрд╣ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реИрдВ рдХрд┐ рдЗрд╕рдХрд╛ рдХреНрдпрд╛ рдХрд╛рд░рдг рд╣реИ? рдпрд╛ рдЖрдк рдЕрднреА рдЙрди рд╕рднреА рдЧреИрд░-рддреНрд░реБрдЯрд┐ рдЕрдкрд╡рд╛рджреЛрдВ рдХреЛ рдЕрдирджреЗрдЦрд╛ рдХрд░рддреЗ рд╣реИрдВ?

рдлрд┐рд▓рд╣рд╛рд▓ рд╣рдо рдЙрдирдХреА рдЕрдирджреЗрдЦреА рдХрд░ рд░рд╣реЗ рд╣реИрдВред
рд╣рдордиреЗ рдмреНрд░реЗрдбрдХреНрд░рдВрдм рд╕реЗ рдкрддрд╛ рд▓рдЧрд╛рдпрд╛ рдХрд┐ рдРрд╕рд╛ рддрдм рд╣реЛрддрд╛ рд╣реИ рдЬрдм рд╡реЗрдмрд╡реНрдпреВ/рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдкреГрд╖реНрдарднреВрдорд┐ рдореЗрдВ рд╣реЛрддрд╛ рд╣реИ (рджрд┐рдЦрд╛рдИ рдирд╣реАрдВ рджреЗрддрд╛), рдЗрд╕рд▓рд┐рдП рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдлрд┐рдХреНрд╕ рдпрд╣ рд╣реИ рдХрд┐ рдкреЗрдЬ рджрд┐рдЦрд╛рдИ рди рджреЗрдиреЗ рдкрд░ рдлрд╝реЗрдЪ рд╣реЛрдиреЗ рд╕реЗ рд░реЛрдХрд╛ рдЬрд╛рдПред рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рд╕реИрдорд╕рдВрдЧ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдкреГрд╖реНрдарднреВрдорд┐ рдореЗрдВ рд╣реЛрдиреЗ рдкрд░ рдиреЗрдЯрд╡рд░реНрдХ рд░реАрдлреНрд░реЗрд╢ рдХреЛ рдмреБрд░реА рддрд░рд╣ рд╕реЗ рд╕рдВрднрд╛рд▓рддреЗ рд╣реИрдВред

@ рдЬреЛрдирд╛рдерди-рдкрд╛рдпрд┐рдХ рдЖрдк рдЙрдиреНрд╣реЗрдВ рдХреИрд╕реЗ рдЕрдирджреЗрдЦрд╛ рдХрд░ рд░рд╣реЗ рд╣реИрдВ? рдЕрдЧреНрд░рд┐рдо рдореЗрдВ рдзрдиреНрдпрд╡рд╛рджред

рд╡рд░реНрддрдорд╛рди рдореЗрдВ рд╣рдо рдЗрд╕реЗ рдЕрдирджреЗрдЦрд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣реЗ рд╣реИрдВред

Sentry.init({
  ignoreErrors: [
    'Non-Error exception captured'
  ]
});

@kamilogorek рдХреНрдпрд╛ рдЖрдк рдЗрд╕ рдореБрджреНрджреЗ рдХреЛ рдкреБрди: рдкреЗрд╢ рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдереЗ?

рд╕рдорд╕реНрдпрд╛ рдХреЛ рдмрдВрдж рдХрд░рдирд╛, рдХреНрдпреЛрдВрдХрд┐ рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдореВрд▓ рд╕рдорд╕реНрдпрд╛ рдЖрдВрд╢рд┐рдХ рд░реВрдк рд╕реЗ рд╣рд▓ рд╣реЛ рдЧрдИ рд╣реИ рдпрд╛ рдХреЛрдИ рдХрд╛рд░реНрдпрд╢реАрд▓ рд╕рдорд╛рдзрд╛рди рд╣реИред рдЕрдЧрд░ рдпрд╣ рдЕрднреА рднреА рдПрдХ рдореБрджреНрджрд╛ рд╣реИ рддреЛ рдореИрдВ рдХрд┐рд╕реА рдХреЛ рдирдП рд╡рд┐рд╡рд░рдг рдХреЗ рд╕рд╛рде рдПрдХ рдирдпрд╛ рдореБрджреНрджрд╛ рдмрдирд╛рдирд╛ рдкрд╕рдВрдж рдХрд░реВрдВрдЧрд╛ред
рдХреГрдкрдпрд╛ рдореБрдЭреЗ рдкрд┐рдВрдЧ рдХрд░рдиреЗ рдореЗрдВ рд╕рдВрдХреЛрдЪ рди рдХрд░реЗрдВ рдпрджрд┐ рдпрд╣ рдЕрднреА рднреА рдкреНрд░рд╛рд╕рдВрдЧрд┐рдХ рд╣реИ, рдФрд░ рдореИрдВ рдЦреБрд╢реА рд╕реЗ рдлрд┐рд░ рд╕реЗ рдЦреЛрд▓реВрдВрдЧрд╛ рдФрд░ рдЗрд╕ рдкрд░ рдХрд╛рдо рдХрд░реВрдВрдЧрд╛ред
рдЪреАрдпрд░реНрд╕!

рдЗрд╕рдХрд╛ рд╕рдорд╛рдзрд╛рди рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рдореИрдВрдиреЗ рд╕рд┐рд░реНрдл рд╕рдВрддрд░реА рдХреЛ рдПрдВрдЧреБрд▓рд░ 8 рдХреЗ рд╕рд╛рде рдПрдХреАрдХреГрдд рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХреА рдФрд░ рдЗрд╕ рдореБрджреНрджреЗ рдХреЛ рдкрд╛рдпрд╛ред

рдореБрдЭреЗ рд╕рдордЭ рдореЗрдВ рдирд╣реАрдВ рдЖрддрд╛ рдХрд┐ рд╕рдВрддрд░реА рдХреИрд╕реЗ рд▓реАрдХ рд╕реЗ рд╣рдЯрдХрд░ рджрд╛рд╡рд╛ рдХрд░ рд╕рдХрддрд╛ рд╣реИ, рдпрд╣ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдЭреВрда рд╣реИред

рдореБрдЭреЗ рд╕рдордЭ рдореЗрдВ рдирд╣реАрдВ рдЖрддрд╛ рдХрд┐ рд╕рдВрддрд░реА рдХреИрд╕реЗ рд▓реАрдХ рд╕реЗ рд╣рдЯрдХрд░ рджрд╛рд╡рд╛ рдХрд░ рд╕рдХрддрд╛ рд╣реИ, рдпрд╣ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдЭреВрда рд╣реИред

@Rush рдПрдХ рдкреНрд░рддрд┐рд▓рд┐рдкрд┐ рдкреНрд░рд╕реНрддреБрдд рдХрд░рдиреЗ рдпреЛрдЧреНрдп рдорд╛рдорд▓рд╛ рдкреНрд░рджрд╛рди рдХрд░реЗрдВ, рдЬрд╣рд╛рдВ рдЖрдк рд╕рдордЭрд╛рддреЗ рд╣реИрдВ рдФрд░ рджрд┐рдЦрд╛рддреЗ рд╣реИрдВ рдХрд┐ рдХреНрдпрд╛ рдЧрд▓рдд рд╣реИ, рдФрд░ рдлрд┐рд░ рд╣рдо рдЖрдкрдХреЗ рджрд╛рд╡реЗ рдХреЛ рд╕рддреНрдпрд╛рдкрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

@kamilogorek рд╕рдЪ рдХрд╣рд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП, рдХрд┐ 6 рдорд╣реАрдиреЗ рд╕реЗ рдЕрдзрд┐рдХ рд╕рдордп рд╕реЗ рдХреЛрдгреАрдп рдХреЗ рд╕рд╛рде рдХрд╛рдо рдирд╣реАрдВ рдХрд░ рд░рд╣рд╛ рд╣реИ, рдЗрд╕рдХреА рдкреБрд╖реНрдЯрд┐ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдпрд╣рд╛рдВ рдкрд░реНрдпрд╛рдкреНрдд рд░рд┐рдкреЛрд░реНрдЯ рд╣реИ, рдЗрд╕ рдкреНрд░рдХрд╛рд░ рдЖрдИрдПрдордУ рдХреЛ рдФрд░ рд░реЗрдкреНрд░реЛ рдорд╛рдорд▓реЛрдВ рдХреА рдХреЛрдИ рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ, рдЖрдк рдЗрд╕реЗ рдЖрд╕рд╛рдиреА рд╕реЗ рдЖрд╕рд╛рдиреА рд╕реЗ рдвреВрдВрдв рд╕рдХрддреЗ рд╣реИрдВред рдореИрдВ рдЦреБрдж рдХреЛ рдЬрд╛рдирддрд╛ рд╣реВрдВ рдХрд┐ рдРрд╕рд╛ рдирд╣реАрдВ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдХреЛрдгреАрдп v8, рдФрд░ рдЕрдм v9 (рдХреЛрдИ рд╕реНрд░реЛрддрдореИрдк рдирд╣реАрдВ) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╣рдорд╛рд░реА рдкрд░рд┐рдпреЛрдЬрдирд╛ рдореЗрдВ рдПрдХ рд╣реА рд╕рдорд╕реНрдпрд╛ рд╣реИред

рдмрд┐рд▓реНрдХреБрд▓, рдФрд░ рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛ рдореИрдВ рдореЗрд░реЗ рд▓рд┐рдП рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рдВрддрд░реА рдХреЗ рд▓рд┐рдП рднреБрдЧрддрд╛рди рдХрд░ рд░рд╣рд╛ рд╣реВрдБред рдпрд╣ рдПрдХ рдУрдкрди рд╕реЛрд░реНрд╕ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдирд╣реАрдВ рд╣реИ рдЬрд╣рд╛рдВ рдореИрдВ рдЕрдкрдиреЗ рд╣рд╛рде рдЧрдВрджреЗ рд╣реЛрдиреЗ рдХреА рдЙрдореНрдореАрдж рдХрд░рддрд╛ рд╣реВрдВред

рдореБрдЭреЗ рд╕рдВрддрд░реА рдкрд╕рдВрдж рд╣реИ рд▓реЗрдХрд┐рди рдореИрдВ рд╡рд┐рдХрд▓реНрдк рддрд▓рд╛рд╢ рд╕рдХрддрд╛ рдерд╛...

рдореИрдВрдиреЗ рдПрдХ рдЙрджрд╛рд╣рд░рдг рдХреЗ рд░реВрдк рдореЗрдВ рдПрдХ https://github.com/gothinkster/angular-realworld-example-app рдРрдк рдХрд╛ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдпрд╛, рддрд╛рдХрд┐ рд╣рд░ рдХреЛрдИ рднреА рд╕рдорд╛рди рдкреНрд░рдЬрдирди рдХрд░ рд╕рдХреЗред

рдмреЗрдпрд░рдмреЛрди рдХреНрд▓реЛрди рдРрдк, рд╕реЗрдВрдЯреНрд░реА рдХреЗ рдПрдВрдЧреБрд▓рд░ рдбреЙрдХреНрд╕ рдХрд╛ рдЕрдиреБрд╕рд░рдг рдХрд┐рдпрд╛ рдФрд░ рд╕реЛрд░реНрд╕рдореИрдкреНрд╕ рдЕрдкрд▓реЛрдб рдХрд┐рдпрд╛ред рдпрд╣рд╛рдБ рдкрд░рд┐рдгрд╛рдо рд╣реИрдВред


рдЙрджрд╛рд╣рд░рдг 1: рдЯреВрдЯрд╛ рд╣реБрдЖ рддрд░реНрдХ

рдПрдХ рддреНрд░реБрдЯрд┐ рд╣реИрдВрдбрд▓рд░ рдореЗрдВ рдХреЛрдгреАрдп рдЖрдкрдХреЛ рдХреНрдпрд╛ рджреЗрддрд╛ рд╣реИ + рд╣рдо рдХрд┐рд╕ рдШрдЯрдирд╛ рдХреЛ рдХреИрдкреНрдЪрд░ рдХрд░рддреЗ рд╣реИрдВ:

Screenshot 2020-04-16 at 12 01 13

рдпрд╣ UI рдореЗрдВ рдХреИрд╕рд╛ рджрд┐рдЦрддрд╛ рд╣реИ:

Screenshot 2020-04-16 at 12 07 02

рдиреЛрдЯ: рд╕рдм рдХреБрдЫ рдЬрдЧрд╣ рдкрд░ рд╣реИ, рдЖрдк рдЖрд╕рд╛рдиреА рд╕реЗ рдХреЛрдб рдХреА рд╕рд╣реА рд▓рд╛рдЗрди рдХреЛ рдЗрдВрдЧрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдЬреЛ рдЯреВрдЯрд╛ рд╣реБрдЖ рдерд╛, рддреНрд░реБрдЯрд┐ рд╕рдВрджреЗрд╢ рдФрд░ рддреНрд░реБрдЯрд┐ рдкреНрд░рдХрд╛рд░ рд╕рд╣реА рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдХреЛрдгреАрдп рдЖрдкрдХреЛ рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рд╕рдВрдкреВрд░реНрдг Error рдСрдмреНрдЬреЗрдХреНрдЯ рджреЗрддрд╛ рд╣реИред


рдЙрджрд╛рд╣рд░рдг 2: рдЯреВрдЯреА рд╣реБрдИ рд╕реЗрд╡рд╛ рдХреЙрд▓

рдПрдХ рддреНрд░реБрдЯрд┐ рд╣реИрдВрдбрд▓рд░ рдореЗрдВ рдХреЛрдгреАрдп рдЖрдкрдХреЛ рдХреНрдпрд╛ рджреЗрддрд╛ рд╣реИ + рд╣рдо рдХрд┐рд╕ рдШрдЯрдирд╛ рдХреЛ рдХреИрдкреНрдЪрд░ рдХрд░рддреЗ рд╣реИрдВ:

Screenshot 2020-04-16 at 12 00 49

рдпрд╣ UI рдореЗрдВ рдХреИрд╕рд╛ рджрд┐рдЦрддрд╛ рд╣реИ:

Screenshot 2020-04-16 at 12 07 12
Screenshot 2020-04-16 at 12 08 03

рдиреЛрдЯ: рдпрд╣ рдмрддрд╛рдиреЗ рдХрд╛ рдХреЛрдИ рддрд░реАрдХрд╛ рдирд╣реАрдВ рд╣реИ рдХрд┐ рдХрд┐рд╕ рдкреНрд░рдХрд╛рд░ рдХреА рддреНрд░реБрдЯрд┐ рд╣реБрдИ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдХреЛрдгреАрдп рдЖрдкрдХреЛ рдпрд╣ рдЬрд╛рдирдХрд╛рд░реА рдирд╣реАрдВ рджреЗрддрд╛ рд╣реИред рд╣рд╛рд▓рд╛рдБрдХрд┐, рдЖрдк рдпрд╣ рдмрддрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдХреНрд░рдордмрджреНрдз рдбреЗрдЯрд╛ рдкрд░ рдПрдХ рдирдЬрд╝рд░ рдбрд╛рд▓ рд╕рдХрддреЗ рд╣реИрдВ, рдХрд┐ "404 Not Found" рддреНрд░реБрдЯрд┐ рдереА, рдЬреЛ рдХрд┐ _рд╕рдмрд╕реЗ рдЕрдзрд┐рдХ рд╕рдВрднрд╛рд╡рдирд╛_ рдЕрдорд╛рдиреНрдп XHR рдХреЙрд▓ рд╕реЗ рдЖрдИ рдереАред рдлрд┐рд░, рдЖрдк рдпрд╣ рджреЗрдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдмреНрд░реЗрдбрдХреНрд░рдВрдм рдкреНрд░рд╡рд╛рд╣ рдХреА рдЬрд╛рдВрдЪ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ _in fact_ рджрд┐рдП рдЧрдП url рдкрд░ рдПрдХ XHR рдХреЙрд▓ рдерд╛ (рдЬрд┐рд╕рдореЗрдВ рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ рдПрдХ рдЯрд╛рдЗрдкреЛ рд╢рд╛рдорд┐рд▓ рдерд╛), рдЬреЛ рдПрдХ рд╕рдЯреАрдХ DOM рддрддреНрд╡ рдкрд░ рдПрдХ рдХреНрд▓рд┐рдХ рд╕реЗ рдкрд╣рд▓реЗ рдерд╛, рдЬрд┐рд╕рдиреЗ рдЗрд╕реЗ рдЯреНрд░рд┐рдЧрд░ рдХрд┐рдпрд╛ред рдЗрд╕ рдЬрд╛рдирдХрд╛рд░реА рдХреЗ рд╕рд╛рде, рд╕рдорд╕реНрдпрд╛ рдХреЛ рдареАрдХ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП 99% рдорд╛рдорд▓реЛрдВ рдореЗрдВ рдпрд╣ рдкрд░реНрдпрд╛рдкреНрдд рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред


рд╣рдо рдХреЗрд╡рд▓ рдЙрд╕реА рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдЬреЛ рд╣рдореЗрдВ рдврд╛рдВрдЪреЗ рджреНрд╡рд╛рд░рд╛ рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рдЕрдиреНрдпрдерд╛, рд╣рдореЗрдВ рдврд╛рдВрдЪреЗ рдХреЛ рд╣реА рдмрдВрджрд░-рдкреИрдЪ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред

рд╣рдо рдХреЗрд╡рд▓ рдЙрд╕реА рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдЬреЛ рд╣рдореЗрдВ рдврд╛рдВрдЪреЗ рджреНрд╡рд╛рд░рд╛ рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рдЕрдиреНрдпрдерд╛, рд╣рдореЗрдВ рдврд╛рдВрдЪреЗ рдХреЛ рд╣реА рдмрдВрджрд░-рдкреИрдЪ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред

рдХреНрдпрд╛ рдпрд╣ "рд╣рдорд╛рд░реА рд╕рдорд╕реНрдпрд╛ рдирд╣реАрдВ" рдХрд╣рдиреЗ рдХрд╛ рдЖрдкрдХрд╛ рддрд░реАрдХрд╛ рд╣реИ? рд╕рдВрддрд░реА рдкрд░ рд╕реНрд╡рд┐рдЪ рдХрд░рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ (рдХреБрдЫ рдкреНрд░рддрд┐рд╕реНрдкрд░реНрдзрд┐рдпреЛрдВ рд╕реЗ) рдЗрди рдПрдХреНрд╕рдПрдЪрдЖрд░ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЛ рдареАрдХ рд╕реЗ рдкрдХрдбрд╝ рд▓рд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ рдФрд░ рдЙрдиреНрд╣реЛрдВрдиреЗ рдХреЛрдгреАрдп рддреНрд░реБрдЯрд┐ рд╣реИрдВрдбрд▓рд░ рдХреЗ рд╕рд╛рде рдПрдХ рд╣реА рдЗрдВрд╕реНрдЯреЙрд▓ рд╡рд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдерд╛ред

@kamilogorek рдПрдХ рд╡рд┐рдХреНрд░реЗрддрд╛ рдХреЗ рд░реВрдк рдореЗрдВ рдЖрдк рдврд╛рдВрдЪреЗ рдХреЗ рд╕рд╛рде рдЯрд┐рдХрдЯ рднреА рдЦреЛрд▓ рд╕рдХрддреЗ рд╣реИрдВред рдореБрдЭреЗ рдпрдХреАрди рд╣реИ рдХрд┐ рдЗрд╕ рдкрд░ рдХреБрдЫ рдзреНрдпрд╛рди рджрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред

рд╕рдВрддрд░реА рдкрд░ рд╕реНрд╡рд┐рдЪ рдХрд░рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ (рдХреБрдЫ рдкреНрд░рддрд┐рд╕реНрдкрд░реНрдзрд┐рдпреЛрдВ рд╕реЗ) рдЗрди рдПрдХреНрд╕рдПрдЪрдЖрд░ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЛ рдареАрдХ рд╕реЗ рдкрдХрдбрд╝ рд▓рд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ рдФрд░ рдЙрдиреНрд╣реЛрдВрдиреЗ рдХреЛрдгреАрдп рддреНрд░реБрдЯрд┐ рд╣реИрдВрдбрд▓рд░ рдХреЗ рд╕рд╛рде рдПрдХ рд╣реА рдЗрдВрд╕реНрдЯреЙрд▓ рд╡рд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдерд╛ред

@szechyjs рддреЛ рдпрд╣ рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рд╣реЛрдЧрд╛ рдпрджрд┐ рдЖрдк рдЗрд╕рдХреЗ рдмрдЬрд╛рдп рд╕рдорд╛рдзрд╛рди рд╕рд╛рдЭрд╛ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдУрдкрди-рд╕реЛрд░реНрд╕ рдПрд╕рдбреАрдХреЗ рд╣реИ рдФрд░ рдпрджрд┐ рд╣рдо рдЬрд╛рдирддреЗ рд╣реИрдВ рдХрд┐ рд╣рдо рдХреНрдпрд╛ рд▓рдХреНрд╖реНрдп рдХрд░ рд░рд╣реЗ рд╣реИрдВ рддреЛ рд╣рдо рд╕реБрдзрд╛рд░реЛрдВ рдкрд░ рдкреБрдирд░рд╛рд╡реГрддрд┐ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

... рдЖрдк рдврд╛рдВрдЪреЗ рдХреЗ рд╕рд╛рде рдЯрд┐рдХрдЯ рднреА рдЦреЛрд▓ рд╕рдХрддреЗ рд╣реИрдВред рдореБрдЭреЗ рдпрдХреАрди рд╣реИ рдХрд┐ рдЗрд╕ рдкрд░ рдХреБрдЫ рдзреНрдпрд╛рди рджрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред

рдореИрдВрдиреЗ рдЕрднреА-рдЕрднреА рд╕рдВрддрд░реА рдХреЗ рд╕рдорд░реНрдерди рд╕реЗ рдПрдХ рдЯрд┐рдХрдЯ рдЦреЛрд▓рд╛ рдФрд░ рдЗрд╕ рдореБрджреНрджреЗ рдХрд╛ рд╣рд╡рд╛рд▓рд╛ рджрд┐рдпрд╛ред

рдпрд╣рд╛рдВ рдПрдХ рд░рд╛рд╣рдЧреАрд░ред рдореЗрд░реЗ рдорд╛рдорд▓реЗ рдХреЗ рд▓рд┐рдП, рдореИрдВ рдореВрд▓ рдирд┐рд╡рд╛рд╕реА рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдореЗрдВ рд╕рдВрднрд╛рд▓реЗ рдЧрдП рдЕрд╕реНрд╡реАрдХреГрддрд┐ рдХреА рд░рд┐рдкреЛрд░реНрдЯ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдФрд░ рдореИрдВ рдУрдкреА рдХреЗ рд╕рдорд╛рди рддреНрд░реБрдЯрд┐ рдХреЛ рджреЗрдЦрдХрд░ рд╕рдорд╛рдкреНрдд рд╣реБрдЖред рдЪреВрдВрдХрд┐ the object you provide is not an instance of Error , рдореИрдВ рдиреАрдЪреЗ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣рд╛ рд╣реВрдВред

export interface IError extends Error {
  code?: ErrorCode
  msg?: string
}

export const appError = (code: ErrorCode, msg: string = ''): IError => {
  const errmsg = `code: ${code}, msg: ${msg}`
  const err: IError = new Error(errmsg)
  err.code = code
  err.msg = msg
  err.name = 'appError'
  return err
}

рдлрд┐рд░ рдореИрдВ рдлреЛрди рдХрд░рддрд╛ рд╣реВрдБ

export const logError = (err: Error) => {
  sentry.captureException(err)
}

рдФрд░ рдореИрдВ рджреЗрдЦрддрд╛ рд╣реВрдБ

уВ╣уВпуГкуГ╝уГ│уВ╖уГзуГГуГИ 0032-05-24 1 58 38

рдЙрдореНрдореАрдж рд╣реИ рдпреЗ рдорджрдж рдХрд░реЗрдЧрд╛!

рдореЗрд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ рд╕рдВрддрд░реА рдХреЗ рдмреНрд░реЗрдбрдХреНрд░рдВрдм рдЕрдиреБрднрд╛рдЧ рдиреЗ рдореБрдЭреЗ рдХрд╛рд░рдг рдмрддрд╛рдиреЗ рдореЗрдВ рдмрд╣реБрдд рдорджрдж рдХреАред рдпрд╣ рдЬреНрдпрд╛рджрд╛рддрд░ рддрдм рд╣реБрдЖ рдЬрдм рдХреБрдЫ рдмрд╛рд╣рд░реА рдПрдкреАрдЖрдИ рдкрд░ 404 рдереЗред

рдбреЙрдХреНрд╕ рдЕрдкрдбреЗрдЯ рдФрд░ рдЖрд╡рд╢реНрдпрдХ рдЬреЗрдПрд╕ рдПрд╕рдбреАрдХреЗ рдкрд░рд┐рд╡рд░реНрддрди рдЖ рд░рд╣реЗ рд╣реИрдВ:

https://github.com/getsentry/sentry-docs/pull/1695/
https://github.com/getsentry/sentry-javascript/pull/2601

рдЗрд╕реЗ рдЗрд╕реА рд╣рдлреНрддреЗ рд░рд┐рд▓реАрдЬ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП! :)

рдЗрд╕рдХреЗ рд▓рд┐рдП рдЖрд╕рд╛рди рд╕реБрдзрд╛рд░ (рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдХреЗрд╡рд▓ HTTP рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯреЗрдб рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рд╣реЛрддрд╛ рд╣реИ):

handleError(error: Error | HttpErrorResponse | any) {
      // ... 
      if (error.constructor.name === "HttpErrorResponse") {
                error.error.Message + " (" + error.message + ")";
                error = error.error;
      }
      // ...
      Sentry.captureMessage(err.originalError || err.error || error);

     throw err;
 }

рдирдП рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рдФрд░ 5.16.0 рдЕрднреА рдЬрд╛рд░реА рдХрд┐рдП рдЧрдП рд╣реИрдВ - https://docs.sentry.io/platforms/javascript/angular/
рдореИрдВрдиреЗ рдЗрд╕реЗ рдпрдерд╛рд╕рдВрднрд╡ рд╕реНрдкрд╖реНрдЯ рдФрд░ рд╡рд┐рд╕реНрддреГрдд рдмрдирд╛рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХреА, рдФрд░ рдЗрд╕реАрд▓рд┐рдП рдпрд╣ рдмрд╣реБрдд рд╕рд╛рд░реЗ рдХреЛрдб рдХреА рддрд░рд╣ "рджрд┐рдЦ рд╕рдХрддрд╛ рд╣реИ"ред

@kamilogorek рдореБрдЭреЗ рдпрд╣ рд╕рдорд╕реНрдпрд╛ Vue рдРрдк рдкрд░ рднреА рдЖ рд░рд╣реА рд╣реИред рдХреНрдпрд╛ рд╡рд╣рд╛рдВ рдХреЛрдИ рдлрд┐рдХреНрд╕ рд╣реИ?

рдореЗрд░рд╛ рднреА рдпрд╣реА рд╡рд┐рдЪрд╛рд░ рд╣реИред рдЕрдкрд╡рд╛рдж рдХреЛ vuejs рд╕рдВрддрд░реА рдПрдХреАрдХрд░рдг рддреНрд░реБрдЯрд┐ рд╣реИрдВрдбрд▓рд░ рдореЗрдВ рдлреЗрдВрдХ рджрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред
https://sentry.io/share/issue/eaf13a2455e04150aaaab595d0d7bafe/

@sblawrie рдЖрдкрдХреЗ рдкрд╛рд╕ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХреНрдпрд╛ рд╕рдорд╕реНрдпрд╛ рд╣реИ? рдХреГрдкрдпрд╛ рдХреБрдЫ рдФрд░ рд╡рд┐рд╡рд░рдг рд╕рд╣рд╛рдпрдХ рд╣реЛрдВрдЧреЗред
@cincauhangus рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЖрдкрдХреЗ рдХреЛрдб рдореЗрдВ рдХрд╣реАрдВ рдЖрдк рд╡рд╛рджрд╛ рдЙрджрд╛рд╣рд░рдг рдХреЗ рд╕рд╛рде рдПрдХ рддреНрд░реБрдЯрд┐ рдлреЗрдВрдХ рд░рд╣реЗ рд╣реИрдВ (рдиреЛрдЯ abort, always, catch, done рдХреБрдВрдЬреА)ред

рдХреБрдЫ рдЗрд╕ рддрд░рд╣:

const promise = new Promise((resolve, reject) => (...));
// somehow somewhere in the code
function foo () {
  // ...
  throw promise
}

рддреЛ Vue рдХреЗ errorHandler рдХреЛ рдПрдХ рд╕рдВрдкреВрд░реНрдг рд╡рд╛рджрд╛ рд╡рд╕реНрддреБ рдорд┐рд▓ рд░рд╣реА рд╣реИ, рдЬрд╣рд╛рдВ рдпрд╣ Error рдЙрджрд╛рд╣рд░рдг рд╣реЛрдиреЗ рдХреА рдЙрдореНрдореАрдж рдХрд░рддрд╛ рд╣реИред

@kamilogorek рд╕реНрдкрд╖реНрдЯреАрдХрд░рдг рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рджред рдореИрдВрдиреЗ рдХреБрдЫ рдмрджрд▓рд╛рд╡ рдХрд┐рдП рд╣реИрдВ рдФрд░ рдЕрдЧрд░ рдпрд╣ рдЕрднреА рднреА рдХреЛрдИ рд╕рдорд╕реНрдпрд╛ рд╣реИ рддреЛ рдореИрдВ рдЗрд╕рдХреА рдирд┐рдЧрд░рд╛рдиреА рдХрд░реВрдВрдЧрд╛ред

рдХреЛрдгреАрдп 8 рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдФрд░ рдПрдХ рд╣реА рд╕рдорд╕реНрдпрд╛ рдЧреИрд░-рддреНрд░реБрдЯрд┐ рдЕрдкрд╡рд╛рдж рдерд╛ред рдЙрджрд╛рд╣рд░рдг 'extractError' рдХреЛрдб рдХреЛ рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░реЗрдВ:

private static extractError(error: any) {
    // Try to unwrap zone.js error.
    // https://github.com/angular/angular/blob/master/packages/core/src/util/errors.ts
    if (error && error.ngOriginalError) {
      error = error.ngOriginalError;
    }
    // We can handle messages and Error objects directly.
    if (typeof error === 'string' || error instanceof Error) {
      return error;
    }
    // If it's http module error, extract as much information from it as we can.
    if (error instanceof HttpErrorResponse) {
      // The `error` property of http exception can be either an `Error` object, which we can use directly...
      if (error.error instanceof Error) {
        return error.error;
      }
      // ... or an`ErrorEvent`, which can provide us with the message but no stack...
      if (error.error instanceof ErrorEvent) {
        return error.error.message;
      }
      // ...or the request body itself, which we can use as a message instead.
      if (typeof error.error === 'string') {
        return `Server returned code ${error.status} with body "${error.error}"`;
      }
      // If we don't have any detailed information, fallback to the request message itself.
      return error.message;
    }

    // ***** CUSTOM *****
    // The above code doesn't always work since 'instanceof' relies on the object being created with the 'new' keyword
    if (error.error && error.error.message) {
      return error.error.message;
    }
    if (error.message) {
      return error.message;
    }
    // ***** END CUSTOM *****

    // Skip if there's no error, and let user decide what to do with it.
    return null;
  }

рдореЗрд░реА Sentry.init beforeSend рдКрдкрд░ @untilinvite рдХреЗ beforeSend рдЙрджрд╛рд╣рд░рдг рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рд╕рдВрд╢реЛрдзрд┐рддред рдпрд╣ рдкрд░рд┐рд╡рд░реНрддрди https://github.com/getsentry/sentry-javascript/issues/2169 рд╕реЗ рдПрдХ рджреВрд╕рд░реЗ рд▓реЙрдЧ рд╕рдВрджреЗрд╢ рдХреЛ рд╕рдВрднрд╛рд▓рдиреЗ рдХреЗ рд▓рд┐рдП рд╣реИ, рдЬреЛ рдЧреИрд░-рддреНрд░реБрдЯрд┐ рдХреЗ рд░реВрдк рдореЗрдВ рднреА рд╣рд▓ рд╣реЛ рд░рд╣рд╛ рд╣реИред

 Sentry.init({
        dsn: AppConfig.envSettings.sentryDSN,
        maxBreadcrumbs: 50,
        environment: this.getEnvName(),
        integrations: [new Sentry.Integrations.Breadcrumbs({ console: false })],
        beforeSend(event, hint) {
          // Note: issue with double entries during http exceptions: https://github.com/getsentry/sentry-javascript/issues/2169
          // Note: issue with a second entry not being set correctly (as a non-error): https://github.com/getsentry/sentry-javascript/issues/2292#issuecomment-554932519
          const isNonErrorException = event.exception.values[0].value.startsWith('Non-Error exception captured');
          if (isNonErrorException) {
            if (!event.extra.__serialized__) {
              return null;
            }
            let realErrMsg = event.extra.__serialized__.error ? event.extra.__serialized__.error.message : null;
            realErrMsg = realErrMsg || event.extra.__serialized__.message;
            // this is a useless error message that masks the actual error.  Lets try to set it properly
            event.exception.values[0].value = realErrMsg;
            event.message = realErrMsg;
          }
          return event;
        }
      });

рд╣рдордиреЗ 5.16.0 рд░рд┐рд▓реАрдЬ рдХреЗ рд╕рд╛рде рдбреЙрдХреНрд╕ рдореЗрдВ рдЬреЛрдбрд╝реЗ рдЧрдП рдкрд░рд┐рд╡рд░реНрддрди рдХрд┐рдП рд╣реИрдВ рдФрд░ рд╣рдореЗрдВ рдЕрднреА рднреА Non-Error exception captured with keys: error, headers, message, name, ok рддреНрд░реБрдЯрд┐рдпрд╛рдВ рдорд┐рд▓ рд░рд╣реА рд╣реИрдВред рдореБрдЭреЗ рдЙрдореНрдореАрдж рд╣реИ рдХрд┐ @ рд╕рдВрддрд░реА/рдХреЛрдгреАрдп рдкреИрдХреЗрдЬ рдЗрди рдореБрджреНрджреЛрдВ рдХреЛ рдареАрдХ рдХрд░реЗрдЧрд╛?

рдХреЛрдгреАрдп: v9.1
рд╕рдВрддрд░реА: v5.20

@szechyjs рдХреНрдпрд╛ рдЖрдкрдиреЗ http рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯрд░ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рджреВрд╕рд░рд╛ рднрд╛рдЧ рднреА рдкрдврд╝рд╛? рдпрд╣ рдЖрдорддреМрд░ рдкрд░ рддреНрд░реБрдЯрд┐ рдХрд╛ рд╕рд╣реА рдврдВрдЧ рд╕реЗ рдкрддрд╛ рди рд▓рдЧрд╛рдиреЗ рдХрд╛ рдореБрдЦреНрдп рдореБрджреНрджрд╛ рд╣реИ - https://docs.sentry.io/platforms/javascript/angular/

@szechyjs рдХреНрдпрд╛ рдЖрдкрдиреЗ http рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯрд░ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рджреВрд╕рд░рд╛ рднрд╛рдЧ рднреА рдкрдврд╝рд╛? рдпрд╣ рдЖрдорддреМрд░ рдкрд░ рддреНрд░реБрдЯрд┐ рдХрд╛ рд╕рд╣реА рдврдВрдЧ рд╕реЗ рдкрддрд╛ рди рд▓рдЧрд╛рдиреЗ рдХрд╛ рдореБрдЦреНрдп рдореБрджреНрджрд╛ рд╣реИ - https://docs.sentry.io/platforms/javascript/angular/

рд╣рдо рдЗрдВрдЯрд░рд╕реЗрдкреНрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░ рд░рд╣реЗ рд╣реИрдВред

рдирдорд╕реНрддреЗ, рдореИрдВрдиреЗ рдЗрд╕ рдореБрджреНрджреЗ рдкрд░ рдХреБрдЫ рд╕рдордп рдмрд┐рддрд╛рдпрд╛ рд╣реИ рдФрд░ рдкрд╛рдпрд╛ рд╣реИ рдХрд┐ error.error.message рдкреНрд░рдХрд╛рд░ рдХреА рддреНрд░реБрдЯрд┐рдпреЛрдВ рдкрд░ HttpErrorResponse рдЖрд╡рд╢реНрдпрдХ рд░реВрдк рд╕реЗ рдХреЛрдИ рдЬрд╛рдирдХрд╛рд░реА рдирд╣реАрдВ рд╣реИред рдЕрдЧрд░ рд╣рдо рдХрдВрд╕реНрдЯреНрд░рдХреНрдЯрд░ рдХреЛ рджреЗрдЦреЗрдВ рддреЛ рд╣рдо рджреЗрдЦреЗрдВрдЧреЗ рдХрд┐ рдПрдВрдЧреБрд▓рд░ рдореИрд╕реЗрдЬ рдкреНрд░реЛрдк рдХреЛ рд░реВрдЯ рдСрдмреНрдЬреЗрдХреНрдЯ рдкрд░ рд╕реЗрдЯ рдХрд░рддрд╛ рд╣реИ рди рдХрд┐ ErrorEvent ред

https://github.com/angular/angular/blob/cb3db0d31b91bea14c7f567fe7e7220b9592c11b/packages/common/http/src/response.ts#L345

рдЗрд╕ рд▓рд╛рдЗрди рдХреЛ рдмрджрд▓рдХрд░ https://github.com/getsentry/sentry-javascript/blob/8eb72865dcd62ff719441105c7eda28007a07e9d/packages/angular/src/errorhandler.ts#L112
рдкреНрд░рддрд┐

if (error.error instanceof ErrorEvent && error.error.message)

рддреНрд░реБрдЯрд┐ рд╣реИрдВрдбрд▓рд░ return error.message; рдФрд░ рдЕрдкреЗрдХреНрд╖рд┐рдд рддреНрд░реБрдЯрд┐ рдлреЗрдВрдХ рджреАред

рд╢рд╛рдирджрд╛рд░ рдХреИрдЪ @jakkn , рдзрдиреНрдпрд╡рд╛рдж! рдкреАрдЖрд░ рдореЗрдВ рдЕрдкрдбреЗрдЯ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИрдВрдбрд▓рд░ https://github.com/getsentry/sentry-javascript/pull/2903

рдореБрдЭреЗ рдпрд╣ рд╕рдорд╕реНрдпрд╛ рдПрдХ рдПрдХреНрд╕рдкреНрд░реЗрд╕ рдРрдк рдкрд░ рдЖ рд░рд╣реА рд╣реИ рдЬрд┐рд╕реЗ рдореИрдВрдиреЗ рдЕрднреА рд╡рд┐рд░рд╛рд╕рдд рдореЗрдВ рд▓рд┐рдпрд╛ рд╣реИред рдпрд╣ рдирд╣реАрдВ рдкрддрд╛ рдХрд┐ рдпрд╣ рдХреИрд╕реЗ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдРрдк рдлреЗрдВрдХрдиреЗ рд╡рд╛рд▓реА рдЕрдзрд┐рдХрд╛рдВрд╢ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рд╣реЛ рд░рд╣рд╛ рд╣реИ, рдФрд░ рдореИрдВ рджрд╕реНрддрд╛рд╡реЗрдЬрд╝реЛрдВ рд╕реЗ рдорд╛рдирдХ рдПрдХреНрд╕рдкреНрд░реЗрд╕ рд╕реЗрдЯрдЕрдк рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣рд╛ рд╣реВрдВред

рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП https://sentry.io/share/issue/bfd4f674c10b4b4a8b6a291dde8e2a66/

рдореИрдВрдиреЗ рдпрд╣ рд╡рд╣реА рддреНрд░реБрдЯрд┐ рдорд╛рд░рд╛, рд╣рд╛рд▓рд╛рдВрдХрд┐ рдпрд╣ рдПрдХреНрд╕рдкреНрд░реЗрд╕ рдЕрдиреБрд░реЛрдз рд╣реИрдВрдбрд▓рд░ рдХреЗ рд▓рд┐рдП рдерд╛ рдФрд░ рдХреЛрдгреАрдп рдирд╣реАрдВред рдореВрд▓ рд░реВрдк рд╕реЗ рдореИрдВ рдПрдХ рддреНрд░реБрдЯрд┐ рд╡рд╕реНрддреБ рдХреЗ рдмрдЬрд╛рдп рдПрдХ рдкреВрдЬреЛ рдХреЗ рд╕рд╛рде next рдХреЙрд▓ рдХрд░ рд░рд╣рд╛ рдерд╛ред рдореИрдВ рдЕрдкрдиреЗ рд╕реНрд╡рдпрдВ рдХреЗ рдПрдХреНрд╕рдкреНрд░реЗрд╕ рддреНрд░реБрдЯрд┐ рд╣реИрдВрдбрд▓рд░ рдореЗрдВ рдЗрд╕реЗ рд░реБрдЪрд┐ рдХреЗ рдХреБрдЫ рдореЗрдВ рдмрджрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рдХреБрдЫ рдордЬреЗрджрд╛рд░ рдЪреАрдЬреЗрдВ рдХрд░рддрд╛ рд╣реВрдВ, рд▓реЗрдХрд┐рди рдХреНрдпреЛрдВрдХрд┐ рд╕рдВрддрд░реАрдУ рдЕрдиреБрд░реЛрдз рдорд┐рдбрд▓рд╡реЗрдпрд░ рдкрд╣рд▓реЗ рдЬрд╛рддрд╛ рд╣реИ, рдЗрд╕реЗ рдпрд╣ рдорд╛рд░реНрд╢рд▓ рддреНрд░реБрдЯрд┐ рдирд╣реАрдВ рдорд┐рд▓реАред

рдЕрдВрдд рдореЗрдВ, рдПрдХ рдЙрдкрдпреЛрдЧрд┐рддрд╛ рдлрд╝рдВрдХреНрд╢рди рдмрдирд╛рдпрд╛ рдЬреЛ рдХрд┐ рдЬреЛ рдХреБрдЫ рднреА рдкреНрд░рджрд╛рди рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ рдЙрд╕реЗ рдФрд░ рдЕрдзрд┐рдХ рдЙрдЪрд┐рдд рдореЗрдВ рдмрджрд▓ рджреЗрдЧрд╛:

export const handleMaybeError = err => {
  if (err instanceof Error) return err
  const newErr = new Error(err.message || 'unexpected')
  for (const [key, value] of Object.entries(err)) {
    newErr[key] = value
  }
  return newErr
}

export const someController = (req, res, next) => {
  try {
    await handleResponse(req, res)
  } catch (err) {
    next(handleMaybeError(err))
  }
}

рдпрд╣ рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рд╕реНрдЯреИрдХ рдХреЗ рдирд┐рд╢рд╛рди рдХреЛ рдмрдврд╝рд╛рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЕрдЧрд░ рдпрд╣ рдПрдХ рдкреВрдЬреЛ рдореЗрдВ рдкрд╛рд░рд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рддреЛ рд╡реИрд╕реЗ рднреА рдХреЛрдИ рд╕реНрдЯреИрдХ рдЯреНрд░реЗрд╕ рдирд╣реАрдВ рдерд╛ред

рд╣рдорд╛рд░реЗ рдЙрджрд╛рд╣рд░рдг рдореЗрдВ, рдЗрд╕реЗ рдареАрдХ рдХрд░рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ, рд░рд┐рдкреЛрд░реНрдЯ рдХреА рдЧрдИ рдЕрдзрд┐рдХрд╛рдВрд╢ рдШрдЯрдирд╛рдПрдВ рдЗрд╕ рд╡рд┐рд╢реЗрд╖ рдореБрджреНрджреЗ рдХреЗ рд▓рд┐рдП рдереАрдВ рдФрд░ рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдПрдХ рд▓рдВрдмреЗ рд╕рдордп рд╕реЗ рдЪрд▓рдиреЗ рд╡рд╛рд▓рд╛ рдиреЛрдб рд╡реЗрдм рд╕рд░реНрд╡рд░ рд╣реИ, рдмреНрд░реЗрдбрдХреНрд░рдВрдм рдмреЗрдХрд╛рд░ рд╣реИрдВ - рдФрд░ рд╕рднреА рддреНрд░реБрдЯрд┐рдпрд╛рдВ рдЗрд╕ рдПрдХ рдИрд╡реЗрдВрдЯ рдкреНрд░рдХрд╛рд░ рдореЗрдВ рд╢рд╛рдорд┐рд▓ рд╣реЛ рдЬрд╛рддреА рд╣реИрдВред

рдХреЙрдиреНрд╕ рдЕрдкрд╡рд╛рдж = рддреНрд░реБрдЯрд┐ред рддреНрд░реБрдЯрд┐ || error.message || рддреНрд░реБрдЯрд┐ред рдореВрд▓ рддреНрд░реБрдЯрд┐ || рддреНрд░реБрдЯрд┐;

рдпрд╣ рд╕рд╣реА/рдЧрд▓рдд рдХрд╛ рдореВрд▓реНрдпрд╛рдВрдХрди рдХрд░реЗрдЧрд╛

рдХреЙрдиреНрд╕ рдЕрдкрд╡рд╛рдж = рддреНрд░реБрдЯрд┐ред рддреНрд░реБрдЯрд┐ || error.message || рддреНрд░реБрдЯрд┐ред рдореВрд▓ рддреНрд░реБрдЯрд┐ || рддреНрд░реБрдЯрд┐;

рдпрд╣ рд╕рд╣реА/рдЧрд▓рдд рдХрд╛ рдореВрд▓реНрдпрд╛рдВрдХрди рдХрд░реЗрдЧрд╛

рд╕рдВрднрд╛рд╡рдирд╛ рдирд╣реАрдВ рд╣реИ, рдЬрдмрдХрд┐ рд╕рдВрднрд╡ рд╣реИред рдпрд╣ рд╕рдм рдиреАрдЪреЗ рдЖрддрд╛ рд╣реИ рдХрд┐ рдЖрдк рдХрд┐рд╕ "рддреНрд░реБрдЯрд┐" рдХреЛ рд╕реНрдЯреНрд░реАрдо рдореЗрдВ рднреЗрдЬрддреЗ рд╣реИрдВред рд▓реЗрдХрд┐рди рдЖрдЦрд┐рд░рдХрд╛рд░, рдЗрд╕рдореЗрдВ рдХрд┐рд╕реА рдкреНрд░рдХрд╛рд░ рдХреА рдЙрдкрдпреЛрдЧреА рдЬрд╛рдирдХрд╛рд░реА рд╢рд╛рдорд┐рд▓ рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдПред

рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
0 / 5 - 0 рд░реЗрдЯрд┐рдВрдЧреНрд╕

рд╕рдВрдмрдВрдзрд┐рдд рдореБрджреНрджреЛрдВ

ma2gedev picture ma2gedev  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

zivl picture zivl  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

simllll picture simllll  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

dwelle picture dwelle  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

mogelbrod picture mogelbrod  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ