From ef2a47220bf742cd4ac35357a2b2c16d71d48cff Mon Sep 17 00:00:00 2001 From: Matt Sicker Date: Mon, 6 Jan 2014 21:36:21 -0600 Subject: [PATCH 2/2] Add transaction support for AbstractDatabaseManager. --- .../core/appender/db/AbstractDatabaseManager.java | 53 ++++++++++++++++++++-- .../core/appender/db/jdbc/JDBCDatabaseManager.java | 36 ++++++++++++--- 2 files changed, 77 insertions(+), 12 deletions(-) diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManager.java index 4c3652e..9c59275 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManager.java @@ -16,12 +16,13 @@ */ package org.apache.logging.log4j.core.appender.db; -import java.util.ArrayList; - import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.AbstractManager; +import org.apache.logging.log4j.core.appender.AppenderLoggingException; import org.apache.logging.log4j.core.appender.ManagerFactory; +import java.util.ArrayList; + /** * Manager that allows database appenders to have their configuration reloaded without losing events. */ @@ -109,13 +110,39 @@ public abstract class AbstractDatabaseManager extends AbstractManager { protected abstract void writeInternal(LogEvent event); /** + * Gets a new transaction object. By default, returns a fake transaction object. This should be overridden by + * implementations that support transactions and are not in auto-commit mode. + * + * @return a new database transaction. + */ + public AbstractTransaction newTransaction() { + return FakeTransaction.getInstance(); + } + + /** * This method is called automatically when the buffer size reaches its maximum or at the beginning of a call to * {@link #disconnect()}. It can also be called manually to flush events to the database. */ public final synchronized void flush() { if (this.isConnected() && this.buffer.size() > 0) { - for (final LogEvent event : this.buffer) { - this.writeInternal(event); + final AbstractTransaction transaction = newTransaction(); + try { + transaction.begin(); + for (final LogEvent event : this.buffer) { + this.writeInternal(event); + } + } catch (TransactionException e) { + LOGGER.warn("Error while flushing log event buffer in log manager [{}].", this.getName(), e); + } catch (AppenderLoggingException e) { + transaction.setRollbackOnly(); + throw LOGGER.throwing(e); + } finally { + try { + transaction.commit(); + } catch (TransactionException e) { + LOGGER.warn("Error committing transaction in log manager [{}].", this.getName(), e); + transaction.rollback(); + } } this.buffer.clear(); } @@ -133,7 +160,23 @@ public abstract class AbstractDatabaseManager extends AbstractManager { this.flush(); } } else { - this.writeInternal(event); + final AbstractTransaction transaction = newTransaction(); + try { + transaction.begin(); + this.writeInternal(event); + } catch (TransactionException e) { + LOGGER.warn("Error writing log event to database in log manager [{}].", this.getName(), e); + } catch (AppenderLoggingException e) { + transaction.setRollbackOnly(); + throw LOGGER.throwing(e); + } finally { + try { + transaction.commit(); + } catch (TransactionException e) { + LOGGER.warn("Error committing transaction in log manager [{}].", this.getName(), e); + transaction.rollback(); + } + } } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JDBCDatabaseManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JDBCDatabaseManager.java index 0c48ac4..6e31cb2 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JDBCDatabaseManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JDBCDatabaseManager.java @@ -16,6 +16,14 @@ */ package org.apache.logging.log4j.core.appender.db.jdbc; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AppenderLoggingException; +import org.apache.logging.log4j.core.appender.ManagerFactory; +import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager; +import org.apache.logging.log4j.core.appender.db.AbstractTransaction; +import org.apache.logging.log4j.core.helpers.Closer; +import org.apache.logging.log4j.core.layout.PatternLayout; + import java.io.StringReader; import java.sql.Connection; import java.sql.PreparedStatement; @@ -24,13 +32,6 @@ import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.appender.AppenderLoggingException; -import org.apache.logging.log4j.core.appender.ManagerFactory; -import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager; -import org.apache.logging.log4j.core.helpers.Closer; -import org.apache.logging.log4j.core.layout.PatternLayout; - /** * An {@link AbstractDatabaseManager} implementation for relational databases accessed via JDBC. */ @@ -110,6 +111,27 @@ public final class JDBCDatabaseManager extends AbstractDatabaseManager { } } + @Override + public AbstractTransaction newTransaction() { + if (this.isTransactional()) { + return new JDBCTransaction(this.connection); + } else { + return super.newTransaction(); + } + } + + private synchronized boolean isTransactional() { + if (!this.isConnected()) { + return false; + } + try { + return !this.connection.isClosed() && !this.connection.getAutoCommit(); + } catch (SQLException e) { + LOGGER.warn("Error checking for auto-commit in log manager [{}].", this.getName(), e); + return false; + } + } + /** * Creates a JDBC manager for use within the {@link JDBCAppender}, or returns a suitable one if it already exists. * -- 1.8.5.2