You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-cvs@jakarta.apache.org by ce...@apache.org on 2002/03/25 22:57:58 UTC

cvs commit: jakarta-log4j/src/java/org/apache/log4j/jdbc JDBCAppender.java

ceki        02/03/25 13:57:58

  Added:       src/java/org/apache/log4j/jdbc JDBCAppender.java
  Log:
  The JDBCAppender provides for sendinhg log messages to a database.
  
  Submitted by Kevin Steppe
  
  Revision  Changes    Path
  1.1                  jakarta-log4j/src/java/org/apache/log4j/jdbc/JDBCAppender.java
  
  Index: JDBCAppender.java
  ===================================================================
  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:ksteppe@pacbell.net">ksteppe@pacbell.net</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);
      }
    }
  
  }
  
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>