Slack 도입부터 활용

짱구·2023년 4월 19일
3

monitoring

목록 보기
2/2
post-thumbnail

Slack 워크스페이스 개설

먼저 slack에 로그인 한 뒤 워크스페이스를 만들어줍니다.

  • 만들어준 워크스페이스를 열어줍니다.

  • 채널 추가를 눌러서 채널을 만들어줍니다.

저는 #ecommerce와 #shop이라는 채널을 추가를 했습니다.

Slack Api

  • create an app을 눌러서 app을 만들어줍니다.

  • From scratch를 누르고 App name과 미리 생성해둔 워크스페이스를 입력 후 Create App을 눌러줍니다.

앱이 잘 생성이 되었다면 앱을 클릭해주고 Incoming Webhooks 메뉴에 들어갑니다.

들어간 다음 Add New Webhook to Workspace라는 버튼을 눌러 채널을 연동하고 웹훅을 만들어줍니다.

아까 만들어준 #ecommerce로 연동을 해줍니다.

  • 웹훅을 만들면 웹훅의 정보를 확인할 수 있습니다.
    • Sample curl request to post to a channel을 복사하고 터미널에서 실행하면 text 문구가 #ecommerce 채널로 전송됩니다.

Spring에서 Slack 연동

web과 slack 의존성 추가


dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'com.slack.api:slack-api-client:1.27.2'

	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'

	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

yml 설정 파일

notification:
  slack:
    webhook:
      url: https://hooks.slack.com/services/[webhook URL]

아까 위에서 만들어준 webhook의 url을 넣어주면 설정이 완료가 됩니다.

Controller

@RestController
@RequiredArgsConstructor
public class HelloSlackClientController {

    @Value("${notification.slack.webhook.url}")
    private String slackAlertWebhookUrl;

    private final ObjectMapper objectMapper;

    @GetMapping(value = "/hello-error-slack-client")
    public String helloErrorSlackClient() throws IOException {
        Slack slack = Slack.getInstance();
        String errorMessage = "주문에서 에러 메세지 발생!!!";
        SlackErrorMessage slackErrorMessage = new SlackErrorMessage(errorMessage);
        WebhookResponse response = slack.send(slackAlertWebhookUrl, objectMapper.writeValueAsString(slackErrorMessage));
        return "Hello Slack Alert Sent = " + response.getCode();
    }
}

메서드로 정의된 위와 같은 코드를 try-catch에서 catch 구문에 특정 시스템 오류가 발생 했을 때 실행 시키는 방식으로 많이 사용합니다.

  • Talend API Tester에서 api 호출을 보내보겠습니다.

응용

필자의 회사는 에러에 대한 이슈가 많고 사용자들이 주로 퇴근 시간 이후에 접속을 한다.
또한 에러 모니터링이 존재하지 않았기에 에러가 일어나거나 시스템 다운이 되면 다음날 출근할 때까지 혹은 원격으로 퇴근하고나서 접속하지 않으면 알 수 없었다.
이러한 상황에서 Slack을 도입하여 에러 메세지를 받아서 바로 에러에 대응할 수 있게 도입을 해보았다.

ControllerAdvice

@ControllerAdvice
@RequiredArgsConstructor
public class ControllerAdviser {

    private final SlackApi slackApi;

    @ExceptionHandler(Exception.class)
    public void SlackErrorMessage(Exception e){
        slackApi.sendErrorForSlack(e);
    }

    @ExceptionHandler(RequestNotFoundException.class)
    public ResponseEntity<String> RequestNotFoundException(RequestNotFoundException e) {
        int statusCode = e.getStatusCode();
        return ResponseEntity.status(statusCode).body(e.getMessage());
    }

    @ExceptionHandler(GlobalException.class)
    public ResponseEntity<String> GlobalException(GlobalException e) {
        int statusCode = e.getStatusCode();
        return ResponseEntity.status(statusCode).body(e.getMessage());
    }

}
  • 최상위 에러와 커스텀해준 에러를 준비했다.

Controller

@RestController
public class SlackController {

    @GetMapping(value = "/error/v1")
    public String ErrorSlackClient1() {
        throw new ReadOnlyBufferException();
    }
    @GetMapping(value = "/error/v2")
    public String ErrorSlackClient2() {
        throw new RequestNotFoundException("뀨잉");
    }
}
  • Handling 해준 에러와 그렇지 않은 에러 두 개의 요청을 보낼것이다.

Service

@Service
@RequiredArgsConstructor
public class SlackService implements SlackApi {
    @Value("${notification.slack.webhook.url}")
    private String slackAlertWebhookUrl;

    private final ObjectMapper objectMapper;

    @Override
    public String sendErrorForSlack(Exception exception) {
        Slack slack = Slack.getInstance();
        WebhookResponse response = null;
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            StringWriter writer = new StringWriter();
            exception.printStackTrace(new PrintWriter(writer));
            
            // TODO : 주석 처리 해둔 Map으로 아래와 같이 사용 가능~!
            // Map<String, String> slackMessage = new HashMap<>();
            // slackMessage.put("text", writer.toString());
            
            SlackErrorMessage slackErrorMessage = new SlackErrorMessage(writer.toString());
            response = slack.send(slackAlertWebhookUrl, objectMapper.writeValueAsString(slackErrorMessage));
            return "Hello Slack Alert Sent = " + response.getCode();
        } catch (IOException e) {
            // TODO : 예외 처리
        }
        return null;
    }
}
  • Handling을 해주지 않은 에러가 발생 시 에러 로그 메시지가 내 채널로 날아온다.
    • 추적도 하기 쉽게 어느 부분에서 어떠한 에러가 났는지 로그를 메시지로 보냈다.

예상치 못한 오류 발생시

채널

채널에 로그가 제대로 찍힌 것을 확인할 수 있다.

Handling한 에러 발생 시

채널

이와 같이 ControllerAdviser에서 handling을 해준 에러들은 메시지가 가지않습니다.

실무 적용

위 에러 메시지는 가독성이 좋지 않아 에러가 정확히 어떤 에러인지 또 언제 어디서 발생했는지 파악하기 매우 어렵습니다.
그래서 아래와 같이 코드를 수정했습니다.

    @Override
    public String sendErrorForSlack(Exception exception) {
        Slack slack = Slack.getInstance();
        WebhookResponse response = null;
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            Map<String, String> slackMessage = new HashMap<>();
            StringWriter writer = new StringWriter();
            exception.printStackTrace(new PrintWriter(writer));

            String emoji = "\u2620";
            String errorTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            String errorPath = request.getRequestURI().toString();
            String exceptionName = exception.getClass().toString();
            String exceptionRoot = exception.getStackTrace()[0].toString();
            String message = String.format("%s [%s] - [My-Slack-Message]- [%s] - [%s] - [%s]", emoji, errorTime, errorPath, exceptionName, exceptionRoot);

            slackMessage.put("text", message);
            response = slack.send(slackAlertWebhookUrl, objectMapper.writeValueAsString(slackMessage));
            return "Hello Slack Alert Sent = " + response.getCode();
        } catch (IOException e) {
            // TODO : 예외 처리
        }
        return null;
    }

순서대로

  1. 아이콘
  2. 에러의 발생 시간
  3. 에러가 발생한 서버
  4. EndPoint
  5. Error명
  6. Error가 발생한 클래스와 상세 위치

실행

profile
코드를 거의 아트의 경지로 끌어올려서 내가 코드고 코드가 나인 물아일체의 경지

0개의 댓글