Приветствую, дорогие друзья! Меня зовут Алексей, и я backend java developer. На одном из моих проектов была задача - предоставлять по требованию SQL запросы уходящие в БД, для этого их решили логировать. В этой статье я постараюсь поделиться несколькими способами, которыми можно достичь данной задачи.
Проект был написан на Spring, поэтому все примеры будут на нём. Запросы были написаны с использованием JPA или сгенерированы через Spring Repository.
Первый и самый проcтой способ: залогировать запросы Hibernate
Указать в application.properties: spring.jpa.show-sql=true
или spring.jpa.properties.hibernate.show_sql=true
Пример ввода:
Так мы можем быстро решить проблему с локальным поиском багов, но есть несколько очевидных минусов:
1. Вывод SQL не проходит через наш логгер и не имеет необходимый для нас формат. Запрос просто уходит в System.out.
2. Вместо параметров запроса мы видим вопросики.
Второй подход: установка уровня логирования
Пример на основе application.properties: logging.level.org.hibernate.SQL=debug
Изменение уровня логов пакета SQL
на debug
добавит в наш лог sql запросы.
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=trace
Изменение же уровня BasicBinder
на trace
добавит нам параметры запросы, правда в слегка непривычной форме - поочередное перечислением после самого запроса.
Пример вывода:
Третий подход: использование proxy драйвера
Например log4jdbc
или p6spy
. Оба прокси рабочие и на них есть стартеры, хотя на log4jdbc
давно не было коммитов на момент написание статьи.
<dependency>
<groupId>com.integralblue</groupId>
<artifactId>log4jdbc-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.gavlyukovskiy</groupId>
<artifactId>p6spy-spring-boot-starter</artifactId>
</dependency>
В принципе, нашей цели мы добились. Но есть одна проблема: иногда запросов очень много, а нам нужны только несколько. Расскажу на примере p6spy
один из способов этого добиться.
Во-первых у нас есть ограничение в самой библиотеке.
В properties
можно указать pattern
по которому будут фильтроваться логируемые запросы.
Например:
decorator.datasource.p6spy.log-filter.pattern=.*insert.*
покажет нам все insert запросы.
Но иногда нам нужно выводить в лог всего один или несколько методов. Сделаем реализацию такой аннотации сами. Объявим аннотацию:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogP6Spy {
}
По сути нам нужно отфильтровать нужные логи по какому то признаку. Я решил сделать это с помощью MDC, у него область видимости как раз ThreadLocal,
что нам на руку. Сделаем фильтр:
public class FilterMdc extends Filter<ILoggingEvent> {
@Override
public FilterReply decide(ILoggingEvent event) {
if (Objects.isNull(MDC.get("Enable"))) {
return FilterReply.DENY;
}
return FilterReply.ACCEPT;
}
}
Обработчик аннотации я сделал через аспект:
@Aspect
@Component
public class AspectLogP6Spy {
@Pointcut("@annotation(com.equant.transaction.LogP6Spy)")
public void annotated() {}
@Before("annotated()")
public void before() {
MDC.put("Enable", "true");
}
@After("annotated()")
public void after() {
MDC.remove("Enable");
}
}
ну и конфигурация на примере Logback
:
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<appender name="STDOUT_FILTER" class="ch.qos.logback.core.ConsoleAppender">
<filter class="com.equant.transaction.FilterMdc">
</filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<logger name="p6spy" level="info" additivity="false">
<appender-ref ref="STDOUT_FILTER"/>
</logger>
<root level="info">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
Создадим дополнительный appender
с фильтром и пропустим через него логи p6spy уровня info
и не забудем указать additivity=«false»
, что бы root appender не обрабатывал этот же пакет. Вот и всё.
Не забываем, что мы сделали это через proxy, а значит у нас есть ограничения в выборе методов, над которыми можно ставить новую аннотацию.
Заключение: Как мы видим в логирование Hibernate нет ничего сложного. Последний подход даёт возможность легко помечать аннотацией только те методы которые нужны.
Была ли у Вас необходимость логировать запросы? Какой подход использовали? Пишите в комментариях.
На этом всё. Не болейте!