Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

env : 에러 로그 웹훅 수정 #312

Merged
merged 1 commit into from
Jan 28, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.ThrowableProxyUtil;
Expand All @@ -26,30 +27,50 @@
@Component
@Setter
public class DiscordLogAppender extends AppenderBase<ILoggingEvent> {

private String discordWebhookUrl;

@Override
protected void append(ILoggingEvent event) {
Map<String, String> mdcPropertyMap = event.getMDCPropertyMap();

String levelStr = event.getLevel().levelStr;
String exceptionBrief = "";
IThrowableProxy throwable = event.getThrowableProxy();
boolean isException = (throwable != null);

if (throwable != null) {
exceptionBrief = throwable.getClassName() + ": " + throwable.getMessage();
// ERROR 레벨이지만 Throwable이 없는 경우 -> log.error
if (event.getLevel() == Level.ERROR && !isException) {
sendMinimalErrorLog(event);
return;
}

if (exceptionBrief.isEmpty()) {
exceptionBrief = "EXCEPTION 정보가 남지 않았습니다.";
}
sendDetailedLog(event);
}

private void sendMinimalErrorLog(ILoggingEvent event) {
String levelStr = event.getLevel().levelStr;

Map<String, Object> embed = new HashMap<>();
embed.put("title", "[" + levelStr + "] 에러 로그 탐지");
embed.put("color", getColorForLevel(levelStr));
embed.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));

List<Map<String, String>> fields = new ArrayList<>();
fields.add(createField("에러 메시지", event.getFormattedMessage(), false));
embed.put("fields", fields);

sendToDiscordEmbed(embed);
}

private void sendDetailedLog(ILoggingEvent event) {
Map<String, String> mdcPropertyMap = event.getMDCPropertyMap();
String levelStr = event.getLevel().levelStr;
IThrowableProxy throwable = event.getThrowableProxy();

String exceptionBrief = throwable.getClassName() + ": " + throwable.getMessage();

Map<String, Object> embed = new HashMap<>();
embed.put("title", "[" + levelStr + "] Exception 발생");
embed.put("title", "[" + levelStr + "] " + "Exception 발생");
embed.put("color", getColorForLevel(levelStr));
embed.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));

// Embed Fields
List<Map<String, String>> fields = new ArrayList<>();
fields.add(createField("문제 간략 내용", exceptionBrief, false));
fields.add(createField("요청 URI", escape(mdcPropertyMap.get(MDCUtil.MDC_REQUEST_URI)), true));
Expand All @@ -58,24 +79,15 @@ protected void append(ILoggingEvent event) {
fields.add(createField("파라미터", escape(mdcPropertyMap.get(MDCUtil.MDC_PARAMETER)), true));
fields.add(createField("요청 Body", escape(mdcPropertyMap.get(MDCUtil.MDC_REQUEST_BODY)), true));

embed.put("fields", fields);
// Stacktrace
String exceptionDetail = ThrowableProxyUtil.asString(throwable);
fields.add(createField("Exception 상세 내용", truncate(exceptionDetail), false));

if (throwable != null) {
String exceptionDetail = ThrowableProxyUtil.asString(throwable);
fields.add(createField("Exception 상세 내용", truncate(exceptionDetail), false));
}
embed.put("fields", fields);

sendToDiscordEmbed(embed);
}

private Map<String, String> createField(String name, String value, boolean inline) {
Map<String, String> field = new HashMap<>();
field.put("name", name);
field.put("value", value != null ? value : "없음");
field.put("inline", String.valueOf(inline));
return field;
}

private void sendToDiscordEmbed(Map<String, Object> embed) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
Expand All @@ -88,7 +100,6 @@ private void sendToDiscordEmbed(Map<String, Object> embed) {

try {
ResponseEntity<String> response = restTemplate.postForEntity(discordWebhookUrl, request, String.class);

if (!response.getStatusCode().is2xxSuccessful()) {
addError("Failed to send log message to Discord: " + response.getStatusCode());
}
Expand All @@ -97,13 +108,23 @@ private void sendToDiscordEmbed(Map<String, Object> embed) {
}
}

private Map<String, String> createField(String name, String value, boolean inline) {
Map<String, String> field = new HashMap<>();
field.put("name", name);
field.put("value", value != null ? value : "없음");
field.put("inline", String.valueOf(inline));
return field;
}

private String escape(String value) {
return value != null ? StringEscapeUtils.escapeJson(value) : "없음";
}

private String truncate(String value) {
final int MAX_LENGTH = 1000;
return value != null && value.length() > MAX_LENGTH ? value.substring(0, MAX_LENGTH) + "..." : value;
return value != null && value.length() > MAX_LENGTH
? value.substring(0, MAX_LENGTH) + "..."
: value;
}

private int getColorForLevel(String level) {
Expand All @@ -116,5 +137,4 @@ private int getColorForLevel(String level) {
default -> 0x000000; // Black
};
}

}
}
Loading