JPA 로그를 보다보면
select ~ from 테이블 where col1 = ? and col2 = ?
로 포맷팅 되지 않은 형태로 sql 로그가 찍히는 걸 볼 수 있다
1. 개행 없음
2. 바인딩 되는 파라미터 정보 부재
spring data jpa 기능으로만 위 1, 2 문제는 해결이 가능하다,
그러나
select ~ from 테이블 where col1 = ? and col2 = ?
~ binding parameter ['1']
~ binding parameter ['philz']
로 파라미터와 sql로그가 따로 찍혀서 불편하게 해석해야 한다
따라서 p6spy를 이용하면 저 물음표 자리에 파라미터 정보를 넣어서 편하게 볼 수 있다
select ...
from 테이블
where col1 = '1' and col2 = 'philz'
설정법
p6spy 의존성 추가
// p6spy
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.8.1'
아래 파일 2개 추가
이때 META-INF.spring 폴더에서 "."은 이름의 한 요소가 아니라, 폴더 구분자다!
즉 실제로는 아래와 같음
그리고 아래와 같은 이름의 파일들을 만들자
resources/ META-INF/spring/
spy.properties
appender=com.p6spy.engine.spy.appender.Slf4JLogger
resources/ META-INF/spring/
org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.github.gavlyukovskiy.boot.jdbc.decorator.DataSourceDecoratorAutoConfiguration
다시 정리하면 최종적으로 아래와 같이 나오면 된다
포맷팅 Bean 등록
Java ver
@Profile({"default", "local"})
@Configuration
public class P6SpySqlFormatter implements MessageFormattingStrategy {
@PostConstruct
public void setLogMessageFormat() {
P6SpyOptions.getActiveInstance().setLogMessageFormat(this.getClass().getName());
}
@Override
public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql, String url) {
String formattedSql = formatSql(category, sql);
return formatLog(elapsed, category, formattedSql);
}
private String formatSql(String category, String sql) {
if (hasText(sql) && isStatement(category)) {
String trimmedSQL = trim(sql);
if (isDdl(trimmedSQL)) {
return FormatStyle.DDL.getFormatter().format(sql);
}
return FormatStyle.BASIC.getFormatter().format(sql); // maybe DML
}
return sql;
}
private static boolean isDdl(String trimmedSQL) {
return trimmedSQL.startsWith("create") || trimmedSQL.startsWith("alter") || trimmedSQL.startsWith("comment");
}
private static String trim(String sql) {
return sql.trim().toLowerCase(Locale.ROOT);
}
private static boolean isStatement(String category) {
return Category.STATEMENT.getName().equals(category);
}
private String formatLog(long elapsed, String category, String formattedSql) {
return String.format("[%s] | %d ms | %s", category, elapsed, formatSql(category, formattedSql));
}
}
Kotlin Ver
import com.p6spy.engine.logging.Category
import com.p6spy.engine.spy.P6SpyOptions
import com.p6spy.engine.spy.appender.MessageFormattingStrategy
import jakarta.annotation.PostConstruct
import org.hibernate.engine.jdbc.internal.FormatStyle
import org.springframework.context.annotation.Configuration
import org.springframework.util.StringUtils.hasText
@Configuration
class P6SpySqlFormatter : MessageFormattingStrategy {
@PostConstruct
fun setLogMessageFormat() {
P6SpyOptions.getActiveInstance().logMessageFormat = this.javaClass.getName()
}
override fun formatMessage(
connectionId: Int,
now: String,
elapsed: Long,
category: String,
prepared: String,
sql: String,
url: String,
): String {
val formattedSql = formatSql(category, sql)
return formatLog(elapsed, category, formattedSql)
}
private fun formatSql(category: String, sql: String): String {
if (hasText(sql) && isStatement(category)) {
val trimmedSQL = trim(sql)
return if (isDdl(trimmedSQL)) {
FormatStyle.DDL.formatter.format(sql)
} else FormatStyle.BASIC.formatter.format(sql) // maybe this line is DML
}
return sql
}
private fun formatLog(elapsed: Long, category: String, formattedSql: String): String {
return String.format("[%s] | %d ms | %s", category, elapsed, formatSql(category, formattedSql))
}
companion object {
private fun isDdl(trimmedSQL: String): Boolean {
return trimmedSQL.startsWith("create") || trimmedSQL.startsWith("alter") || trimmedSQL.startsWith("comment")
}
private fun trim(sql: String): String {
return sql.trim { it <= ' ' }.lowercase()
}
private fun isStatement(category: String): Boolean {
return Category.STATEMENT.name == category
}
}
}
P6Sp6 설정을 하면 기존에 설정하던 아래의 yaml 설정 들은 필요 없어진다
spring:
jpa:
show-sql: true
properties:
format_sql: true
use_sql_comments: true
logging.level:
org.hibernate:
orm.jdbc.bind: trace
type: trace
stat: debug
참고
https://jong-bae.tistory.com/23
https://shanepark.tistory.com/415
같이 보면 좋은 자료
https://curiousjinan.tistory.com/entry/spring-boot-3-p6spy-jpa-logging
'Back-end > Spring Boot, JPA' 카테고리의 다른 글
ArgumentResolver처리 과정 + Spring Web MVC 흐름 (0) | 2023.06.20 |
---|---|
Argument Resolver 등록 관련 디버깅 (feat. Spring MVC 흐름) (0) | 2023.06.20 |
Spring Boot 3.x에서 H2 DB 설정 (0) | 2023.05.12 |
spring security (2) | 2021.05.05 |
newlecture: spring MVC 1 (0) | 2021.04.26 |
hi hello... World >< 가장 아름다운 하나의 해답이 존재한다
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!