JDBC 프록시는 애플리케이션과 실제 드라이버 사이에 얇은 층을 두어 호출을 가로채는 방식이다. 실행된 SQL, 바인딩된 파라미터 값, 걸린 시간을 남기거나 SQL 을 바꿔치기하는 데 쓴다.
애플리케이션 → JDBC 프록시 → JDBC 드라이버 → DBMS
여기서 중요한 전제가 하나 있다. 프록시는 별도 서버가 아니라 클라이언트 프로세스 안에서 도는 라이브러리다. 네트워크 중간에 서서 여러 클라이언트의 SQL 을 모으는 게이트웨이가 아니다. 그런 구조가 필요하면 DBMS 별 프록시 제품이나 DBMS 자체의 감사 로그를 봐야 한다.
| 도구 | 성격 |
|---|---|
| P6Spy | SQL 로깅에 특화. 드라이버를 감싸는 방식이라 URL 과 드라이버 클래스만 바꾸면 붙는다 |
| datasource-proxy | DataSource 를 감싸고 리스너를 등록해 직접 로직을 넣는다. 마스킹처럼 커스텀 처리가 필요할 때 |
| log4jdbc | SLF4J 로 출력. 오래된 프로젝트에 남아 있는 경우가 많다 |
| OpenTelemetry Java Instrumentation | 자동 계측 에이전트. JDBC 호출을 트레이스 span 으로 만든다. Apache 2.0 |
단순히 어떤 SQL 이 나가는지 보고 싶은 것이라면 P6Spy, 마이크로서비스 전반의 호출을 추적하는 것이 목적이라면 OpenTelemetry 쪽이다. 둘은 목적이 달라 대체 관계가 아니다.
P6Spy 는 실제 드라이버를 감싸므로 애플리케이션 코드를 고치지 않고 연결 설정만 바꾼다.
# 바꾸기 전
driver: oracle.jdbc.OracleDriver
url: jdbc:oracle:thin:@db.example.com:1521/ORCL
# 바꾼 뒤
driver: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:oracle:thin:@db.example.com:1521/ORCL
동작은 클래스패스의 spy.properties 로 정한다.
modulelist=com.p6spy.engine.spy.P6SpyFactory,com.p6spy.engine.logging.P6LogFactory
appender=com.p6spy.engine.spy.appender.Slf4JLogger
logMessageFormat=com.p6spy.engine.spy.appender.MultiLineFormat
excludecategories=info,debug,result,resultset
executionThreshold=0
executionThreshold 를 밀리초로 주면 그보다 오래 걸린 질의만 남긴다. 운영 환경에서 전체 로깅을 켜 두면 로그 양이 감당되지 않으므로 이 값을 쓴다.
SQL 전체 로깅을 켜면 바인딩 파라미터가 그대로 찍힌다. 주민등록번호나 계좌번호가 조건절에 들어가는 질의라면 로그 파일이 그 자체로 개인정보가 된다. 다음 중 하나를 미리 정해 둔다.
excludecategories 로 결과셋과 파라미터를 빼고 SQL 문장만 남긴다.DBeaver 같은 클라이언트에서 나가는 SQL 을 남기려면, 클라이언트 쪽 드라이버 설정에 P6Spy jar 를 추가하고 드라이버 클래스와 URL 접두사를 바꾼다. 클라이언트마다 드라이버 등록 화면이 다르고, 여러 데이터소스를 쓰면 데이터소스마다 같은 작업을 해야 한다.
이 방식으로는 접근 제어를 할 수 없다. 프록시가 클라이언트 프로세스 안에 있으므로 사용자가 설정을 되돌리면 그만이다. 접근 제어가 목적이라면 DBMS 계정 권한과 감사 로그로 풀어야 한다. 중앙에서 SQL 을 통제하려면 Trino 같은 쿼리 게이트웨이나 상용 DB 접근 제어 제품이 필요하다.
P6Spy 는 Maven 프로젝트이므로 JDK 와 Maven 만 있으면 빌드된다. 만들어진 jar 를 애플리케이션 클래스패스에 넣으면 된다. 특정 리눅스 배포판에 맞춘 패키지가 필요한 것은 아니다.
git clone https://github.com/p6spy/p6spy.git
cd p6spy
mvn -DskipTests package
ls target/*.jar