diff --git a/build.gradle b/build.gradle index 2eb6b76..2c51afd 100644 --- a/build.gradle +++ b/build.gradle @@ -58,7 +58,7 @@ dependencies { // Monitoring implementation 'org.springframework.boot:spring-boot-starter-actuator' - implementation 'io.micrometer:micrometer-registry-cloudwatch2' + implementation 'io.micrometer:micrometer-registry-prometheus' // Tracing implementation 'io.micrometer:micrometer-tracing-bridge-otel' diff --git a/src/main/java/com/daedan/festabook/global/config/CloudWatchMetricsConfig.java b/src/main/java/com/daedan/festabook/global/config/CloudWatchMetricsConfig.java deleted file mode 100644 index 2dee54c..0000000 --- a/src/main/java/com/daedan/festabook/global/config/CloudWatchMetricsConfig.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.daedan.festabook.global.config; - -import io.micrometer.cloudwatch2.CloudWatchConfig; -import io.micrometer.cloudwatch2.CloudWatchMeterRegistry; -import io.micrometer.core.instrument.Clock; -import java.time.Duration; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import software.amazon.awssdk.services.cloudwatch.CloudWatchAsyncClient; - -@Profile({"prod", "dev"}) -@Configuration -public class CloudWatchMetricsConfig { - - @Value("${cloudwatch.namespace}") - private String cloudWatchNamespace; - - private static final Duration CLOUDWATCH_STEP = Duration.ofMinutes(1); - - @Bean - public CloudWatchConfig cloudWatchConfig() { - return new CloudWatchConfig() { - - @Override - public String get(String key) { - return null; - } - - @Override - public String namespace() { - return cloudWatchNamespace; - } - - @Override - public Duration step() { - return CLOUDWATCH_STEP; - } - }; - } - - @Bean - public CloudWatchAsyncClient cloudWatchAsyncClient() { - return CloudWatchAsyncClient.create(); - } - - @Bean - public CloudWatchMeterRegistry cloudWatchMeterRegistry(CloudWatchConfig config, - CloudWatchAsyncClient client) { - return new CloudWatchMeterRegistry(config, Clock.SYSTEM, client); - } -} diff --git a/src/main/java/com/daedan/festabook/global/logging/LocalLoggingAspect.java b/src/main/java/com/daedan/festabook/global/logging/LocalLoggingAspect.java index e51288a..ac2e623 100644 --- a/src/main/java/com/daedan/festabook/global/logging/LocalLoggingAspect.java +++ b/src/main/java/com/daedan/festabook/global/logging/LocalLoggingAspect.java @@ -17,16 +17,7 @@ @Order(Ordered.HIGHEST_PRECEDENCE) public class LocalLoggingAspect { - @Around(""" - execution(* com.daedan.festabook..*.*(..)) && - ( - within(@org.springframework.web.bind.annotation.RestController *) || - within(@org.springframework.stereotype.Service *) || - within(@com.daedan.festabook.global.logging.Loggable *) || - execution(* org.springframework.data.jpa.repository.JpaRepository+.*(..)) - ) - """ - ) + @Around("com.daedan.festabook.global.logging.LoggingPointcuts.applicationLayers()") public Object allLayersLogging(ProceedingJoinPoint joinPoint) throws Throwable { StopWatch stopWatch = new StopWatch(); String className = joinPoint.getSignature().getDeclaringType().getSimpleName(); diff --git a/src/main/java/com/daedan/festabook/global/logging/LoggingAspect.java b/src/main/java/com/daedan/festabook/global/logging/LoggingAspect.java index 0e3f9c8..d39a906 100644 --- a/src/main/java/com/daedan/festabook/global/logging/LoggingAspect.java +++ b/src/main/java/com/daedan/festabook/global/logging/LoggingAspect.java @@ -4,9 +4,6 @@ import com.daedan.festabook.global.logging.dto.MethodEventLog; import com.daedan.festabook.global.logging.dto.MethodLog; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.context.Scope; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; @@ -23,21 +20,10 @@ @Component @Profile("prod | dev") @RequiredArgsConstructor -@Order(Ordered.HIGHEST_PRECEDENCE) +@Order(Ordered.HIGHEST_PRECEDENCE + 1) public class LoggingAspect { - private final Tracer tracer; - - @Around(""" - execution(* com.daedan.festabook..*.*(..)) && - ( - within(@org.springframework.web.bind.annotation.RestController *) || - within(@org.springframework.stereotype.Service *) || - within(@com.daedan.festabook.global.logging.Loggable *) || - execution(* org.springframework.data.jpa.repository.JpaRepository+.*(..)) - ) - """ - ) + @Around("com.daedan.festabook.global.logging.LoggingPointcuts.applicationLayers()") public Object allLayersLogging(ProceedingJoinPoint joinPoint) throws Throwable { StopWatch stopWatch = new StopWatch(); String className = joinPoint.getSignature().getDeclaringType().getSimpleName(); @@ -46,23 +32,15 @@ public Object allLayersLogging(ProceedingJoinPoint joinPoint) throws Throwable { MethodEventLog methodEvent = MethodEventLog.from(className, methodName); log.info("", kv("event", methodEvent)); - String spanName = className + "::" + methodName; - Span span = tracer.spanBuilder(spanName).startSpan(); - - Object result = null; stopWatch.start(); - try (Scope scope = span.makeCurrent()) { - result = joinPoint.proceed(); + try { + return joinPoint.proceed(); } finally { stopWatch.stop(); long executionTime = stopWatch.getTotalTimeMillis(); MethodLog methodLog = MethodLog.from(className, methodName, executionTime); log.info("", kv("event", methodLog)); - - span.end(); } - - return result; } } diff --git a/src/main/java/com/daedan/festabook/global/logging/LoggingPointcuts.java b/src/main/java/com/daedan/festabook/global/logging/LoggingPointcuts.java new file mode 100644 index 0000000..1885fc7 --- /dev/null +++ b/src/main/java/com/daedan/festabook/global/logging/LoggingPointcuts.java @@ -0,0 +1,21 @@ +package com.daedan.festabook.global.logging; + +import org.aspectj.lang.annotation.Pointcut; + +public final class LoggingPointcuts { + + private LoggingPointcuts() { + } + + @Pointcut(""" + execution(* com.daedan.festabook..*.*(..)) && + ( + within(@org.springframework.web.bind.annotation.RestController *) || + within(@org.springframework.stereotype.Service *) || + within(@com.daedan.festabook.global.logging.Loggable *) || + execution(* org.springframework.data.jpa.repository.JpaRepository+.*(..)) + ) + """) + public void applicationLayers() { + } +} diff --git a/src/main/java/com/daedan/festabook/global/logging/TracingAspect.java b/src/main/java/com/daedan/festabook/global/logging/TracingAspect.java new file mode 100644 index 0000000..6e13b94 --- /dev/null +++ b/src/main/java/com/daedan/festabook/global/logging/TracingAspect.java @@ -0,0 +1,48 @@ +package com.daedan.festabook.global.logging; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Scope; +import lombok.RequiredArgsConstructor; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +@Aspect +@Component +@Profile("prod | dev") +@Order(Ordered.HIGHEST_PRECEDENCE) +public class TracingAspect { + + private final Tracer tracer; + private final String env; + + public TracingAspect( + Tracer tracer, + @Value("${env}") String env + ) { + this.tracer = tracer; + this.env = env; + } + + @Around("com.daedan.festabook.global.logging.LoggingPointcuts.applicationLayers()") + public Object trace(ProceedingJoinPoint joinPoint) throws Throwable { + String className = joinPoint.getSignature().getDeclaringType().getSimpleName(); + String methodName = joinPoint.getSignature().getName(); + + String spanName = className + "::" + methodName; + Span span = tracer.spanBuilder(spanName).startSpan(); + span.setAttribute("env", env); + + try (Scope scope = span.makeCurrent()) { + return joinPoint.proceed(); + } finally { + span.end(); + } + } +} diff --git a/src/main/java/com/daedan/festabook/global/security/config/SecurityConfig.java b/src/main/java/com/daedan/festabook/global/security/config/SecurityConfig.java index 1c8309c..58accba 100644 --- a/src/main/java/com/daedan/festabook/global/security/config/SecurityConfig.java +++ b/src/main/java/com/daedan/festabook/global/security/config/SecurityConfig.java @@ -50,6 +50,7 @@ public class SecurityConfig { "/lineups", "/time-tags", "/actuator/health", + "/actuator/prometheus", "/test/**" }; diff --git a/src/main/resources/application-monitoring.yml b/src/main/resources/application-monitoring.yml new file mode 100644 index 0000000..fed9687 --- /dev/null +++ b/src/main/resources/application-monitoring.yml @@ -0,0 +1,26 @@ +server: + tomcat: + mbeanregistry: + enabled: true + +management: + server: + port: 9000 + endpoints: + web: + exposure: + include: prometheus, health + metrics: + tags: + application: ${spring.application.name} + prometheus: + metrics: + export: + enabled: true + tracing: + sampling: + probability: 1.0 + otlp: + tracing: + endpoint: "http://localhost:4317" + transport: grpc diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 34f086c..ae88145 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -4,9 +4,6 @@ server: context-path: /api shutdown: graceful forward-headers-strategy: native - tomcat: - mbeanregistry: - enabled: true spring: datasource: @@ -21,7 +18,7 @@ spring: hibernate: ddl-auto: none profiles: - include: secret + include: secret, monitoring lifecycle: # WAS 종료 대기 시간 15초로 설정 timeout-per-shutdown-phase: 15s @@ -31,7 +28,6 @@ spring: max-file-size: 10MB max-request-size: 11MB - storage: image: max-size: 10485760 # 10 * 1024 * 1024, 10MB diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index 4031761..088b3e6 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -2,7 +2,7 @@ - + ${LOG_FILE_PATH}/application.json