diff --git a/infrastructure/jpa/build.gradle.kts b/infrastructure/jpa/build.gradle.kts index 06299a39..72d73e5c 100644 --- a/infrastructure/jpa/build.gradle.kts +++ b/infrastructure/jpa/build.gradle.kts @@ -5,6 +5,9 @@ dependencies { // spring implementation("org.springframework.boot:spring-boot-starter-data-jpa:_") + // p6spy + implementation("com.github.gavlyukovskiy:p6spy-spring-boot-starter:_") + // h2 - DB (또는 도커) 세팅 후 사라질 예정,, runtimeOnly("com.h2database:h2") diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/common/.gitkeep b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/common/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/common/P6spySqlFormatter.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/common/P6spySqlFormatter.java new file mode 100644 index 00000000..67f75a3b --- /dev/null +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/common/P6spySqlFormatter.java @@ -0,0 +1,70 @@ +package org.depromeet.spot.jpa.common; + +import java.util.Locale; + +import org.hibernate.engine.jdbc.internal.FormatStyle; +import org.springframework.context.annotation.Configuration; + +import com.p6spy.engine.logging.Category; +import com.p6spy.engine.spy.appender.MessageFormattingStrategy; + +@Configuration +public class P6spySqlFormatter implements MessageFormattingStrategy { + + private static String ROOT_PACKAGE = "org.depromeet.spot"; + private static String P6SPY_PACKAGE = "org.depromeet.spot.jpa.common.P6spySqlFormatter"; + + @Override + public String formatMessage( + int connectionId, + String now, + long elapsed, + String category, + String prepared, + String sql, + String url) { + sql = formatSql(category, sql); + return String.format( + "[%s] | took %d ms | connectionId %d | %s | %s", + category, elapsed, connectionId, filterStack(), formatSql(category, sql)); + } + + private String formatSql(String category, String sql) { + if (isNotEmpty(sql) && isStatement(category)) { + String trimmedSQL = sql.trim().toLowerCase(Locale.ROOT); + if (isDDL(trimmedSQL)) { + return FormatStyle.DDL.getFormatter().format(sql); + } + return FormatStyle.BASIC.getFormatter().format(sql); + } + return sql; + } + + // 일반적인 쿼리인지 판단 + // 트랜잭션 커밋, 롤백 등 쿼리가 아닌 작업은 포맷팅하지 않는다. + private boolean isStatement(String category) { + return Category.STATEMENT.getName().equals(category); + } + + private boolean isNotEmpty(String sql) { + return sql != null && !sql.trim().isEmpty(); + } + + private boolean isDDL(String sql) { + return (sql.startsWith("create")) || sql.startsWith("alter") || sql.startsWith("comment"); + } + + private String filterStack() { + StackTraceElement[] stackTraces = new Throwable().getStackTrace(); + StringBuilder sb = new StringBuilder(); + int order = 1; + + for (StackTraceElement element : stackTraces) { + String trace = element.toString(); + if (trace.startsWith(ROOT_PACKAGE) && !trace.contains(P6SPY_PACKAGE)) { + sb.append("\n\t\t").append(order++).append(".").append(trace); + } + } + return sb.toString(); + } +} diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/config/P6spyConfig.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/config/P6spyConfig.java new file mode 100644 index 00000000..3209fa9b --- /dev/null +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/config/P6spyConfig.java @@ -0,0 +1,17 @@ +package org.depromeet.spot.jpa.config; + +import jakarta.annotation.PostConstruct; + +import org.depromeet.spot.jpa.common.P6spySqlFormatter; +import org.springframework.context.annotation.Configuration; + +import com.p6spy.engine.spy.P6SpyOptions; + +@Configuration +public class P6spyConfig { + + @PostConstruct + public void setLogMessageFormat() { + P6SpyOptions.getActiveInstance().setLogMessageFormat(P6spySqlFormatter.class.getName()); + } +} diff --git a/infrastructure/jpa/src/main/resources/application-jpa.yaml b/infrastructure/jpa/src/main/resources/application-jpa.yaml index 8f7afdca..c5b523ff 100644 --- a/infrastructure/jpa/src/main/resources/application-jpa.yaml +++ b/infrastructure/jpa/src/main/resources/application-jpa.yaml @@ -17,3 +17,8 @@ spring: console: enabled: true path: /h2-console + +decorator: + datasource: + p6spy: + enable-logging: true \ No newline at end of file diff --git a/versions.properties b/versions.properties index c0dbde06..c3ce1b2e 100644 --- a/versions.properties +++ b/versions.properties @@ -23,3 +23,5 @@ version.org.springframework.boot..spring-boot-starter-data-jpa=3.0.1 version.org.springdoc..springdoc-openapi-starter-webmvc-ui=2.5.0 +version.com.github.gavlyukovskiy..p6spy-spring-boot-starter=1.9.0 +