ARTEMIS-920 Log SQL Exceptions and Warnings
This commit is contained in:
parent
c441625e5a
commit
837066d40d
|
@ -22,9 +22,12 @@ import java.sql.Driver;
|
||||||
import java.sql.DriverManager;
|
import java.sql.DriverManager;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.sql.SQLWarning;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.apache.activemq.artemis.jdbc.store.sql.SQLProvider;
|
import org.apache.activemq.artemis.jdbc.store.sql.SQLProvider;
|
||||||
import org.apache.activemq.artemis.journal.ActiveMQJournalLogger;
|
import org.apache.activemq.artemis.journal.ActiveMQJournalLogger;
|
||||||
|
@ -61,7 +64,7 @@ public abstract class AbstractJDBCDriver {
|
||||||
this.sqlProvider = provider;
|
this.sqlProvider = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() throws Exception {
|
public void start() throws SQLException {
|
||||||
connect();
|
connect();
|
||||||
createSchema();
|
createSchema();
|
||||||
prepareStatements();
|
prepareStatements();
|
||||||
|
@ -69,7 +72,12 @@ public abstract class AbstractJDBCDriver {
|
||||||
|
|
||||||
public void stop() throws SQLException {
|
public void stop() throws SQLException {
|
||||||
if (sqlProvider.closeConnectionOnShutdown()) {
|
if (sqlProvider.closeConnectionOnShutdown()) {
|
||||||
|
try {
|
||||||
connection.close();
|
connection.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logger.error(JDBCUtils.appendSQLExceptionDetails(new StringBuilder(), e));
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,58 +85,100 @@ public abstract class AbstractJDBCDriver {
|
||||||
|
|
||||||
protected abstract void createSchema() throws SQLException;
|
protected abstract void createSchema() throws SQLException;
|
||||||
|
|
||||||
protected void createTable(String... schemaSqls) throws SQLException {
|
protected final void createTable(String... schemaSqls) throws SQLException {
|
||||||
createTableIfNotExists(connection, sqlProvider.getTableName(), schemaSqls);
|
createTableIfNotExists(connection, sqlProvider.getTableName(), schemaSqls);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void connect() throws Exception {
|
private void connect() throws SQLException {
|
||||||
if (dataSource != null) {
|
if (dataSource != null) {
|
||||||
|
try {
|
||||||
connection = dataSource.getConnection();
|
connection = dataSource.getConnection();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logger.error(JDBCUtils.appendSQLExceptionDetails(new StringBuilder(), e));
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
Driver dbDriver = getDriver(jdbcDriverClass);
|
if (jdbcDriverClass == null || jdbcDriverClass.isEmpty()) {
|
||||||
|
throw new IllegalStateException("jdbcDriverClass is null or empty!");
|
||||||
|
}
|
||||||
|
if (jdbcConnectionUrl == null || jdbcConnectionUrl.isEmpty()) {
|
||||||
|
throw new IllegalStateException("jdbcConnectionUrl is null or empty!");
|
||||||
|
}
|
||||||
|
final Driver dbDriver = getDriver(jdbcDriverClass);
|
||||||
connection = dbDriver.connect(jdbcConnectionUrl, new Properties());
|
connection = dbDriver.connect(jdbcConnectionUrl, new Properties());
|
||||||
|
if (connection == null) {
|
||||||
|
throw new IllegalStateException("the driver: " + jdbcDriverClass + " isn't able to connect to the requested url: " + jdbcConnectionUrl);
|
||||||
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
logger.error(JDBCUtils.appendSQLExceptionDetails(new StringBuilder(), e));
|
||||||
ActiveMQJournalLogger.LOGGER.error("Unable to connect to database using URL: " + jdbcConnectionUrl);
|
ActiveMQJournalLogger.LOGGER.error("Unable to connect to database using URL: " + jdbcConnectionUrl);
|
||||||
throw new RuntimeException("Error connecting to database", e);
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() throws Exception {
|
public void destroy() throws Exception {
|
||||||
|
final String dropTableSql = "DROP TABLE " + sqlProvider.getTableName();
|
||||||
try {
|
try {
|
||||||
connection.setAutoCommit(false);
|
connection.setAutoCommit(false);
|
||||||
try (Statement statement = connection.createStatement()) {
|
try (Statement statement = connection.createStatement()) {
|
||||||
statement.executeUpdate("DROP TABLE " + sqlProvider.getTableName());
|
statement.executeUpdate(dropTableSql);
|
||||||
}
|
}
|
||||||
connection.commit();
|
connection.commit();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
logger.error(JDBCUtils.appendSQLExceptionDetails(new StringBuilder(), e, dropTableSql));
|
||||||
|
try {
|
||||||
connection.rollback();
|
connection.rollback();
|
||||||
|
} catch (SQLException rollbackEx) {
|
||||||
|
logger.error(JDBCUtils.appendSQLExceptionDetails(new StringBuilder(), rollbackEx, dropTableSql));
|
||||||
|
throw rollbackEx;
|
||||||
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void createTableIfNotExists(Connection connection, String tableName, String... sqls) throws SQLException {
|
private static void createTableIfNotExists(Connection connection,
|
||||||
|
String tableName,
|
||||||
|
String... sqls) throws SQLException {
|
||||||
logger.tracef("Validating if table %s didn't exist before creating", tableName);
|
logger.tracef("Validating if table %s didn't exist before creating", tableName);
|
||||||
try {
|
try {
|
||||||
connection.setAutoCommit(false);
|
connection.setAutoCommit(false);
|
||||||
try (ResultSet rs = connection.getMetaData().getTables(null, null, tableName, null)) {
|
try (ResultSet rs = connection.getMetaData().getTables(null, null, tableName, null)) {
|
||||||
if (rs != null && !rs.next()) {
|
if (rs != null && !rs.next()) {
|
||||||
|
if (logger.isTraceEnabled()) {
|
||||||
logger.tracef("Table %s did not exist, creating it with SQL=%s", tableName, Arrays.toString(sqls));
|
logger.tracef("Table %s did not exist, creating it with SQL=%s", tableName, Arrays.toString(sqls));
|
||||||
|
}
|
||||||
|
final SQLWarning sqlWarning = rs.getWarnings();
|
||||||
|
if (sqlWarning != null) {
|
||||||
|
logger.warn(JDBCUtils.appendSQLExceptionDetails(new StringBuilder(), sqlWarning));
|
||||||
|
}
|
||||||
try (Statement statement = connection.createStatement()) {
|
try (Statement statement = connection.createStatement()) {
|
||||||
for (String sql : sqls) {
|
for (String sql : sqls) {
|
||||||
statement.executeUpdate(sql);
|
statement.executeUpdate(sql);
|
||||||
|
final SQLWarning statementSqlWarning = statement.getWarnings();
|
||||||
|
if (statementSqlWarning != null) {
|
||||||
|
logger.warn(JDBCUtils.appendSQLExceptionDetails(new StringBuilder(), statementSqlWarning, sql));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
connection.commit();
|
connection.commit();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
final String sqlStatements = Stream.of(sqls).collect(Collectors.joining("\n"));
|
||||||
|
logger.error(JDBCUtils.appendSQLExceptionDetails(new StringBuilder(), e, sqlStatements));
|
||||||
|
try {
|
||||||
connection.rollback();
|
connection.rollback();
|
||||||
|
} catch (SQLException rollbackEx) {
|
||||||
|
logger.error(JDBCUtils.appendSQLExceptionDetails(new StringBuilder(), rollbackEx, sqlStatements));
|
||||||
|
throw rollbackEx;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Driver getDriver(String className) throws Exception {
|
private Driver getDriver(String className) {
|
||||||
try {
|
try {
|
||||||
Driver driver = (Driver) Class.forName(className).newInstance();
|
Driver driver = (Driver) Class.forName(className).newInstance();
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.activemq.artemis.jdbc.store.drivers;
|
package org.apache.activemq.artemis.jdbc.store.drivers;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
import org.apache.activemq.artemis.jdbc.store.drivers.derby.DerbySQLProvider;
|
import org.apache.activemq.artemis.jdbc.store.drivers.derby.DerbySQLProvider;
|
||||||
import org.apache.activemq.artemis.jdbc.store.drivers.mysql.MySQLSQLProvider;
|
import org.apache.activemq.artemis.jdbc.store.drivers.mysql.MySQLSQLProvider;
|
||||||
import org.apache.activemq.artemis.jdbc.store.drivers.postgres.PostgresSQLProvider;
|
import org.apache.activemq.artemis.jdbc.store.drivers.postgres.PostgresSQLProvider;
|
||||||
|
@ -63,4 +65,59 @@ public class JDBCUtils {
|
||||||
return factory.create(tableName);
|
return factory.create(tableName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append to {@code errorMessage} a detailed description of the provided {@link SQLException}.<br/>
|
||||||
|
* The information appended are:
|
||||||
|
* <ul>
|
||||||
|
* <li>SQL STATEMENTS</li>
|
||||||
|
* <li>SQL EXCEPTIONS details ({@link SQLException#getSQLState},
|
||||||
|
* {@link SQLException#getErrorCode} and {@link SQLException#getMessage}) of the linked list ({@link SQLException#getNextException}) of exceptions</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param errorMessage the target where append the exceptions details
|
||||||
|
* @param exception the SQL exception (or warning)
|
||||||
|
* @param sqlStatements the SQL statements related to the {@code exception}
|
||||||
|
* @return {@code errorMessage}
|
||||||
|
*/
|
||||||
|
public static StringBuilder appendSQLExceptionDetails(StringBuilder errorMessage,
|
||||||
|
SQLException exception,
|
||||||
|
CharSequence sqlStatements) {
|
||||||
|
errorMessage.append("\nSQL STATEMENTS: \n").append(sqlStatements);
|
||||||
|
return appendSQLExceptionDetails(errorMessage, exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append to {@code errorMessage} a detailed description of the provided {@link SQLException}.<br/>
|
||||||
|
* The information appended are:
|
||||||
|
* <ul>
|
||||||
|
* <li>SQL EXCEPTIONS details ({@link SQLException#getSQLState},
|
||||||
|
* {@link SQLException#getErrorCode} and {@link SQLException#getMessage}) of the linked list ({@link SQLException#getNextException}) of exceptions</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param errorMessage the target where append the exceptions details
|
||||||
|
* @param exception the SQL exception (or warning)
|
||||||
|
* @return {@code errorMessage}
|
||||||
|
*/
|
||||||
|
public static StringBuilder appendSQLExceptionDetails(StringBuilder errorMessage, SQLException exception) {
|
||||||
|
errorMessage.append("\nSQL EXCEPTIONS: ");
|
||||||
|
SQLException nextEx = exception;
|
||||||
|
int level = 0;
|
||||||
|
do {
|
||||||
|
errorMessage.append('\n');
|
||||||
|
for (int i = 0; i < level; i++) {
|
||||||
|
errorMessage.append(' ');
|
||||||
|
}
|
||||||
|
formatSqlException(errorMessage, nextEx);
|
||||||
|
nextEx = exception.getNextException();
|
||||||
|
level++;
|
||||||
|
} while (nextEx != null);
|
||||||
|
return errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StringBuilder formatSqlException(StringBuilder errorMessage, SQLException exception) {
|
||||||
|
final String sqlState = exception.getSQLState();
|
||||||
|
final int errorCode = exception.getErrorCode();
|
||||||
|
final String message = exception.getMessage();
|
||||||
|
return errorMessage.append("SQLState: ").append(sqlState).append(" ErrorCode: ").append(errorCode).append(" Message: ").append(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ public class JDBCSequentialFileFactory implements SequentialFileFactory, ActiveM
|
||||||
started = true;
|
started = true;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ActiveMQJournalLogger.LOGGER.error("Could not start file factory, unable to connect to database");
|
ActiveMQJournalLogger.LOGGER.error("Could not start file factory, unable to connect to database", e);
|
||||||
started = false;
|
started = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,7 +107,7 @@ public class JDBCJournalImpl extends AbstractJDBCDriver implements Journal {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() throws Exception {
|
public void start() throws SQLException {
|
||||||
super.start();
|
super.start();
|
||||||
syncTimer = new JDBCJournalSync(scheduledExecutorService, completeExecutor, SYNC_DELAY, TimeUnit.MILLISECONDS, this);
|
syncTimer = new JDBCJournalSync(scheduledExecutorService, completeExecutor, SYNC_DELAY, TimeUnit.MILLISECONDS, this);
|
||||||
started = true;
|
started = true;
|
||||||
|
|
Loading…
Reference in New Issue