Save This Page
Home » slf4j-1.5.5 » org.apache » log4j » jdbc » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    * 
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    * 
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   package org.apache.log4j.jdbc;
   18   
   19   import org.apache.log4j.spi;
   20   import org.apache.log4j.PatternLayout;
   21   
   22   import java.util.ArrayList;
   23   import java.util.Iterator;
   24   
   25   import java.sql.DriverManager;
   26   import java.sql.Connection;
   27   import java.sql.Statement;
   28   import java.sql.SQLException;
   29   
   30   
   31   /**
   32     <p><b><font color="#FF2222">WARNING: This version of JDBCAppender
   33     is very likely to be completely replaced in the future. Moreoever,
   34     it does not log exceptions</font></b>.
   35   
   36     The JDBCAppender provides for sending log events to a database.
   37     
   38     
   39     <p>Each append call adds to an <code>ArrayList</code> buffer.  When
   40     the buffer is filled each log event is placed in a sql statement
   41     (configurable) and executed.
   42   
   43     <b>BufferSize</b>, <b>db URL</b>, <b>User</b>, & <b>Password</b> are
   44     configurable options in the standard log4j ways.
   45   
   46     <p>The <code>setSql(String sql)</code> sets the SQL statement to be
   47     used for logging -- this statement is sent to a
   48     <code>PatternLayout</code> (either created automaticly by the
   49     appender or added by the user).  Therefore by default all the
   50     conversion patterns in <code>PatternLayout</code> can be used
   51     inside of the statement.  (see the test cases for examples)
   52   
   53     <p>Overriding the {@link #getLogStatement} method allows more
   54     explicit control of the statement used for logging.
   55   
   56     <p>For use as a base class:
   57   
   58       <ul>
   59   
   60       <li>Override <code>getConnection()</code> to pass any connection
   61       you want.  Typically this is used to enable application wide
   62       connection pooling.
   63   
   64        <li>Override <code>closeConnection(Connection con)</code> -- if
   65        you override getConnection make sure to implement
   66        <code>closeConnection</code> to handle the connection you
   67        generated.  Typically this would return the connection to the
   68        pool it came from.
   69   
   70        <li>Override <code>getLogStatement(LoggingEvent event)</code> to
   71        produce specialized or dynamic statements. The default uses the
   72        sql option value.
   73   
   74       </ul>
   75   
   76       @author Kevin Steppe (<A HREF="mailto:ksteppe@pacbell.net">ksteppe@pacbell.net</A>)
   77   
   78   */
   79   public class JDBCAppender extends org.apache.log4j.AppenderSkeleton
   80       implements org.apache.log4j.Appender {
   81   
   82     /**
   83      * URL of the DB for default connection handling
   84      */
   85     protected String databaseURL = "jdbc:odbc:myDB";
   86   
   87     /**
   88      * User to connect as for default connection handling
   89      */
   90     protected String databaseUser = "me";
   91   
   92     /**
   93      * User to use for default connection handling
   94      */
   95     protected String databasePassword = "mypassword";
   96   
   97     /**
   98      * Connection used by default.  The connection is opened the first time it
   99      * is needed and then held open until the appender is closed (usually at
  100      * garbage collection).  This behavior is best modified by creating a
  101      * sub-class and overriding the <code>getConnection</code> and
  102      * <code>closeConnection</code> methods.
  103      */
  104     protected Connection connection = null;
  105   
  106     /**
  107      * Stores the string given to the pattern layout for conversion into a SQL
  108      * statement, eg: insert into LogTable (Thread, Class, Message) values
  109      * ("%t", "%c", "%m").
  110      *
  111      * Be careful of quotes in your messages!
  112      *
  113      * Also see PatternLayout.
  114      */
  115     protected String sqlStatement = "";
  116   
  117     /**
  118      * size of LoggingEvent buffer before writting to the database.
  119      * Default is 1.
  120      */
  121     protected int bufferSize = 1;
  122   
  123     /**
  124      * ArrayList holding the buffer of Logging Events.
  125      */
  126     protected ArrayList buffer;
  127   
  128     /**
  129      * Helper object for clearing out the buffer
  130      */
  131     protected ArrayList removes;
  132   
  133     public JDBCAppender() {
  134       super();
  135       buffer = new ArrayList(bufferSize);
  136       removes = new ArrayList(bufferSize);
  137     }
  138   
  139     /**
  140      * Adds the event to the buffer.  When full the buffer is flushed.
  141      */
  142     public void append(LoggingEvent event) {
  143       buffer.add(event);
  144   
  145       if (buffer.size() >= bufferSize)
  146         flushBuffer();
  147     }
  148   
  149     /**
  150      * By default getLogStatement sends the event to the required Layout object.
  151      * The layout will format the given pattern into a workable SQL string.
  152      *
  153      * Overriding this provides direct access to the LoggingEvent
  154      * when constructing the logging statement.
  155      *
  156      */
  157     protected String getLogStatement(LoggingEvent event) {
  158       return getLayout().format(event);
  159     }
  160   
  161     /**
  162      *
  163      * Override this to provide an alertnate method of getting
  164      * connections (such as caching).  One method to fix this is to open
  165      * connections at the start of flushBuffer() and close them at the
  166      * end.  I use a connection pool outside of JDBCAppender which is
  167      * accessed in an override of this method.
  168      * */
  169     protected void execute(String sql) throws SQLException {
  170   
  171       Connection con = null;
  172       Statement stmt = null;
  173   
  174       try {
  175           con = getConnection();
  176   
  177           stmt = con.createStatement();
  178           stmt.executeUpdate(sql);
  179       } catch (SQLException e) {
  180          if (stmt != null)
  181   	     stmt.close();
  182          throw e;
  183       }
  184       stmt.close();
  185       closeConnection(con);
  186   
  187       //System.out.println("Execute: " + sql);
  188     }
  189   
  190   
  191     /**
  192      * Override this to return the connection to a pool, or to clean up the
  193      * resource.
  194      *
  195      * The default behavior holds a single connection open until the appender
  196      * is closed (typically when garbage collected).
  197      */
  198     protected void closeConnection(Connection con) {
  199     }
  200   
  201     /**
  202      * Override this to link with your connection pooling system.
  203      *
  204      * By default this creates a single connection which is held open
  205      * until the object is garbage collected.
  206      */
  207     protected Connection getConnection() throws SQLException {
  208         if (!DriverManager.getDrivers().hasMoreElements())
  209   	     setDriver("sun.jdbc.odbc.JdbcOdbcDriver");
  210   
  211         if (connection == null) {
  212           connection = DriverManager.getConnection(databaseURL, databaseUser,
  213   					databasePassword);
  214         }
  215   
  216         return connection;
  217     }
  218   
  219     /**
  220      * Closes the appender, flushing the buffer first then closing the default
  221      * connection if it is open.
  222      */
  223     public void close()
  224     {
  225       flushBuffer();
  226   
  227       try {
  228         if (connection != null && !connection.isClosed())
  229             connection.close();
  230       } catch (SQLException e) {
  231           errorHandler.error("Error closing connection", e, ErrorCode.GENERIC_FAILURE);
  232       }
  233       this.closed = true;
  234     }
  235   
  236     /**
  237      * loops through the buffer of LoggingEvents, gets a
  238      * sql string from getLogStatement() and sends it to execute().
  239      * Errors are sent to the errorHandler.
  240      *
  241      * If a statement fails the LoggingEvent stays in the buffer!
  242      */
  243     public void flushBuffer() {
  244       //Do the actual logging
  245       removes.ensureCapacity(buffer.size());
  246       for (Iterator i = buffer.iterator(); i.hasNext();) {
  247         try {
  248           LoggingEvent logEvent = (LoggingEvent)i.next();
  249   	    String sql = getLogStatement(logEvent);
  250   	    execute(sql);
  251           removes.add(logEvent);
  252         }
  253         catch (SQLException e) {
  254   	    errorHandler.error("Failed to excute sql", e,
  255   			   ErrorCode.FLUSH_FAILURE);
  256         }
  257       }
  258       
  259       // remove from the buffer any events that were reported
  260       buffer.removeAll(removes);
  261       
  262       // clear the buffer of reported events
  263       removes.clear();
  264     }
  265   
  266   
  267     /** closes the appender before disposal */
  268     public void finalize() {
  269       close();
  270     }
  271   
  272   
  273     /**
  274      * JDBCAppender requires a layout.
  275      * */
  276     public boolean requiresLayout() {
  277       return true;
  278     }
  279   
  280   
  281     /**
  282      *
  283      */
  284     public void setSql(String s) {
  285       sqlStatement = s;
  286       if (getLayout() == null) {
  287           this.setLayout(new PatternLayout(s));
  288       }
  289       else {
  290           ((PatternLayout)getLayout()).setConversionPattern(s);
  291       }
  292     }
  293   
  294   
  295     /**
  296      * Returns pre-formated statement eg: insert into LogTable (msg) values ("%m")
  297      */
  298     public String getSql() {
  299       return sqlStatement;
  300     }
  301   
  302   
  303     public void setUser(String user) {
  304       databaseUser = user;
  305     }
  306   
  307   
  308     public void setURL(String url) {
  309       databaseURL = url;
  310     }
  311   
  312   
  313     public void setPassword(String password) {
  314       databasePassword = password;
  315     }
  316   
  317   
  318     public void setBufferSize(int newBufferSize) {
  319       bufferSize = newBufferSize;
  320       buffer.ensureCapacity(bufferSize);
  321       removes.ensureCapacity(bufferSize);
  322     }
  323   
  324   
  325     public String getUser() {
  326       return databaseUser;
  327     }
  328   
  329   
  330     public String getURL() {
  331       return databaseURL;
  332     }
  333   
  334   
  335     public String getPassword() {
  336       return databasePassword;
  337     }
  338   
  339   
  340     public int getBufferSize() {
  341       return bufferSize;
  342     }
  343   
  344   
  345     /**
  346      * Ensures that the given driver class has been loaded for sql connection
  347      * creation.
  348      */
  349     public void setDriver(String driverClass) {
  350       try {
  351         Class.forName(driverClass);
  352       } catch (Exception e) {
  353         errorHandler.error("Failed to load driver", e,
  354   			 ErrorCode.GENERIC_FAILURE);
  355       }
  356     }
  357   }
  358   

Save This Page
Home » slf4j-1.5.5 » org.apache » log4j » jdbc » [javadoc | source]