Sentry-javascript: ν‚€λ‘œ 캑처된 λΉ„ 였λ₯˜ μ˜ˆμ™Έ: 였λ₯˜, 헀더, λ©”μ‹œμ§€, 이름, 확인

에 λ§Œλ“  2019λ…„ 10μ›” 28일  Β·  48μ½”λ©˜νŠΈ  Β·  좜처: getsentry/sentry-javascript

νŒ¨ν‚€μ§€ + 버전

  • [x] @sentry/browser
  • [ ] @sentry/node
  • [ ] raven-js
  • [ ] raven-node _(λ…Έλ“œμš© 레이븐)_
  • [ ] λ‹€λ₯Έ:
  • [x] @angular/core

버전:

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

μ„€λͺ…

μ „μ—­ ErrorInterceptorκ°€ μžˆλŠ” Angular 앱에 λŒ€ν•œ 초기 섀정이 μžˆμŠ΅λ‹ˆλ‹€.
μ—¬κΈ° λ‚΄κ°€ λ³΄λ‚΄λŠ” 방법

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

그리고 이 였λ₯˜(ν‚€λ₯Ό μ‚¬μš©ν•˜μ—¬ 캑처된 λΉ„ 였λ₯˜ μ˜ˆμ™Έ: 였λ₯˜, 헀더, λ©”μ‹œμ§€, 이름, 확인) 였λ₯˜κ°€ 계속 λ°œμƒν•˜κ³  μ„€λͺ…μ—μ„œ 무엇이 잘λͺ»λ˜μ—ˆλŠ”지, μ–΄λ–»κ²Œ μž¬ν˜„ν•˜λŠ”μ§€ 이해할 수 μ—†μŠ΅λ‹ˆλ‹€.

Needs Reproduction

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

μš°λ¦¬λŠ” ν˜„μž¬ 그것을 λ¬΄μ‹œν•˜κΈ° μœ„ν•΄ λ‹€μŒμ„ μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

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

λͺ¨λ“  48 λŒ“κΈ€

이것은 μ œκ³΅ν•˜λŠ” 객체가 Angular μ•±μ—μ„œ μŠ€νƒ 좔적을 λ³΄μœ ν•˜λŠ” 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, μ–΄λ–»κ²Œ 넣어도 이 였λ₯˜κ°€ 계속 λ°œμƒν•©λ‹ˆλ‹€.

sentry.error-handler.ts

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

νŒ¨ν‚€μ§€.json

{
  ...
  "@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 ν•΄κ²° 방법에 κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€! μ‘°λ§Œκ°„ μˆ˜μ • κ°€λŠ₯성이 있기λ₯Ό λ°”λžλ‹ˆλ‹€.

λ‚˜μ—κ²Œλ„ μ΄λŸ¬ν•œ 였λ₯˜κ°€ λ°œμƒν•œλ‹€κ³  λ§ν•˜λ©΄μ„œ μ†Œλ¦¬λ₯Ό 지λ₯΄μ‹­μ‹œμ˜€. μ €λŠ” Angular 8을 μ‚¬μš©ν•˜κ³  있으며 이에 λŒ€ν•œ λͺ‡ 가지 문제λ₯Ό 읽은 ν›„ 였λ₯˜ 처리 ꡬ성 μš”μ†Œμ—μ„œ 였λ₯˜λ₯Ό μ œλŒ€λ‘œ μ²˜λ¦¬ν•˜μ§€ λͺ»ν•˜λŠ” 경우일 수 μžˆμŒμ„ κΉ¨λ‹¬μ•˜μŠ΅λ‹ˆλ‹€. captureException에 μ „λ‹¬λ˜κΈ° 전에 μ˜ˆμ™Έλ₯Ό μ •μ˜ν•˜λŠ” λ‹€λ₯Έ μ‚¬λžŒλ“€μ΄ μ œμ•ˆν•œ ν•΄κ²° 방법을 μ‹œλ„ν–ˆμ§€λ§Œ 였λ₯˜κ°€ 쀄어듀지 μ•Šμ•˜μŠ΅λ‹ˆλ‹€. λˆ„κ΅°κ°€ 이에 λŒ€ν•΄ μΆ”κ°€ 정보λ₯Ό μ œκ³΅ν•˜κ±°λ‚˜ GChronos의 (κ°μ‚¬ν•©λ‹ˆλ‹€!) μ†”λ£¨μ…˜μ„ μ‚¬μš©ν•΄μ•Ό ν•˜λŠ” κ²½μš°μ— 쒋을 κ²ƒμž…λ‹ˆλ‹€.

+1

+1

λˆ„κ΅°κ°€κ°€ 이것을 λ””λ²„κΉ…ν•˜λŠ” 데 μ‚¬μš©ν•  μˆ˜μžˆλŠ” μž¬ν˜„μ„ 제곡 ν•  수 μžˆμŠ΅λ‹ˆκΉŒ? κΈ°λ³Έ angular-cli 앱을 μ‚¬μš©ν•˜λ €κ³  ν–ˆμ§€λ§Œ 이 λ™μž‘μ„ μž¬ν˜„ν•  수 μ—†μŠ΅λ‹ˆλ‹€. 감사 ν•΄μš”!

@kamilogorek λ‚˜λŠ” 당신이 μƒˆλ‘œμš΄ Angular 8 ν”„λ‘œμ νŠΈλ₯Ό μ‹œμž‘ν•˜κ³  500을 λ°˜ν™˜ν•˜λŠ” 끝점을 ν–₯ν•΄ http μš”μ²­μ„ ν•  수 μžˆλ‹€κ³  λ―ΏμŠ΅λ‹ˆλ‹€. 그리고 Sentry둜 μ „νŒŒλ˜λ„λ‘ http μ„œλΉ„μŠ€μ—μ„œ 였λ₯˜λ₯Ό ν¬μ°©ν•˜μ§€ λͺ»ν•©λ‹ˆλ‹€.

μ’€ 더 λ§Žμ€ 정보λ₯Ό μ•Œλ €λ“œλ¦΄ 수 μžˆμŠ΅λ‹ˆλ‹€. 이것은 우리의 ErrorHandlerμž…λ‹ˆλ‹€.

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

Sentry μΆ”μ μ—μ„œλŠ” 항상 λ‹€μŒ μ΄λ²€νŠΈμ— λŒ€ν•΄ 쀑볡 이벀트λ₯Ό λ°›μŠ΅λ‹ˆλ‹€.

  • Object.a ν‚€λ‘œ 캑처된 였λ₯˜κ°€ μ•„λ‹Œ μ˜ˆμ™Έ: ...
  • captureException ν‚€λ‘œ 캑처된 였λ₯˜κ°€ μ•„λ‹Œ μ˜ˆμ™Έ: ...

(λ‹€λ₯Έ μ΄λ²€νŠΈλŠ” λͺ©λ‘μ˜ ν•œ ν•­λͺ©μœΌλ‘œ μ •μƒμ μœΌλ‘œ 보고됨)

μš°λ¦¬λŠ” 수백 개의 "Non.Error" 이벀트λ₯Ό μˆ˜μ§‘ν–ˆμœΌλ©° ν₯λ―Έλ‘­κ²Œλ„ 이듀 λͺ¨λ‘μ—λŠ” λ‹€μŒκ³Ό 같은 곡톡점이 μžˆμŠ΅λ‹ˆλ‹€.

  • λΈŒλΌμš°μ € 이름: μ‚Όμ„± 인터넷 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λ…„ λ™μ•ˆ 첫 번째 ꡬ성을 가지고 μžˆμ—ˆμ§€λ§Œ Angular 8둜 μ—…κ·Έλ ˆμ΄λ“œν•œ μ΄ν›„λ‘œ λͺ‡ 가지 μ„ΌνŠΈλ¦¬ μ˜ˆμ™Έκ°€ μžˆμŠ΅λ‹ˆλ‹€.

@jonathan-payiq 이 문제의 원인을 찾을 수 μžˆμŠ΅λ‹ˆκΉŒ? μ•„λ‹ˆλ©΄ μ§€κΈˆ 였λ₯˜κ°€ μ•„λ‹Œ μ˜ˆμ™Έλ₯Ό λͺ¨λ‘ λ¬΄μ‹œν•©λ‹ˆκΉŒ?

μš°λ¦¬λŠ” ν˜„μž¬ 그것듀을 λ¬΄μ‹œν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.
μš°λ¦¬λŠ” μ›Ήλ·°/λΈŒλΌμš°μ €κ°€ λ°±κ·ΈλΌμš΄λ“œ(보이지 μ•ŠμŒ)에 μžˆμ„ λ•Œ μ΄λŸ¬ν•œ 일이 λ°œμƒν•œλ‹€λŠ” 것을 λΈŒλ ˆλ“œν¬λŸΌμ—μ„œ μ•Œμ•„λƒˆμœΌλ―€λ‘œ νŽ˜μ΄μ§€κ°€ ν‘œμ‹œλ˜μ§€ μ•ŠλŠ” λ™μ•ˆ κ°€μ Έμ˜€κΈ°κ°€ λ°œμƒν•˜μ§€ μ•Šλ„λ‘ μˆ˜μ •ν–ˆμŠ΅λ‹ˆλ‹€. λΆ„λͺ…νžˆ μ‚Όμ„± λΈŒλΌμš°μ €λŠ” λ°±κ·ΈλΌμš΄λ“œμ—μ„œ λ„€νŠΈμ›Œν¬ μƒˆλ‘œ 고침을 잘λͺ» μ²˜λ¦¬ν•©λ‹ˆλ‹€.

@jonathan-payiq μ •ν™•νžˆ μ–΄λ–»κ²Œ λ¬΄μ‹œν•©λ‹ˆκΉŒ? 미리 κ°μ‚¬λ“œλ¦½λ‹ˆλ‹€.

μš°λ¦¬λŠ” ν˜„μž¬ 그것을 λ¬΄μ‹œν•˜κΈ° μœ„ν•΄ λ‹€μŒμ„ μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

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

@kamilogorek 문제λ₯Ό μž¬ν˜„ν•  수 μžˆμ—ˆμŠ΅λ‹ˆκΉŒ?

μ›λž˜ λ¬Έμ œκ°€ λΆ€λΆ„μ μœΌλ‘œ ν•΄κ²°λ˜μ—ˆκ±°λ‚˜ μž‘λ™ν•˜λŠ” μ†”λ£¨μ…˜μ΄ μžˆλŠ” κ²ƒμ²˜λŸΌ λ³΄μ΄λ―€λ‘œ 문제λ₯Ό μ’…λ£Œν•©λ‹ˆλ‹€. μ—¬μ „νžˆ 문제인 경우 λˆ„κ΅°κ°€κ°€ μƒˆλ‘œμš΄ μ„€λͺ…μœΌλ‘œ μƒˆ 문제λ₯Ό λ§Œλ“œλŠ” 것을 μ„ ν˜Έν•©λ‹ˆλ‹€.
μ—¬μ „νžˆ 관련성이 μžˆλŠ” 경우 μ£Όμ €ν•˜μ§€ 말고 μ €μ—κ²Œ 핑을 λ³΄λ‚΄μ£Όμ‹­μ‹œμ˜€. 그러면 기꺼이 λ‹€μ‹œ μ—΄μ–΄ μž‘μ—…ν•˜κ² μŠ΅λ‹ˆλ‹€.
건배!

ν•΄κ²°λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. 방금 Sentryλ₯Ό Angular 8κ³Ό ν†΅ν•©ν•˜λ €κ³  μ‹œλ„ν–ˆλŠ”λ° 이 문제λ₯Ό λ°œκ²¬ν–ˆμŠ΅λ‹ˆλ‹€.

λ‚˜λŠ” Sentryκ°€ κΈ°λ³Έ 섀정을 μ–΄λ–»κ²Œ μ£Όμž₯ν•  수 μžˆλŠ”μ§€ 이해가 λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 그것은 μ™„μ „νžˆ κ±°μ§“μž…λ‹ˆλ‹€.

λ‚˜λŠ” Sentryκ°€ κΈ°λ³Έ 섀정을 μ–΄λ–»κ²Œ μ£Όμž₯ν•  수 μžˆλŠ”μ§€ 이해가 λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 그것은 μ™„μ „νžˆ κ±°μ§“μž…λ‹ˆλ‹€.

@Rush λŠ” 무엇이 잘λͺ»λ˜μ—ˆλŠ”지 μ„€λͺ…ν•˜κ³  λ³΄μ—¬μ£ΌλŠ” μž¬ν˜„ κ°€λŠ₯ν•œ 사둀λ₯Ό μ œκ³΅ν•˜λ©΄ κ·€ν•˜μ˜ μ£Όμž₯을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

@kamilogorek 사싀은 6κ°œμ›” 이상 Angular와 ν•¨κ»˜ μž‘λ™ν•˜μ§€ μ•Šκ³  있으며 이λ₯Ό 확인할 수 μžˆλŠ” λ³΄κ³ μ„œκ°€ μΆ©λΆ„ν•˜λ―€λ‘œ IMOμ—μ„œ 더 μ΄μƒμ˜ μž¬ν˜„ 사둀가 ν•„μš”ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 슀슀둜 κ½€ μ‰½κ²Œ 찾을 수 μžˆμŠ΅λ‹ˆλ‹€. Angular v8을 μ‚¬μš©ν•˜λŠ” ν”„λ‘œμ νŠΈμ—μ„œ λ™μΌν•œ λ¬Έμ œκ°€ 있고 이제 v9(μ†ŒμŠ€λ§΅ μ—†μŒ)κ°€ 있기 λ•Œλ¬Έμ— 그렇지 μ•Šλ‹€λŠ” 것을 슀슀둜 μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€.

μ •ν™•νžˆ, 그리고 Sentryκ°€ μ €λ₯Ό μœ„ν•΄ 일할 수 μžˆλ„λ‘ λΉ„μš©μ„ μ§€λΆˆν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. λ‚΄ 손이 λ”λŸ¬μ›Œμ§ˆ κ²ƒμœΌλ‘œ μ˜ˆμƒλ˜λŠ” μ˜€ν”ˆ μ†ŒμŠ€ ν”„λ‘œμ νŠΈκ°€ μ•„λ‹™λ‹ˆλ‹€.

λ‚˜λŠ” Sentryλ₯Ό μ’‹μ•„ν•˜μ§€λ§Œ λŒ€μ•ˆμ„ 찾을 수 μžˆμŠ΅λ‹ˆλ‹€ ...

https://github.com/gothinkster/angular-realworld-example-app 앱을 예둜 λ“€μ–΄ λͺ¨λ‘κ°€ λ™μΌν•œ μž¬μƒμ‚°μ„ ν•  수 μžˆλ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€.

Barebone은 앱을 λ³΅μ œν•˜κ³  Sentry의 Angular λ¬Έμ„œλ₯Ό λ”°λ₯΄κ³  μ†ŒμŠ€ 맡을 μ—…λ‘œλ“œν–ˆμŠ΅λ‹ˆλ‹€. κ²°κ³ΌλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.


예 1: 깨진 논리

Angularκ°€ 였λ₯˜ μ²˜λ¦¬κΈ°μ—μ„œ μ œκ³΅ν•˜λŠ” 것 + μΊ‘μ²˜ν•œ 이벀트:

Screenshot 2020-04-16 at 12 01 13

UIμ—μ„œ μ–΄λ–»κ²Œ λ³΄μ΄λŠ”μ§€:

Screenshot 2020-04-16 at 12 07 02

μ°Έκ³ : λͺ¨λ“  것이 μ œμžλ¦¬μ— μžˆμŠ΅λ‹ˆλ‹€. Angularκ°€ μž‘μ—…ν•  전체 Error 개체λ₯Ό μ œκ³΅ν•˜κΈ° λ•Œλ¬Έμ— 였λ₯˜ λ©”μ‹œμ§€ 및 였λ₯˜ μœ ν˜•μ΄ μ˜¬λ°”λ₯Έ μ˜¬λ°”λ₯Έ μ½”λ“œ 쀄을 μ‰½κ²Œ 가리킬 수 μžˆμŠ΅λ‹ˆλ‹€.


예 2: 깨진 μ„œλΉ„μŠ€ 호좜

Angularκ°€ 였λ₯˜ μ²˜λ¦¬κΈ°μ—μ„œ μ œκ³΅ν•˜λŠ” 것 + μΊ‘μ²˜ν•œ 이벀트:

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

μ°Έκ³ : Angularκ°€ 이 정보λ₯Ό μ œκ³΅ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— μ–΄λ–€ μœ ν˜•μ˜ 였λ₯˜κ°€ λ°œμƒν–ˆλŠ”μ§€ μ•Œ 수 μžˆλŠ” 방법이 μ—†μŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μ§λ ¬ν™”λœ 데이터λ₯Ό μ‚΄νŽ΄λ³΄λ©΄ "404 Not Found" 였λ₯˜κ°€ λ°œμƒν–ˆμŒμ„ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. 이 였λ₯˜λŠ” _λŒ€λΆ€λΆ„μ˜_ 잘λͺ»λœ XHR ν˜ΈμΆœμ—μ„œ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€. 그런 λ‹€μŒ νƒμƒ‰κ²½λ‘œ 흐름을 μ‘°μ‚¬ν•˜μ—¬ _사싀_ 주어진 URL(이 경우 μ˜€νƒ€κ°€ 포함됨)에 λŒ€ν•œ XHR 호좜이 μžˆμ—ˆκ³  μ •ν™•ν•œ DOM μš”μ†Œλ₯Ό ν΄λ¦­ν•œ ν›„ 이λ₯Ό νŠΈλ¦¬κ±°ν–ˆλŠ”μ§€ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. 이 정보λ₯Ό μ‚¬μš©ν•˜λ©΄ 99%의 경우 문제λ₯Ό ν•΄κ²°ν•˜κΈ°μ— μΆ©λΆ„ν•©λ‹ˆλ‹€.


μš°λ¦¬λŠ” ν”„λ ˆμž„μ›Œν¬μ—μ„œ 주어진 κ²ƒλ§ŒμœΌλ‘œ μž‘μ—…ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 그렇지 μ•ŠμœΌλ©΄ ν”„λ ˆμž„μ›Œν¬ 자체λ₯Ό μ›μˆ­μ΄ νŒ¨μΉ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.

μš°λ¦¬λŠ” ν”„λ ˆμž„μ›Œν¬μ—μ„œ 주어진 κ²ƒλ§ŒμœΌλ‘œ μž‘μ—…ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 그렇지 μ•ŠμœΌλ©΄ ν”„λ ˆμž„μ›Œν¬ 자체λ₯Ό μ›μˆ­μ΄ νŒ¨μΉ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.

이것이 "우리의 λ¬Έμ œκ°€ μ•„λ‹ˆλ‹€"라고 λ§ν•˜λŠ” λ‹Ήμ‹ μ˜ λ°©μ‹μž…λ‹ˆκΉŒ? (일뢀 κ²½μŸμ—…μ²΄μ˜) μ„ΌνŠΈλ¦¬λ‘œ μ „ν™˜ν•˜κΈ° 전에 μ΄λŸ¬ν•œ XHR 였λ₯˜λŠ” 잘 μΊ‘μ²˜λ˜μ—ˆμœΌλ©° 각도 였λ₯˜ μ²˜λ¦¬κΈ°μ™€ λ™μΌν•œ μ„€μΉ˜ 방법을 μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€.

@kamilogorek λ²€λ”λ‘œμ„œ ν”„λ ˆμž„μ›Œν¬λ‘œ 티켓을 μ—΄ μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. λ‚˜λŠ” 그것이 μ•½κ°„μ˜ 관심을 끌 것이라고 ν™•μ‹ ν•©λ‹ˆλ‹€.

(일뢀 κ²½μŸμ—…μ²΄μ˜) μ„ΌνŠΈλ¦¬λ‘œ μ „ν™˜ν•˜κΈ° 전에 μ΄λŸ¬ν•œ XHR 였λ₯˜λŠ” 잘 μΊ‘μ²˜λ˜μ—ˆμœΌλ©° 각도 였λ₯˜ μ²˜λ¦¬κΈ°μ™€ λ™μΌν•œ μ„€μΉ˜ 방법을 μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€.

@szechyjs κ·Έλ ‡λ‹€λ©΄ μ˜€ν”ˆ μ†ŒμŠ€ SDK이고 μš°λ¦¬κ°€ λͺ©ν‘œλ‘œ ν•˜λŠ” 것을 μ•ˆλ‹€λ©΄ κ°œμ„  사항을 λ°˜λ³΅ν•  수 있기 λ•Œλ¬Έμ— λŒ€μ‹  μ†”λ£¨μ…˜μ„ κ³΅μœ ν•  수 μžˆλ‹€λ©΄ 쒋을 κ²ƒμž…λ‹ˆλ‹€.

... ν”„λ ˆμž„μ›Œν¬λ‘œ 티켓을 μ—΄ μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. λ‚˜λŠ” 그것이 μ•½κ°„μ˜ 관심을 끌 것이라고 ν™•μ‹ ν•©λ‹ˆλ‹€.

방금 μ„ΌνŠΈλ¦¬ 지원을 톡해 티켓을 μ—΄κ³  이 문제λ₯Ό μ–ΈκΈ‰ν–ˆμŠ΅λ‹ˆλ‹€.

μ—¬κΈ° μ§€λ‚˜κ°€λŠ” μ‚¬λžŒ. 제 κ²½μš°μ—λŠ” λ°˜μ‘ λ„€μ΄ν‹°λΈŒμ—μ„œ 처리된 κ±°λΆ€λ₯Ό λ³΄κ³ ν•˜κ³  OP와 λ™μΌν•œ 였λ₯˜κ°€ ν‘œμ‹œλ˜μ—ˆμŠ΅λ‹ˆλ‹€. 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

도움이 λ˜μ—ˆκΈ°λ₯Ό λ°”λžλ‹ˆλ‹€!

제 κ²½μš°μ—λŠ” Sentry의 Breadcrumbs μ„Ήμ…˜μ΄ 원인을 μ°ΎλŠ” 데 λ§Žμ€ 도움이 λ˜μ—ˆμŠ΅λ‹ˆλ‹€. 일뢀 μ™ΈλΆ€ API에 404κ°œκ°€ μžˆμ„ λ•Œ 주둜 λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.

λ¬Έμ„œ μ—…λ°μ΄νŠΈ 및 ν•„μš”ν•œ JS SDK 변경이 μ˜ˆμ •λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

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 μ½”λ“œ μ–΄λ”˜κ°€μ— promise μΈμŠ€ν„΄μŠ€ μžμ²΄μ— 였λ₯˜κ°€ λ°œμƒν•˜λŠ” 것 κ°™μŠ΅λ‹ˆλ‹€( abort, always, catch, done ν‚€ μ°Έκ³ ).

λ‹€μŒκ³Ό 같은 것:

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

λ”°λΌμ„œ Vue의 errorHandler λŠ” Error μΈμŠ€ν„΄μŠ€κ°€ 될 κ²ƒμœΌλ‘œ μ˜ˆμƒλ˜λŠ” 전체 약속 개체λ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€.

@kamilogorek μ„€λͺ… κ°μ‚¬ν•©λ‹ˆλ‹€. λͺ‡ 가지λ₯Ό λ³€κ²½ν–ˆμœΌλ©° μ—¬μ „νžˆ λ¬Έμ œκ°€ μžˆλŠ”μ§€ λͺ¨λ‹ˆν„°λ§ν•  κ²ƒμž…λ‹ˆλ‹€.

Angular 8을 μ‚¬μš©ν•˜κ³  λ™μΌν•œ λ¬Έμ œκ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€. Non-Error μ˜ˆμ™Έ. 예제 '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;
  }

μœ„μ˜ @untilinvite 의 beforeSend 예제λ₯Ό 기반으둜 λ‚΄ Sentry.init 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 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€. @sentry/angular νŒ¨ν‚€μ§€κ°€ μ΄λŸ¬ν•œ 문제λ₯Ό ν•΄κ²°ν•˜κΈ°λ₯Ό λ°”λžλ‹ˆλ‹€.

각도: v9.1
μ„ΌνŠΈλ¦¬: v5.20

@szechyjs http 인터셉터에 λŒ€ν•œ 두 번째 뢀뢄도 μ½μ—ˆμŠ΅λ‹ˆκΉŒ? 이것은 일반적으둜 μ˜¬λ°”λ₯΄κ²Œ κ°μ§€λ˜μ§€ μ•ŠλŠ” 였λ₯˜λ₯Ό λ‚˜νƒ€λ‚΄λŠ” μ£Όμš” λ¬Έμ œμž…λ‹ˆλ‹€ - https://docs.sentry.io/platforms/javascript/angular/

@szechyjs http 인터셉터에 λŒ€ν•œ 두 번째 뢀뢄도 μ½μ—ˆμŠ΅λ‹ˆκΉŒ? 이것은 일반적으둜 μ˜¬λ°”λ₯΄κ²Œ κ°μ§€λ˜μ§€ μ•ŠλŠ” 였λ₯˜λ₯Ό λ‚˜νƒ€λ‚΄λŠ” μ£Όμš” λ¬Έμ œμž…λ‹ˆλ‹€ - https://docs.sentry.io/platforms/javascript/angular/

μš°λ¦¬λŠ” 인터셉터λ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

μ•ˆλ…•ν•˜μ„Έμš”, μ €λŠ” 이 λ¬Έμ œμ— λŒ€ν•΄ μ‹œκ°„μ„ λ³΄λƒˆκ³  HttpErrorResponse μœ ν˜•μ˜ 였λ₯˜μ— λŒ€ν•œ error.error.message 에 정보가 λ°˜λ“œμ‹œ ν¬ν•¨λ˜μ–΄ μžˆμ§€λŠ” μ•Šλ‹€λŠ” 것을 λ°œκ²¬ν–ˆμŠ΅λ‹ˆλ‹€. μƒμ„±μžλ₯Ό 보면 Angularκ°€ ErrorEvent μ•„λ‹ˆλΌ 루트 객체에 message prop을 μ„€μ •ν•œλ‹€λŠ” 것을 μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€.

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 , κ°μ‚¬ν•©λ‹ˆλ‹€! PR https://github.com/getsentry/sentry-javascript/pull/2903의 μ—…λ°μ΄νŠΈλœ 처리기

방금 μƒμ†ν•œ Express μ•±μ—μ„œ 이 λ¬Έμ œκ°€ λ°œμƒν•©λ‹ˆλ‹€. μ–΄λ–»κ²Œ 이런 일이 일어날 수 μžˆλŠ”μ§€ λͺ¨λ₯΄μ§€λ§Œ μ•±μ—μ„œ λ°œμƒν•˜λŠ” λŒ€λΆ€λΆ„μ˜ 였λ₯˜μ— λŒ€ν•΄ λ°œμƒν•˜λŠ” κ²ƒμœΌλ‘œ 보이며 λ¬Έμ„œμ—μ„œ ν‘œμ€€ Express 섀정을 μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

예: https://sentry.io/share/issue/bfd4f674c10b4b4a8b6a291dde8e2a66/

비둝 그것이 각도가 μ•„λ‹ˆλΌ μ΅μŠ€ν”„λ ˆμŠ€ μš”μ²­ ν•Έλ“€λŸ¬λ₯Ό μœ„ν•œ κ²ƒμ΄μ§€λ§Œ, λ‚˜λŠ” 이와 같은 였λ₯˜λ₯Ό μ³€λ‹€. 기본적으둜 Error 개체 λŒ€μ‹  pojo둜 next λ₯Ό ν˜ΈμΆœν–ˆμŠ΅λ‹ˆλ‹€. λ‚˜λŠ” 그것을 ν₯미둜운 κ²ƒμœΌλ‘œ λ°”κΎΈκΈ° μœ„ν•΄ λ‚΄ 고유의 μ΅μŠ€ν”„λ ˆμŠ€ 였λ₯˜ μ²˜λ¦¬κΈ°μ—μ„œ λͺ‡ 가지 μž¬λ―ΈμžˆλŠ” μž‘μ—…μ„ μˆ˜ν–‰ν•˜μ§€λ§Œ sentryio μš”μ²­ 미듀웨어가 λ¨Όμ € μ‹€ν–‰λ˜κΈ° λ•Œλ¬Έμ— 이 μ •λ ¬λœ 였λ₯˜κ°€ λ°œμƒν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

κ²°κ΅­, 제곡된 λͺ¨λ“  것을 더 합리적인 κ²ƒμœΌλ‘œ λ°”κΎΈλŠ” μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜λ₯Ό λ§Œλ“€μ—ˆμŠ΅λ‹ˆλ‹€.

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

이것은 μŠ€νƒ 좔적을 λ‹€μ€‘ν™”ν•œλ‹€κ³  μƒκ°ν•˜μ§€λ§Œ μ‹€μ œλ‘œ 이것이 전달 된 pojo라면 μ–΄μ¨Œλ“  μŠ€νƒ 좔적이 μ—†μ—ˆμŠ΅λ‹ˆλ‹€.

이 λ¬Έμ œκ°€ μˆ˜μ •λ˜κΈ° 전에 보고된 이벀트의 λŒ€λΆ€λΆ„μ€ 이 νŠΉμ • λ¬Έμ œμ— λŒ€ν•œ 것이며 μž₯κΈ° μ‹€ν–‰ λ…Έλ“œ μ›Ή μ„œλ²„μ΄κΈ° λ•Œλ¬Έμ— 이동 κ²½λ‘œλŠ” 거의 μ“Έλͺ¨κ°€ μ—†μœΌλ©° λͺ¨λ“  였λ₯˜λŠ” 이 ν•˜λ‚˜μ˜ 이벀트 μœ ν˜•μ— μ§‘μ€‘λ©λ‹ˆλ‹€.

const μ˜ˆμ™Έ = error.error || 였λ₯˜ λ©”μ‹œμ§€ || error.original였λ₯˜ || 였λ₯˜;

이것은 μ°Έ/κ±°μ§“μœΌλ‘œ 평가될 κ²ƒμž…λ‹ˆλ‹€

const μ˜ˆμ™Έ = error.error || 였λ₯˜ λ©”μ‹œμ§€ || error.original였λ₯˜ || 였λ₯˜;

이것은 μ°Έ/κ±°μ§“μœΌλ‘œ 평가될 κ²ƒμž…λ‹ˆλ‹€

κ°€λŠ₯성은 μ—†μ§€λ§Œ κ°€λŠ₯ν•©λ‹ˆλ‹€. 그것은 λͺ¨λ‘ 당신이 슀트림으둜 λ³΄λ‚΄λŠ” "였λ₯˜"에 달렀 μžˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ κ²°κ΅­ μ—¬κΈ°μ—λŠ” μΌμ’…μ˜ μœ μš©ν•œ 정보가 ν¬ν•¨λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.

이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰