Skip to content

Commit

Permalink
Merge pull request #54 from YAPP-Github/MAFOO-38
Browse files Browse the repository at this point in the history
[MAFOO-37] feat: ์‚ฌ์ง„ ์•จ๋ฒ” ๋„๋ฉ”์ธ(photo-service) ์Šฌ๋ž™ ์•Œ๋ฆผ, ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๊ธฐ
  • Loading branch information
gmkim20713 authored Aug 19, 2024
2 parents af6e965 + a1e72d6 commit b9f96ae
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 1 deletion.
1 change: 1 addition & 0 deletions photo-service/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dependencies {
implementation("io.micrometer:micrometer-tracing-bridge-otel:1.3.2")
implementation("io.opentelemetry:opentelemetry-exporter-zipkin:1.40.0")
implementation("io.micrometer:micrometer-registry-prometheus:1.13.2")
implementation("com.slack.api:slack-api-client:1.40.3")
}

tasks.withType<Test> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package kr.mafoo.photo.config;

import com.slack.api.Slack;
import com.slack.api.methods.MethodsClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SlackApiConfig {

@Value("${slack.webhook.token}")
private String token;

@Bean
public MethodsClient getClient() {
Slack slackClient = Slack.getInstance();
return slackClient.methods(token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package kr.mafoo.photo.handler;

import kr.mafoo.photo.service.SlackService;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.InetSocketAddress;

@ControllerAdvice
@RequiredArgsConstructor
public class GlobalExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
private final SlackService slackService;

@ExceptionHandler(ResponseStatusException.class)
public Mono<ResponseEntity<String>> handleResponseStatusException(ServerWebExchange exchange, ResponseStatusException ex) {
return handleExceptionInternal(exchange, ex, (HttpStatus) ex.getStatusCode());
}

@ExceptionHandler(Exception.class)
public Mono<ResponseEntity<String>> handleGenericException(ServerWebExchange exchange, Exception ex) {
return handleExceptionInternal(exchange, ex, HttpStatus.INTERNAL_SERVER_ERROR);
}

private Mono<ResponseEntity<String>> handleExceptionInternal(ServerWebExchange exchange, Exception ex, HttpStatus status) {
String method = exchange.getRequest().getMethod().toString();
String userAgent = exchange.getRequest().getHeaders().getFirst("User-Agent");
String proxyIp = exchange.getRequest().getHeaders().getFirst("X-Forwarded-For");
InetSocketAddress address = exchange.getRequest().getRemoteAddress();
String originIp = proxyIp != null ? proxyIp : (address != null ? address.toString() : "UNKNOWN SOURCE");
String fullPath = exchange.getRequest().getURI().getPath() +
(exchange.getRequest().getURI().getQuery() != null ? "?" + exchange.getRequest().getURI().getQuery() : "");

logger.error("Exception occurred: {} {} {} ERROR {} {}", method, fullPath, originIp, ex.getMessage(), userAgent);

if (status == HttpStatus.INTERNAL_SERVER_ERROR) {
return slackService.sendErrorNotification(
method,
fullPath,
originIp,
userAgent,
ex.getMessage()
).then(Mono.just(new ResponseEntity<>("Internal Server Error", HttpStatus.INTERNAL_SERVER_ERROR)));
}

return Mono.just(new ResponseEntity<>(status.getReasonPhrase(), status));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package kr.mafoo.photo.service;

import com.slack.api.methods.MethodsClient;
import com.slack.api.methods.request.chat.ChatPostMessageRequest;
import com.slack.api.model.block.Blocks;
import com.slack.api.model.block.LayoutBlock;
import com.slack.api.model.block.composition.MarkdownTextObject;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;

import static com.slack.api.model.block.Blocks.*;
import static com.slack.api.model.block.composition.BlockCompositions.plainText;

@Service
@RequiredArgsConstructor
public class SlackService {

@Value(value = "${slack.webhook.token}")
private String token;

@Value(value = "${slack.webhook.channel.error}")
private String errorChannel;

private final MethodsClient methodsClient;

public Mono<Void> sendErrorNotification(String method, String uri, String originIp, String userAgent, String message) {
return Mono.fromCallable(() -> {
List<LayoutBlock> layoutBlocks = new ArrayList<>();

// Header ์‚ฝ์ž…
layoutBlocks.add(
Blocks.header(
headerBlockBuilder ->
headerBlockBuilder.text(plainText("๐Ÿšจ ์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ ์—๋Ÿฌ ๋ฐœ์ƒ"))
)
);

layoutBlocks.add(divider());

// Content ์‚ฝ์ž…
MarkdownTextObject errorMethodMarkdown =
MarkdownTextObject.builder().text("`METHOD`\n" + method).build();

MarkdownTextObject errorUriMarkdown =
MarkdownTextObject.builder().text("`URI`\n" + uri).build();

layoutBlocks.add(
section(
section -> section.fields(List.of(errorMethodMarkdown, errorUriMarkdown))
)
);

MarkdownTextObject errorOriginIpMarkdown =
MarkdownTextObject.builder().text("`์—๋Ÿฌ ๋ฐœ์ƒ IP`\n" + originIp).build();

MarkdownTextObject errorUserAgentMarkdown =
MarkdownTextObject.builder().text("`์—๋Ÿฌ ๋ฐœ์ƒ ํ™˜๊ฒฝ`\n" + userAgent).build();

layoutBlocks.add(
section(
section -> section.fields(List.of(errorOriginIpMarkdown, errorUserAgentMarkdown))
)
);

MarkdownTextObject errorMessageMarkdown =
MarkdownTextObject.builder().text("`๋ฉ”์„ธ์ง€`\n" + message).build();

layoutBlocks.add(
section(
section -> section.fields(List.of(errorMessageMarkdown))
)
);

ChatPostMessageRequest chatPostMessageRequest =
ChatPostMessageRequest
.builder()
.text("์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ ์—๋Ÿฌ ๋ฐœ์ƒ ์•Œ๋ฆผ")
.channel(errorChannel)
.blocks(layoutBlocks)
.build();

return methodsClient.chatPostMessage(chatPostMessageRequest);

}).then();
}

}
2 changes: 1 addition & 1 deletion photo-service/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ management:
enabled: false
zipkin:
tracing:
endpoint: http://zipkin/api/v2/spans
endpoint: http://zipkin/api/v2/spans

0 comments on commit b9f96ae

Please sign in to comment.