Ceki, I finally tested and debugged the new JDBCAppender. This should now be ready for inclusion. I've attached both the source file and an example config file. Code follows: -------------------------------------------------------------------------------------
package org.apache.log4j; import org.apache.log4j.*; import org.apache.log4j.spi.*; import org.apache.log4j.PatternLayout; import org.apache.log4j.helpers.OptionConverter; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import java.sql.DriverManager; import java.sql.Connection; import java.sql.Statement; import java.sql.SQLException; /** * The JDBCAppender provides for sendinhg log messages to a database. * * Each append call adds to an ArrayList buffer. When the buffer is filled * each log event is placed in a sql statement (configurable) and executed. * * BufferSize, db URL, User, & Password are configurable options in * the standard Log4J ways. * * The setSql(String sql) sets the SQL statement to be used for logging -- * this statement is sent to a PatternLayout (either created automaticly * by the appender or added by the user). Therefore by default all the * conversion patterns in PatternLayout can be used inside of the statement. * (see the test cases for examples) * * Overriding the getLogStatement method allows more explicit control of the * statement used for logging. * * For use as a base class: * * Override getConnection() to pass any connection you want. * Typically this is used to enable application wide connection pooling. * Override closeConnection(Connection con) -- if you override getConnection * make sure to implement closeConnection to handle the connection you * generated. Typically this would return the connection to the pool it * came from. * * Override getLogStatement(LoggingEvent event) to produce specialized or * dynamic statements The default uses the sql option value * * @author: Kevin Steppe (<A HREF="mailto:[EMAIL PROTECTED]">[EMAIL PROTECTED]</A>) */ public class JDBCAppender extends org.apache.log4j.AppenderSkeleton implements org.apache.log4j.Appender { /** * URL of the DB for default connection handling */ protected String databaseURL = "jdbc:odbc:myDB"; /** * User to connect as for default connection handling */ protected String databaseUser = "me"; /** * User to use for default connection handling */ protected String databasePassword = "mypassword"; /** * Connection used by default. The connection is opened the first time it * is needed and then held open until the appender is closed (usually at * garbage collection). This behavior is best modified by creating a * sub-class and overriding the <code>getConnection</code> and * <code>closeConnection</code> methods. */ protected Connection connection = null; /** * Stores the string given to the pattern layout for conversion into a SQL * statement, eg: insert into LogTable (Thread, Class, Message) values * ("%t", "%c", "%m") * * Be careful of quotes in your messages! * * Also see PatternLayout. */ protected String sqlStatement = ""; /** * size of LoggingEvent buffer before writting to the database. * Default is 1. */ protected int bufferSize = 1; /** * ArrayList holding the buffer of Logging Events. */ protected ArrayList buffer; /** * Helper object for clearing out the buffer */ protected ArrayList removes; public JDBCAppender() { super(); buffer = new ArrayList(bufferSize); removes = new ArrayList(bufferSize); } /** * Adds the event to the buffer. When full the buffer is flushed. */ public void append(LoggingEvent event) { buffer.add(event); if (buffer.size() >= bufferSize) flushBuffer(); } /** * By default getLogStatement sends the event to the required Layout object. * The layout will format the given pattern into a workable SQL string. * * Overriding this provides direct access to the LoggingEvent * when constructing the logging statement. * */ protected String getLogStatement(LoggingEvent event) { return getLayout().format(event); } /** * * Override this to provide an alertnate method of getting * connections (such as caching). One method to fix this is to open * connections at the start of flushBuffer() and close them at the * end. I use a connection pool outside of JDBCAppender which is * accessed in an override of this method. * */ protected void execute(String sql) throws SQLException { Connection con = null; Statement stmt = null; try { con = getConnection(); stmt = con.createStatement(); stmt.executeUpdate(sql); } catch (SQLException e) { if (stmt != null) stmt.close(); throw e; } stmt.close(); closeConnection(con); System.out.println("Execute: " + sql); } /** * Override this to return the connection to a pool, or to clean up the * resource. * * The default behavior holds a single connection open until the appender * is closed (typically when garbage collected). */ protected void closeConnection(Connection con) { } /** * Override this to link with your connection pooling system. * * By default this creates a single connection which is held open * until the object is garbage collected. */ protected Connection getConnection() throws SQLException { if (!DriverManager.getDrivers().hasMoreElements()) setDriver("sun.jdbc.odbc.JdbcOdbcDriver"); if (connection == null) { connection = DriverManager.getConnection(databaseURL, databaseUser, databasePassword); } return connection; } /** * Closes the appender, flushing the buffer first then closing the default * connection if it is open. */ public void close() { flushBuffer(); try { if (connection != null && !connection.isClosed()) connection.close(); } catch (SQLException e) { errorHandler.error("Error closing connection", e, ErrorCode.GENERIC_FAILURE); } this.closed = true; } /** * loops through the buffer of LoggingEvents, gets a * sql string from getLogStatement() and sends it to execute(). * Errors are sent to the errorHandler. * * If a statement fails the LoggingEvent stays in the buffer! */ public void flushBuffer() { //Do the actual logging removes.ensureCapacity(buffer.size()); for (Iterator i = buffer.iterator(); i.hasNext();) { try { LoggingEvent logEvent = (LoggingEvent)i.next(); String sql = getLogStatement(logEvent); execute(sql); removes.add(logEvent); } catch (SQLException e) { errorHandler.error("Failed to excute sql", e, ErrorCode.FLUSH_FAILURE); } } buffer.removeAll(removes); //buffer.clear(); } /** closes the appender before disposal */ public void finalize() { close(); } /** * JDBCAppender builds a layout internally if one is not provided. */ public boolean requiresLayout() { return false; } /** * */ public void setSql(String s) { sqlStatement = s; if (getLayout() == null) { this.setLayout(new PatternLayout(s)); } else { ((PatternLayout)getLayout()).setConversionPattern(s); } } /** * Returns pre-formated statement eg: insert into LogTable (msg) values ("%m") */ public String getSql() { return sqlStatement; } public void setUser(String user) { databaseUser = user; } public void setURL(String url) { databaseURL = url; } public void setPassword(String password) { databasePassword = password; } public void setBufferSize(int newBufferSize) { bufferSize = newBufferSize; buffer.ensureCapacity(bufferSize); removes.ensureCapacity(bufferSize); } public String getUser() { return databaseUser; } public String getURL() { return databaseURL; } public String getPassword() { return databasePassword; } public int getBufferSize() { return bufferSize; } /** * Ensures that the given driver class has been loaded for sql connection * creation. */ public void setDriver(String driverClass) { try { Class.forName(driverClass); } catch (Exception e) { errorHandler.error("Failed to load driver", e, ErrorCode.GENERIC_FAILURE); } } }
# An example config file for JDBCAppender: log4j.rootCategory=DEBUG, jdbc # JDBCAppender writes messages into the database log4j.appender.jdbc=org.apache.log4j.JDBCAppender # DB Options log4j.appender.jdbc.URL=jdbc:odbc:myDB log4j.appender.jdbc.user=me log4j.appender.jdbc.password=password log4j.appender.jdbc.driver=sun.jdbc.odbc.JdbcOdbcDriver #SQL statement to be used (with multiple columns formated) log4j.appender.jdbc.sql=insert into logTable (message, class, priority, log_date) values ('%m', '%c', '%p', '%d') #set the buffer size log4j.appender.JDBC.buffersize=2
JDBCAppender.java
Description: application/unknown-content-type-jbuilder.javasource
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>