Skip to content

Commit 071ff5c

Browse files
authored
�v2.2.2
* chore: dev/prod 별 센트리 dsn, environment, servername 설정 * chore: logback 전용 Sentry 의존성 추가 * feat: Sentry logback 기능 추가 * feat: Sentry 속성을 주입하는 SentryConfig 추가 * feat: Sentry Exception 테스트용 임시 API 추가 * chore: slack webhook 의존성 주입 * chore: slack 웹훅에 사용될 환경변수 설정 * chore: 에러감지 시 Slack으로 Sentry 주소를 보내주도록 구현 * chore: test용 yml파일에 변경사항 업데이트 * feat: IOException Handler 메서드 추가
2 parents a2abdc3 + adaa6d8 commit 071ff5c

File tree

10 files changed

+177
-29
lines changed

10 files changed

+177
-29
lines changed

main/build.gradle

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,71 +23,82 @@ repositories {
2323
}
2424

2525
dependencies {
26+
// Spring Boot Dependencies
2627
implementation 'org.springframework.boot:spring-boot-starter-web'
27-
// https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-ui
2828
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
2929
implementation 'org.springframework.boot:spring-boot-starter-webflux:3.1.5'
3030
implementation 'org.springframework.boot:spring-boot-starter-actuator'
3131
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
3232
implementation 'org.springframework.boot:spring-boot-starter-security'
3333

34-
// lombok
34+
// Lombok Dependencies (Compile-time code generation library)
3535
compileOnly 'org.projectlombok:lombok'
3636
annotationProcessor 'org.projectlombok:lombok'
3737
testCompileOnly 'org.projectlombok:lombok'
3838
testAnnotationProcessor 'org.projectlombok:lombok'
3939

40+
// Development Tools
4041
developmentOnly 'org.springframework.boot:spring-boot-devtools'
4142

43+
// Test Dependencies
4244
testImplementation 'org.springframework.boot:spring-boot-starter-test'
45+
testImplementation 'org.testcontainers:testcontainers:1.17.3'
46+
testImplementation 'org.testcontainers:junit-jupiter:1.16.2'
47+
testImplementation 'org.testcontainers:postgresql:1.17.3'
48+
testImplementation 'org.testcontainers:jdbc'
49+
50+
// Apache Commons Dependencies
51+
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.14.0'
52+
53+
// AOP (Aspect-Oriented Programming) for cross-cutting concerns
54+
implementation 'org.springframework.boot:spring-boot-starter-aop'
55+
56+
// Database and Persistence Dependencies
4357
implementation group: 'org.postgresql', name: 'postgresql', version: '42.6.0'
4458
implementation 'org.springframework.boot:spring-boot-starter-validation'
45-
implementation 'org.postgresql:postgresql:42.3.0'
4659

47-
// jsonb 타입 핸들링 위함
60+
// jsonb Type Handling
4861
implementation 'io.hypersistence:hypersistence-utils-hibernate-62:3.6.0'
4962

63+
// JWT Dependencies for Security and Authentication
5064
implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5'
5165
implementation group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5'
5266
implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5'
5367
implementation 'com.auth0:java-jwt:4.4.0'
5468

55-
// h2
69+
// H2 Database (for testing and in-memory DB)
5670
runtimeOnly 'com.h2database:h2'
5771

58-
// mac m1 setting
72+
// macOS M1 Support (Network Resolver for macOS)
5973
implementation 'io.netty:netty-resolver-dns-native-macos:4.1.68.Final:osx-aarch_64'
6074

75+
// Spring Cloud OpenFeign (Service-to-service communication)
6176
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.0.3'
6277

63-
// test container
64-
testImplementation 'org.testcontainers:testcontainers:1.17.3' // TC 의존성
65-
testImplementation 'org.testcontainers:junit-jupiter:1.16.2' // TC 의존성
66-
testImplementation 'org.testcontainers:postgresql:1.17.3' // PostgreSQL 컨테이너 사용
67-
testImplementation 'org.testcontainers:jdbc' // DB와의 JDBC connection
68-
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.14.0'
69-
70-
// aop
71-
implementation 'org.springframework.boot:spring-boot-starter-aop'
72-
73-
// MapStruct
78+
// MapStruct (DTO transformation code generation)
7479
implementation 'org.mapstruct:mapstruct:1.5.3.Final'
7580
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.3.Final'
7681

77-
// queryDsl
82+
// QueryDSL (JPA Query Generation)
7883
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
7984
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
8085
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
8186
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
8287

83-
// AWS SDK for S3
88+
// AWS SDK for S3 (for file upload and download)
8489
implementation "software.amazon.awssdk:s3:2.27.0"
8590

86-
// csv 관련
91+
// CSV Processing Library
8792
implementation 'com.opencsv:opencsv:5.5.2'
8893

89-
// prometheus
94+
// Prometheus (Monitoring and Metrics Collection)
9095
implementation 'io.micrometer:micrometer-registry-prometheus'
96+
97+
// Sentry Logback (Error Tracking)
98+
implementation 'io.sentry:sentry-logback:7.17.0'
99+
100+
// Slack Webhook
101+
implementation 'com.github.maricn:logback-slack-appender:1.4.0'
91102
}
92103

93104
tasks.named('test') {
@@ -157,4 +168,4 @@ jacocoTestCoverageVerification {
157168
]
158169
}
159170
}
160-
}
171+
}

main/src/main/java/org/sopt/makers/crew/main/global/advice/ControllerExceptionAdvice.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package org.sopt.makers.crew.main.global.advice;
22

3+
import java.io.IOException;
4+
35
import org.sopt.makers.crew.main.global.exception.BaseException;
4-
import org.sopt.makers.crew.main.global.exception.ExceptionResponse;
56
import org.sopt.makers.crew.main.global.exception.ErrorStatus;
7+
import org.sopt.makers.crew.main.global.exception.ExceptionResponse;
68
import org.springframework.dao.DataIntegrityViolationException;
79
import org.springframework.http.HttpStatus;
810
import org.springframework.http.ResponseEntity;
@@ -108,12 +110,19 @@ public ResponseEntity<ExceptionResponse> handleHttpRequestMethodNotSupportedExce
108110
ErrorStatus.INVALID_INPUT_VALUE.getErrorCode()));
109111
}
110112

113+
@ExceptionHandler(IOException.class)
114+
public ResponseEntity<ExceptionResponse> handleIOException(IOException e) {
115+
log.warn("{}", e.getMessage());
116+
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
117+
.body(ExceptionResponse.fail(
118+
ErrorStatus.IO_EXCEPTION.getErrorCode()));
119+
}
120+
111121
@ExceptionHandler(Exception.class)
112122
public ResponseEntity<ExceptionResponse> handleException(Exception e) {
113123
log.error("{}", e.getMessage());
114124
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
115125
.body(ExceptionResponse.fail(
116126
ErrorStatus.INTERNAL_SERVER_ERROR.getErrorCode()));
117127
}
118-
119128
}

main/src/main/java/org/sopt/makers/crew/main/global/config/SecurityConfig.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ private String[] getAuthWhitelist() {
5252
"/meeting/v2/org-user/**",
5353
"/auth/v2",
5454
"/auth/v2/**",
55-
actuatorEndPoint + "/health"
55+
actuatorEndPoint + "/health",
56+
"/sentry" // prod에서 테스트 후 삭제
5657
};
5758
}
5859

@@ -103,4 +104,4 @@ CorsConfigurationSource corsConfigurationSource() {
103104
return source;
104105
}
105106

106-
}
107+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.sopt.makers.crew.main.global.config;
2+
3+
import org.springframework.beans.factory.annotation.Value;
4+
import org.springframework.context.annotation.Configuration;
5+
6+
import io.sentry.Sentry;
7+
import jakarta.annotation.PostConstruct;
8+
9+
@Configuration
10+
public class SentryConfig {
11+
12+
@Value("${sentry.dsn}")
13+
private String sentryDsn;
14+
15+
@Value("${sentry.environment}")
16+
private String environment;
17+
18+
@Value("${sentry.servername}")
19+
private String serverName;
20+
21+
@PostConstruct
22+
public void initSentry() {
23+
Sentry.init(options -> {
24+
options.setDsn(sentryDsn);
25+
options.setEnvironment(environment);
26+
options.setServerName(serverName);
27+
});
28+
}
29+
}

main/src/main/java/org/sopt/makers/crew/main/global/exception/ErrorStatus.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public enum ErrorStatus {
3838
CO_LEADER_CANNOT_APPLY("공동 모임장은 신청할 수 없습니다."),
3939
LEADER_CANNOT_BE_CO_LEADER_APPLY("모임장은 공동 모임장이 될 수 없습니다."),
4040
NOT_ALLOW_MEETING_APPLY("허용되지 않는 모임 신청입니다."),
41+
IO_EXCEPTION("파일 입출력 오류가 발생했습니다."),
4142

4243
/**
4344
* 401 UNAUTHORIZED
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.sopt.makers.crew.main.global.sentry;
2+
3+
import org.springframework.http.ResponseEntity;
4+
import org.springframework.web.bind.annotation.GetMapping;
5+
import org.springframework.web.bind.annotation.RequestMapping;
6+
import org.springframework.web.bind.annotation.RestController;
7+
8+
import io.sentry.Sentry;
9+
import lombok.extern.slf4j.Slf4j;
10+
11+
@Slf4j
12+
@RestController
13+
@RequestMapping("/sentry")
14+
public class SentryController {
15+
16+
@GetMapping
17+
public ResponseEntity<String> testSentry() { // prod에서 테스트 후 삭제
18+
try {
19+
throw new Exception("This is a test exception for Sentry.");
20+
} catch (Exception e) {
21+
Sentry.captureException(e);
22+
log.error("Exception captured in Sentry", e);
23+
return ResponseEntity.status(500).body("Exception captured in Sentry");
24+
}
25+
}
26+
}

main/src/main/resources/application-dev.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,16 @@ management:
9999
custom:
100100
paths:
101101
eventApply: ${DEV_EVENT_APPLY_PATH}
102+
103+
sentry:
104+
dsn: ${SENTRY_DSN}
105+
environment: ${DEV_SENTRY_ENVIRONMENT}
106+
servername: ${DEV_SENTRY_SERVERNAME}
107+
108+
logging:
109+
slack:
110+
webhook-uri: ${SENTRY_SLACK_WEBHOOK_URI}
111+
config: classpath:logback-spring.xml
112+
sentry:
113+
repository-uri: ${SENTRY_REPOSITORY_URI}
114+
environment: dev

main/src/main/resources/application-prod.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,16 @@ management:
9999
custom:
100100
paths:
101101
eventApply: ${PROD_EVENT_APPLY_PATH}
102+
103+
sentry:
104+
dsn: ${SENTRY_DSN}
105+
environment: ${PROD_SENTRY_ENVIRONMENT}
106+
servername: ${PROD_SENTRY_SERVERNAME}
107+
108+
logging:
109+
slack:
110+
webhook-uri: ${SENTRY_SLACK_WEBHOOK_URI}
111+
config: classpath:logback-spring.xml
112+
sentry:
113+
repository-uri: ${SENTRY_REPOSITORY_URI}
114+
environment: prod

main/src/main/resources/application-test.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,16 @@ management:
9898
custom:
9999
paths:
100100
eventApply: ${DEV_EVENT_APPLY_PATH}
101+
102+
sentry:
103+
dsn: ${SENTRY_DSN}
104+
environment: ${DEV_SENTRY_ENVIRONMENT}
105+
servername: ${DEV_SENTRY_SERVERNAME}
106+
107+
logging:
108+
slack:
109+
webhook-uri: ${SENTRY_SLACK_WEBHOOK_URI}
110+
config: classpath:logback-spring.xml
111+
sentry:
112+
repository-uri: ${SENTRY_REPOSITORY_URI}
113+
environment: test

main/src/main/resources/logback-spring.xml

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,58 @@
33
<property name="LOG_PATTERN"
44
value="[%d{yyyy-MM-dd HH:mm:ss}:%-4relative] %green([%thread]) %highlight(%-5level) %boldWhite([%C.%M:%yellow(%L)]) - %msg%n"/>
55

6+
<appender name="Sentry" class="io.sentry.logback.SentryAppender">
7+
<maxRequestBodySize>always</maxRequestBodySize>
8+
<sendDefaultPii>true</sendDefaultPii>
9+
<tracesSampleRate>1.0</tracesSampleRate>
10+
<minimumEventLevel>ERROR</minimumEventLevel>
11+
<minimumBreadcrumbLevel>DEBUG</minimumBreadcrumbLevel>
12+
</appender>
13+
14+
<springProperty name="SLACK_WEBHOOK_URI" source="logging.slack.webhook-uri"/>
15+
<springProperty name="SENTRY_REPOSITORY_URI" source="logging.sentry.repository-uri"/>
16+
<springProperty name="ENVIRONMENT" source="logging.sentry.environment"/>
17+
<appender name="SLACK" class="com.github.maricn.logback.SlackAppender">
18+
<webhookUri>${SLACK_WEBHOOK_URI}</webhookUri>
19+
<layout class="ch.qos.logback.classic.PatternLayout">
20+
<pattern>*🚨[${ENVIRONMENT}] %d{yyyy-MM-dd HH:mm:ss.SSS} %-4relative [%thread] %-5level %class - %msg &lt;${SENTRY_REPOSITORY_URI}|Go-To-Sentry&gt;*
21+
%n
22+
</pattern>
23+
</layout>
24+
<username>posting bot</username>
25+
<iconEmoji>:robot_face:</iconEmoji>
26+
<colorCoding>true</colorCoding>
27+
</appender>
28+
29+
<appender name="ASYNC_SLACK" class="ch.qos.logback.classic.AsyncAppender">
30+
<appender-ref ref="SLACK"/>
31+
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
32+
<level>ERROR</level>
33+
</filter>
34+
</appender>
35+
636
<springProfile name="local">
737
<include resource="console-appender.xml"/>
8-
938
<root level="INFO">
1039
<appender-ref ref="CONSOLE"/>
1140
</root>
1241
</springProfile>
1342

1443
<springProfile name="dev">
1544
<include resource="console-appender.xml"/>
16-
1745
<root level="INFO">
1846
<appender-ref ref="CONSOLE"/>
47+
<appender-ref ref="Sentry"/>
48+
<appender-ref ref="ASYNC_SLACK"/>
1949
</root>
2050
</springProfile>
2151

2252
<springProfile name="prod">
2353
<include resource="console-appender.xml"/>
2454
<root level="INFO">
2555
<appender-ref ref="CONSOLE"/>
56+
<appender-ref ref="Sentry"/>
57+
<appender-ref ref="ASYNC_SLACK"/>
2658
</root>
2759
</springProfile>
2860

@@ -32,4 +64,4 @@
3264
<appender-ref ref="CONSOLE"/>
3365
</root>
3466
</springProfile>
35-
</configuration>
67+
</configuration>

0 commit comments

Comments
 (0)