diff --git a/doma-slf4j/.gitignore b/doma-slf4j/.gitignore new file mode 100644 index 000000000..96dece6c2 --- /dev/null +++ b/doma-slf4j/.gitignore @@ -0,0 +1,11 @@ +/.classpath +/.gradle/ +/.project +/.settings/ +/bin/ +/build/ +/target/ +/.metadata +.idea +out +.factorypath diff --git a/doma-slf4j/build.gradle.kts b/doma-slf4j/build.gradle.kts new file mode 100644 index 000000000..96cb3cec7 --- /dev/null +++ b/doma-slf4j/build.gradle.kts @@ -0,0 +1,7 @@ +description = "doma-slf4j" + +dependencies { + api("org.slf4j:slf4j-api:1.7.30") + implementation(project(":doma-core")) + testImplementation("ch.qos.logback:logback-classic:1.2.3") +} diff --git a/doma-slf4j/src/main/java/org/seasar/doma/jdbc/LogKind.java b/doma-slf4j/src/main/java/org/seasar/doma/jdbc/LogKind.java new file mode 100644 index 000000000..3b0292eb3 --- /dev/null +++ b/doma-slf4j/src/main/java/org/seasar/doma/jdbc/LogKind.java @@ -0,0 +1,19 @@ +package org.seasar.doma.jdbc; + +public enum LogKind { + DAO, + SKIP, + SQL, + LOCAL_TRANSACTION, + FAILURE; + + private final String fullName; + + LogKind() { + this.fullName = getClass().getName() + "." + name(); + } + + public String fullName() { + return fullName; + } +} diff --git a/doma-slf4j/src/main/java/org/seasar/doma/jdbc/Slf4jJdbcLogger.java b/doma-slf4j/src/main/java/org/seasar/doma/jdbc/Slf4jJdbcLogger.java new file mode 100644 index 000000000..a315e67c0 --- /dev/null +++ b/doma-slf4j/src/main/java/org/seasar/doma/jdbc/Slf4jJdbcLogger.java @@ -0,0 +1,309 @@ +package org.seasar.doma.jdbc; + +import java.sql.SQLException; +import java.util.Objects; +import java.util.function.Supplier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; +import org.slf4j.event.Level; + +public class Slf4jJdbcLogger extends AbstractJdbcLogger { + + public Slf4jJdbcLogger() { + super(Level.DEBUG); + } + + public Slf4jJdbcLogger(Level level) { + super(level); + } + + @SuppressWarnings("CStyleArrayDeclaration") + @Override + protected void logDaoMethodEntering( + String callerClassName, + String callerMethodName, + Object args[], + Level level, + Supplier messageSupplier) { + log(LogKind.DAO.fullName(), level, callerClassName, callerMethodName, null, messageSupplier); + } + + @Override + protected void logDaoMethodExiting( + String callerClassName, + String callerMethodName, + Object result, + Level level, + Supplier messageSupplier) { + log(LogKind.DAO.fullName(), level, callerClassName, callerMethodName, null, messageSupplier); + } + + @Override + protected void logDaoMethodThrowing( + String callerClassName, + String callerMethodName, + RuntimeException e, + Level level, + Supplier messageSupplier) { + log(LogKind.DAO.fullName(), level, callerClassName, callerMethodName, null, messageSupplier); + } + + @Override + protected void logSqlExecutionSkipping( + String callerClassName, + String callerMethodName, + SqlExecutionSkipCause cause, + Level level, + Supplier messageSupplier) { + String loggerName = LogKind.SKIP.fullName() + "." + cause.name(); + log(loggerName, level, callerClassName, callerMethodName, null, messageSupplier); + } + + @Override + protected void logSql( + String callerClassName, + String callerMethodName, + Sql sql, + Level level, + Supplier messageSupplier) { + String loggerName = LogKind.SQL.fullName() + "." + sql.getKind().name(); + log(loggerName, level, callerClassName, callerMethodName, null, messageSupplier); + } + + @Override + protected void logTransactionBegun( + String callerClassName, + String callerMethodName, + String transactionId, + Level level, + Supplier messageSupplier) { + log( + LogKind.LOCAL_TRANSACTION.fullName(), + level, + callerClassName, + callerMethodName, + null, + messageSupplier); + } + + @Override + protected void logTransactionEnded( + String callerClassName, + String callerMethodName, + String transactionId, + Level level, + Supplier messageSupplier) { + log( + LogKind.LOCAL_TRANSACTION.fullName(), + level, + callerClassName, + callerMethodName, + null, + messageSupplier); + } + + @Override + protected void logTransactionSavepointCreated( + String callerClassName, + String callerMethodName, + String transactionId, + String savepointName, + Level level, + Supplier messageSupplier) { + log( + LogKind.LOCAL_TRANSACTION.fullName(), + level, + callerClassName, + callerMethodName, + null, + messageSupplier); + } + + @Override + protected void logTransactionCommitted( + String callerClassName, + String callerMethodName, + String transactionId, + Level level, + Supplier messageSupplier) { + log( + LogKind.LOCAL_TRANSACTION.fullName(), + level, + callerClassName, + callerMethodName, + null, + messageSupplier); + } + + @Override + protected void logTransactionRolledback( + String callerClassName, + String callerMethodName, + String transactionId, + Level level, + Supplier messageSupplier) { + log( + LogKind.LOCAL_TRANSACTION.fullName(), + level, + callerClassName, + callerMethodName, + null, + messageSupplier); + } + + @Override + protected void logTransactionSavepointRolledback( + String callerClassName, + String callerMethodName, + String transactionId, + String savepointName, + Level level, + Supplier messageSupplier) { + log( + LogKind.LOCAL_TRANSACTION.fullName(), + level, + callerClassName, + callerMethodName, + null, + messageSupplier); + } + + @Override + protected void logTransactionSavepointReleased( + String callerClassName, + String callerMethodName, + String transactionId, + String savepointName, + Level level, + Supplier messageSupplier) { + log( + LogKind.LOCAL_TRANSACTION.fullName(), + level, + callerClassName, + callerMethodName, + null, + messageSupplier); + } + + @Override + protected void logTransactionRollbackFailure( + String callerClassName, + String callerMethodName, + SQLException e, + Level level, + Supplier messageSupplier) { + log(LogKind.FAILURE.fullName(), level, callerClassName, callerMethodName, e, messageSupplier); + } + + @Override + protected void logAutoCommitEnablingFailure( + String callerClassName, + String callerMethodName, + SQLException e, + Level level, + Supplier messageSupplier) { + log(LogKind.FAILURE.fullName(), level, callerClassName, callerMethodName, e, messageSupplier); + } + + @Override + protected void logTransactionIsolationSettingFailure( + String callerClassName, + String callerMethodName, + int transactionIsolationLevel, + SQLException e, + Level level, + Supplier messageSupplier) { + log(LogKind.FAILURE.fullName(), level, callerClassName, callerMethodName, e, messageSupplier); + } + + @Override + protected void logConnectionClosingFailure( + String callerClassName, + String callerMethodName, + SQLException e, + Level level, + Supplier messageSupplier) { + log(LogKind.FAILURE.fullName(), level, callerClassName, callerMethodName, e, messageSupplier); + } + + @Override + protected void logStatementClosingFailure( + String callerClassName, + String callerMethodName, + SQLException e, + Level level, + Supplier messageSupplier) { + log(LogKind.FAILURE.fullName(), level, callerClassName, callerMethodName, e, messageSupplier); + } + + @Override + protected void logResultSetClosingFailure( + String callerClassName, + String callerMethodName, + SQLException e, + Level level, + Supplier messageSupplier) { + log(LogKind.FAILURE.fullName(), level, callerClassName, callerMethodName, e, messageSupplier); + } + + protected void log( + String loggerName, + Level level, + String callerClassName, + String callerMethodName, + Throwable throwable, + Supplier messageSupplier) { + Objects.requireNonNull(loggerName); + Objects.requireNonNull(level); + Objects.requireNonNull(messageSupplier); + final Logger logger = LoggerFactory.getLogger(loggerName); + switch (level) { + case ERROR: + if (logger.isErrorEnabled()) { + Marker marker = getMarker(callerClassName, callerMethodName); + logger.error(marker, messageSupplier.get(), throwable); + } + break; + case WARN: + if (logger.isWarnEnabled()) { + Marker marker = getMarker(callerClassName, callerMethodName); + logger.warn(marker, messageSupplier.get(), throwable); + } + break; + case INFO: + if (logger.isInfoEnabled()) { + Marker marker = getMarker(callerClassName, callerMethodName); + logger.info(marker, messageSupplier.get(), throwable); + } + break; + case DEBUG: + if (logger.isDebugEnabled()) { + Marker marker = getMarker(callerClassName, callerMethodName); + logger.debug(marker, messageSupplier.get(), throwable); + } + break; + case TRACE: + if (logger.isTraceEnabled()) { + Marker marker = getMarker(callerClassName, callerMethodName); + logger.trace(marker, messageSupplier.get(), throwable); + } + break; + } + } + + private Marker getMarker(String callerClassName, String callerMethodName) { + return MarkerFactory.getMarker(callerClassName + "#" + callerMethodName); + } + + @Override + protected void log( + Level level, + String callerClassName, + String callerMethodName, + Throwable throwable, + Supplier messageSupplier) { + throw new UnsupportedOperationException(); + } +} diff --git a/doma-slf4j/src/test/java/org/seasar/doma/jdbc/Slf4jJdbcLoggerTest.java b/doma-slf4j/src/test/java/org/seasar/doma/jdbc/Slf4jJdbcLoggerTest.java new file mode 100644 index 000000000..6eeae3bf0 --- /dev/null +++ b/doma-slf4j/src/test/java/org/seasar/doma/jdbc/Slf4jJdbcLoggerTest.java @@ -0,0 +1,113 @@ +package org.seasar.doma.jdbc; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; +import java.sql.SQLException; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; + +class Slf4jJdbcLoggerTest { + + private final Sql selectSql = + new PreparedSql( + SqlKind.SELECT, + "raw", + "formatted", + "sqlFilePath", + Collections.emptyList(), + SqlLogType.FORMATTED); + + private final Sql insertSql = + new PreparedSql( + SqlKind.INSERT, + "raw", + "formatted", + "sqlFilePath", + Collections.emptyList(), + SqlLogType.FORMATTED); + + @AfterEach + void afterEach() { + List events = getEvents(); + events.clear(); + } + + @Test + void constructor() { + Slf4jJdbcLogger logger = new Slf4jJdbcLogger(Level.TRACE); + String className = getClass().getName(); + String methodName = "constructor"; + logger.logSql(className, methodName, selectSql); + + List events = getEvents(); + assertEquals(0, events.size()); + } + + @Test + void logSql() { + Slf4jJdbcLogger logger = new Slf4jJdbcLogger(); + String className = getClass().getName(); + String methodName = "logSql"; + logger.logSql(className, methodName, selectSql); + + List events = getEvents(); + assertEquals(1, events.size()); + } + + @Test + void logSql_childLogger() { + Slf4jJdbcLogger logger = new Slf4jJdbcLogger(); + String className = getClass().getName(); + String methodName = "logSql"; + logger.logSql(className, methodName, insertSql); + + List events = getEvents(); + assertEquals(0, events.size()); + } + + @Test + void logSql_classAndMethod_null() { + Slf4jJdbcLogger logger = new Slf4jJdbcLogger(); + logger.logSql(null, null, selectSql); + + List events = getEvents(); + assertEquals(1, events.size()); + } + + @Test + void logConnectionClosingFailure() { + Slf4jJdbcLogger logger = new Slf4jJdbcLogger(); + String className = getClass().getName(); + String methodName = "logConnectionClosingFailure"; + SQLException exception = new SQLException("my exception"); + logger.logConnectionClosingFailure(className, methodName, exception); + + List events = getEvents(); + assertEquals(1, events.size()); + } + + @Test + void logSqlExecutionSkipping() { + Slf4jJdbcLogger logger = new Slf4jJdbcLogger(); + String className = getClass().getName(); + String methodName = "logSqlExecutionSkipping"; + logger.logSqlExecutionSkipping(className, methodName, SqlExecutionSkipCause.STATE_UNCHANGED); + + List events = getEvents(); + assertEquals(1, events.size()); + } + + private List getEvents() { + ch.qos.logback.classic.Logger rootLogger = + (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); + ListAppender appender = + (ListAppender) rootLogger.getAppender("LIST"); + return appender.list; + } +} diff --git a/doma-slf4j/src/test/resources/logback-test.xml b/doma-slf4j/src/test/resources/logback-test.xml new file mode 100644 index 000000000..b47ba0c9b --- /dev/null +++ b/doma-slf4j/src/test/resources/logback-test.xml @@ -0,0 +1,18 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %marker - %msg%n + + + + + + + + + + + + + + diff --git a/settings.gradle.kts b/settings.gradle.kts index 734b9eb1e..916833a2b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,3 +3,4 @@ include("doma-core") include("doma-kotlin") include("doma-mock") include("doma-processor") +include("doma-slf4j")