From 84f9da4aa5c73c13bccb93f2f739bac598e91cfc Mon Sep 17 00:00:00 2001 From: coco3x Date: Tue, 3 Dec 2024 13:27:53 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20logback=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=85=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network-api/src/main/resources/logback.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 modules/network-api/src/main/resources/logback.xml diff --git a/modules/network-api/src/main/resources/logback.xml b/modules/network-api/src/main/resources/logback.xml new file mode 100644 index 00000000..7593e798 --- /dev/null +++ b/modules/network-api/src/main/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %-40.40logger{36} - %msg%n + + + + + + + + + \ No newline at end of file From 46d225930ed8fe4261561db66b006421acd72d6a Mon Sep 17 00:00:00 2001 From: coco3x Date: Tue, 3 Dec 2024 20:36:06 +0900 Subject: [PATCH 2/5] update: logback.xml -> logback-spring.xml --- .../src/main/resources/logback-spring.xml | 49 +++++++++++++++++++ .../src/main/resources/logback.xml | 16 ------ 2 files changed, 49 insertions(+), 16 deletions(-) create mode 100644 modules/network-api/src/main/resources/logback-spring.xml delete mode 100644 modules/network-api/src/main/resources/logback.xml diff --git a/modules/network-api/src/main/resources/logback-spring.xml b/modules/network-api/src/main/resources/logback-spring.xml new file mode 100644 index 00000000..2a810731 --- /dev/null +++ b/modules/network-api/src/main/resources/logback-spring.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + ${COLOR_LOG_PATTERN} + + + + + + + ${NO_COLOR_LOG_PATTERN} + + + + + + ${user.home}/whozin_log/temp.log + + + + log_%d{yyyy-MM-dd}.%i.log + + 10MB + + 30 + + 5GB + + + [%d{HH:mm:ss}] [%thread] %level %logger{0} - %msg %n + + + + \ No newline at end of file diff --git a/modules/network-api/src/main/resources/logback.xml b/modules/network-api/src/main/resources/logback.xml deleted file mode 100644 index 7593e798..00000000 --- a/modules/network-api/src/main/resources/logback.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %-40.40logger{36} - %msg%n - - - - - - - - - \ No newline at end of file From f5625a248a0d2b1c19a24f6c5b47045cf61887a2 Mon Sep 17 00:00:00 2001 From: coco3x Date: Wed, 4 Dec 2024 21:03:14 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat:=20logging=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/infrastructure/logging/build.gradle | 23 +++ .../com/whoz_in/logging/DiscordAppender.java | 134 ++++++++++++++++++ settings.gradle | 4 +- 3 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 modules/infrastructure/logging/build.gradle create mode 100644 modules/infrastructure/logging/src/main/java/com/whoz_in/logging/DiscordAppender.java diff --git a/modules/infrastructure/logging/build.gradle b/modules/infrastructure/logging/build.gradle new file mode 100644 index 00000000..127dee42 --- /dev/null +++ b/modules/infrastructure/logging/build.gradle @@ -0,0 +1,23 @@ +plugins { + id 'java' +} + +group = 'com.whoz_in' +version = '0.0.1-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.apache.httpcomponents:httpclient:4.5.14' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.2' + implementation 'ch.qos.logback:logback-classic:1.5.11' + + testImplementation platform('org.junit:junit-bom:5.9.1') + testImplementation 'org.junit.jupiter:junit-jupiter' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/modules/infrastructure/logging/src/main/java/com/whoz_in/logging/DiscordAppender.java b/modules/infrastructure/logging/src/main/java/com/whoz_in/logging/DiscordAppender.java new file mode 100644 index 00000000..0bc8272f --- /dev/null +++ b/modules/infrastructure/logging/src/main/java/com/whoz_in/logging/DiscordAppender.java @@ -0,0 +1,134 @@ +package com.whoz_in.logging; + +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Layout; +import ch.qos.logback.core.UnsynchronizedAppenderBase; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; + +public class DiscordAppender extends UnsynchronizedAppenderBase { + private final CloseableHttpClient httpClient = HttpClients.createDefault(); + //null인 필드는 body에 추가하지 않도록 함 + private final ObjectMapper objectMapper = new ObjectMapper().setSerializationInclusion( + JsonInclude.Include.NON_NULL); + + private String webhookUri; + private Layout layout; + private PatternLayoutEncoder encoder; + private String username; + private String avatarUrl; + + @Override + public void start() { + if (webhookUri == null || webhookUri.isBlank()) { + addError("Webhook URI가 없음"); + return; + } + + if (layout == null){ + if (encoder != null) { + layout = encoder.getLayout(); + } else { + addError("layout이나 encoder가 설정되지 않음"); + return; + } + } + super.start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + try { + // layout 적용 + String message = layout.doLayout(eventObject); + // 메세지 가공 + message = cleanseMessage(message); + // 디스코드 웹훅 명세에 맞게 json으로 직렬화 + String jsonPayload = makeJsonPayload(message); + // 디스코드로 전송 + send(jsonPayload); + } catch (Exception e) { + addError("디스코드 웹훅 요청 실패: ", e); + } + } + + private String makeJsonPayload(String message) throws JsonProcessingException { + // body 만들기 + Map payload = new HashMap<>(); + payload.put("content", message); + payload.put("username", username); + payload.put("avatar_url", avatarUrl); + + return objectMapper.writeValueAsString(payload); + } + + private void send(String jsonPayload) throws IOException { + HttpPost post = new HttpPost(webhookUri); + post.setHeader("Content-Type", "application/json"); + post.setEntity(new StringEntity(jsonPayload, "UTF-8")); + + try (CloseableHttpResponse response = httpClient.execute(post)) { + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode != 204 && statusCode != 200) { //성공 시 204를 보내긴 하던데 일단 200도 추가 + addError("" + statusCode); + } + } + } + + // 디스코드는 2000자 이하만 보낼 수 있음 + private String cleanseMessage(String message) { + message = message.replaceAll("```\\s```", ""); + if (message.length() > 2000) { + message = message.substring(0, 1997) + "..."; + } + return message; + } + + //Logback XML 설정을 위한 Getter, Setter + //이 Appender 재사용할 수 있으니 Lombok 안썼음 + public Layout getLayout() { + return layout; + } + + public void setLayout(Layout layout) { + this.layout = layout; + } + + public PatternLayoutEncoder getEncoder() { + return encoder; + } + + public void setEncoder(PatternLayoutEncoder encoder) { + this.encoder = encoder; + } + + public String getWebhookUri() { + return webhookUri; + } + + public void setWebhookUri(String webhookUri) { + this.webhookUri = webhookUri; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getAvatarUrl() { + return avatarUrl; + } + + public void setAvatarUrl(String avatarUrl) { + this.avatarUrl = avatarUrl; + } +} diff --git a/settings.gradle b/settings.gradle index cfdd9e18..6235b489 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,4 +12,6 @@ findProject(':modules:infrastructure:spring')?.name = 'spring' include 'modules:infrastructure:api-query-jpa' findProject(':modules:infrastructure:api-query-jpa')?.name = 'api-query-jpa' include 'modules:infrastructure:log-writer' -findProject(':modules:infrastructure:log-writer')?.name = 'log-writer' \ No newline at end of file +findProject(':modules:infrastructure:log-writer')?.name = 'log-writer' +include 'modules:infrastructure:logging' +findProject(':modules:infrastructure:logging')?.name = 'logging' \ No newline at end of file From 5d2af31c51bbea83d49ef135f06ad889686c155e Mon Sep 17 00:00:00 2001 From: coco3x Date: Wed, 4 Dec 2024 21:11:20 +0900 Subject: [PATCH 4/5] =?UTF-8?q?chore:=20=EB=AA=A8=EB=93=88=EB=A7=88?= =?UTF-8?q?=EB=8B=A4=20env=EB=A5=BC=20=EA=B4=80=EB=A6=AC=ED=95=A0=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=EC=9D=84=20=EC=97=86=EC=95=B1=EB=8B=88?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/whoz_in/log_writer/config/PropertiesConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/config/PropertiesConfig.java b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/config/PropertiesConfig.java index a33029c9..48c7c6fd 100644 --- a/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/config/PropertiesConfig.java +++ b/modules/infrastructure/log-writer/src/main/java/com/whoz_in/log_writer/config/PropertiesConfig.java @@ -3,7 +3,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; -@Configuration -@PropertySource("classpath:/env.properties") +@Configuration("logWriterPropertiesConfig") +@PropertySource("classpath:/env-log-writer.properties") public class PropertiesConfig { } From 17d87e2a9c056cc748316b28c7506b695e747133 Mon Sep 17 00:00:00 2001 From: coco3x Date: Wed, 4 Dec 2024 21:16:03 +0900 Subject: [PATCH 5/5] =?UTF-8?q?feat(net-api):=20prod=20=ED=99=98=EA=B2=BD?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=B0=9C=EC=83=9D=ED=95=98=EB=8A=94=20err?= =?UTF-8?q?or=20=EB=A1=9C=EA=B7=B8=EB=A5=BC=20=EB=94=94=EC=8A=A4=EC=BD=94?= =?UTF-8?q?=EB=93=9C=EB=A1=9C=20=EC=A0=84=EC=86=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/network-api/build.gradle | 1 + .../src/main/resources/application.yml | 7 +++++++ .../src/main/resources/logback-spring.xml | 20 +++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/modules/network-api/build.gradle b/modules/network-api/build.gradle index 1d33445e..4dd344ec 100644 --- a/modules/network-api/build.gradle +++ b/modules/network-api/build.gradle @@ -23,6 +23,7 @@ repositories { } dependencies { + implementation project(':modules:infrastructure:logging') implementation project(':modules:infrastructure:log-writer') implementation 'org.springframework.boot:spring-boot-starter' diff --git a/modules/network-api/src/main/resources/application.yml b/modules/network-api/src/main/resources/application.yml index bacc4e9b..2ed22e3d 100644 --- a/modules/network-api/src/main/resources/application.yml +++ b/modules/network-api/src/main/resources/application.yml @@ -1,6 +1,9 @@ spring: application: name: WhozIn-Network + #@PropertySource는 스프링 컨텍스트가 초기화된 이후 처리되기 때문에 여기서 env를 미리 로드 + config: + import: "classpath:env-network-api.properties" profiles: group: @@ -8,3 +11,7 @@ spring: prod: active: local include: log-writer + +logging: + discord: + webhook-url: ${DISCORD_WEBHOOK_URL} \ No newline at end of file diff --git a/modules/network-api/src/main/resources/logback-spring.xml b/modules/network-api/src/main/resources/logback-spring.xml index 2a810731..dff78bcb 100644 --- a/modules/network-api/src/main/resources/logback-spring.xml +++ b/modules/network-api/src/main/resources/logback-spring.xml @@ -1,3 +1,5 @@ + + @@ -9,6 +11,7 @@ + @@ -46,4 +49,21 @@ + + + + + + + + + ${DISCORD_WEBHOOK_URL} + + ERROR + + + ${NO_COLOR_LOG_PATTERN} + + + \ No newline at end of file