+++ /dev/null
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.log4j.jdbc;
-
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Iterator;
-
-import org.apache.log4j.PatternLayout;
-import org.apache.log4j.spi.ErrorCode;
-import org.apache.log4j.spi.LoggingEvent;
-
-
-/**
- The JDBCAppender provides for sending log events to a database.
-
- <p><b><font color="#FF2222">WARNING: This version of JDBCAppender
- is very likely to be completely replaced in the future. Moreoever,
- it does not log exceptions</font></b>.
-
- <p>Each append call adds to an <code>ArrayList</code> buffer. When
- the buffer is filled each log event is placed in a sql statement
- (configurable) and executed.
-
- <b>BufferSize</b>, <b>db URL</b>, <b>User</b>, & <b>Password</b> are
- configurable options in the standard log4j ways.
-
- <p>The <code>setSql(String sql)</code> sets the SQL statement to be
- used for logging -- this statement is sent to a
- <code>PatternLayout</code> (either created automaticly by the
- appender or added by the user). Therefore by default all the
- conversion patterns in <code>PatternLayout</code> can be used
- inside of the statement. (see the test cases for examples)
-
- <p>Overriding the {@link #getLogStatement} method allows more
- explicit control of the statement used for logging.
-
- <p>For use as a base class:
-
- <ul>
-
- <li>Override <code>getConnection()</code> to pass any connection
- you want. Typically this is used to enable application wide
- connection pooling.
-
- <li>Override <code>closeConnection(Connection con)</code> -- if
- you override getConnection make sure to implement
- <code>closeConnection</code> to handle the connection you
- generated. Typically this would return the connection to the
- pool it came from.
-
- <li>Override <code>getLogStatement(LoggingEvent event)</code> to
- produce specialized or dynamic statements. The default uses the
- sql option value.
-
- </ul>
-
- @author Kevin Steppe (<A HREF="mailto:ksteppe@pacbell.net">ksteppe@pacbell.net</A>)
-
-*/
-public class JDBCAppender extends org.apache.log4j.AppenderSkeleton {
-
- /**
- * 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;
-
- private boolean locationInfo = false;
-
- public JDBCAppender() {
- super();
- buffer = new ArrayList(bufferSize);
- removes = new ArrayList(bufferSize);
- }
-
- /**
- * Gets whether the location of the logging request call
- * should be captured.
- *
- * @since 1.2.16
- * @return the current value of the <b>LocationInfo</b> option.
- */
- public boolean getLocationInfo() {
- return locationInfo;
- }
-
- /**
- * The <b>LocationInfo</b> option takes a boolean value. By default, it is
- * set to false which means there will be no effort to extract the location
- * information related to the event. As a result, the event that will be
- * ultimately logged will likely to contain the wrong location information
- * (if present in the log format).
- * <p/>
- * <p/>
- * Location information extraction is comparatively very slow and should be
- * avoided unless performance is not a concern.
- * </p>
- * @since 1.2.16
- * @param flag true if location information should be extracted.
- */
- public void setLocationInfo(final boolean flag) {
- locationInfo = flag;
- }
-
-
- /**
- * Adds the event to the buffer. When full the buffer is flushed.
- */
- public void append(LoggingEvent event) {
- event.getNDC();
- event.getThreadName();
- // Get a copy of this thread's MDC.
- event.getMDCCopy();
- if (locationInfo) {
- event.getLocationInformation();
- }
- event.getRenderedMessage();
- event.getThrowableStrRep();
- 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);
- } finally {
- if(stmt != null) {
- 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();) {
- LoggingEvent logEvent = (LoggingEvent)i.next();
- try {
- String sql = getLogStatement(logEvent);
- execute(sql);
- }
- catch (SQLException e) {
- errorHandler.error("Failed to excute sql", e,
- ErrorCode.FLUSH_FAILURE);
- } finally {
- removes.add(logEvent);
- }
- }
-
- // remove from the buffer any events that were reported
- buffer.removeAll(removes);
-
- // clear the buffer of reported events
- removes.clear();
- }
-
-
- /** closes the appender before disposal */
- public void finalize() {
- close();
- }
-
-
- /**
- * JDBCAppender requires a layout.
- * */
- public boolean requiresLayout() {
- return true;
- }
-
-
- /**
- *
- */
- public void setSql(String sql) {
- sqlStatement = sql;
- if (getLayout() == null) {
- this.setLayout(new PatternLayout(sql));
- }
- else {
- ((PatternLayout)getLayout()).setConversionPattern(sql);
- }
- }
-
-
- /**
- * 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);
- }
- }
-}
-