NestJS sentry + slack error로그 수집

이게되네·2021년 4월 5일
3

NestJS

목록 보기
5/6
post-thumbnail
post-custom-banner

Sentry는 코드를 진단하고 고치며 최적화하여 개발자를 돕는 모니터링 플랫폼 애플리케이션(오픈소스) 입니다.

NestJS에서 에러가 발생했을때 sentry에 전달하고, sentry client인 raven을 통한 인터셉터로 slack에 알리는 애플리케이션을 구축해봅니다.

참고 🔍
(이 포스팅의 주요 핵심 개념은 NestJS의 Interceptor 기능 입니다.)
NestJS - Interceptor
Sentry.io
🙈[Spring] Interceptor (1) - 개념 및 예제🐵

NestJS 프로젝트 생성 및 페키지 관련 설치

$ nest new sentry-test
$ cd sentry-test
$ npm i @sentry/node nest-raven

Sentry 설정

Sentry App

Sentry 웹 → Project → Create a new Project → Node 선택 → 프로젝트 생성후 DSN 복사

Sentry 연결 및 초기화

main.ts에 sentry 연결

import { NestFactory } from '@nestjs/core';
import { init as SentryInit } from '@sentry/node';**
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

	// connect Sentry
  SentryInit({
    dsn:
      'https://940633ab27b94fdd8afc6ae73b986030@o561269.ingest.sentry.io/이히히',
  });

  await app.listen(3000);
}
bootstrap();

NOTION
sentry는 무료버전으로 request 수가 제한되어있습니다. 되도록 NODE_ENV == 'production' 환경에서 적용하도록 합시다.

NestJS Interceptor 설정

// app.module.ts

import { HttpException, Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { RavenInterceptor, RavenModule } from 'nest-raven';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    RavenModule,
    ConfigModule.forRoot({
      envFilePath: '.env',
    }),
  ],
  controllers: [AppController],
  providers: [
    AppService,

    {
      provide: APP_INTERCEPTOR, // 전역 인터셉터로 지정
      useValue: new RavenInterceptor({
        filters: [
          {
            type: HttpException,
            // Filter exceptions of type HttpException.
            // Ignore those that have status code of less than 500
            filter: (exception: HttpException) => {
              return 500 > exception.getStatus();
            },
          },
        ],
      }),
    },

  ],
})
export class AppModule {}

Slack 설정

Slack API Incoming Webhooks 생성

Slack API 홈페이지Create New App → 이름 및 workspace 지정 → Feature/Incoming Webhooks 메뉴 → Activate Incoming Webhooks 를 On → Webhook URL .env 파일에 저장

// .env
SLACK_WEBHOOK=https://hooks.slack.com/services/T01SZ44S95J/B01TGPASYDN/이히히

Slack 메세징 솔루션 사용

$ npm i @slack/client

NestJS Interceptor 적용

  1. webhook.interceptor.ts
import {
    CallHandler,
    ExecutionContext,
    Injectable,
    NestInterceptor,
} from '@nestjs/common';
import { captureException } from '@sentry/minimal';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { IncomingWebhook } from '@slack/client';

@Injectable()
export class WebhookInterceptor implements NestInterceptor {
    intercept(_: ExecutionContext, next: CallHandler) /** : Observable<any>*/ {
    return next.handle().pipe(
        catchError((error) => {
        const webhook = new IncomingWebhook(**process.env.SLACK_WEBHOOK**);
        webhook.send({
            attachments: [
            {
                color: 'danger',
                text: '회사 드가자~ 드가자~!',
                fields: [
                {
                    title: `Request Message: ${error.message}`,
                    value: error.stack,
                    short: false,
                },
                ],
                ts: Math.floor(new Date().getTime() / 1000).toString(), // unix form
            },
            ],
        });
        return null;
        }),
    );
    }
}
  1. app.module.ts
import { HttpException, Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { RavenInterceptor, RavenModule } from 'nest-raven';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { WebhookInterceptor } from './webhook.interceptor';

@Module({
    // ...

    providers: [
    // ...

      // Webhook Interceptor 적용
    {
        provide: APP_INTERCEPTOR,
        useClass: WebhookInterceptor,
    },

    ],
})
export class AppModule {}

Test

  1. app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }

  @Get('error')
  getError() {
    throw new Error('this is error!!');
  } 
}
  1. App 실행 후, error request
$ curl -o http://127.0.0.1:3000/error

Result

profile
BackEnd Developer
post-custom-banner

0개의 댓글