You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-dev@logging.apache.org by ce...@apache.org on 2004/05/04 11:36:03 UTC

cvs commit: logging-log4j/src/java/org/apache/log4j/db/dialect postgresql.sql MySQLDialect.java mysql.sql oracle.sql SQLDialect.java PostgreSQLDialect.java

ceki        2004/05/04 02:36:03

  Added:       src/java/org/apache/log4j/db UrlConnectionSource.java
                        ConnectionSource.java DBAppender.java
                        JNDIConnectionSource.java
                        ConnectionSourceSkeleton.java
               src/java/org/apache/log4j/db/dialect postgresql.sql
                        MySQLDialect.java mysql.sql oracle.sql
                        SQLDialect.java PostgreSQLDialect.java
  Log:
  An initial draft of DBAppender. It seems to work on MySQL and PostgreSQL using the URLConnectionSource
  
  Revision  Changes    Path
  1.1                  logging-log4j/src/java/org/apache/log4j/db/UrlConnectionSource.java
  
  Index: UrlConnectionSource.java
  ===================================================================
  /*
   * Copyright 1999,2004 The Apache Software Foundation.
   *
   * Licensed 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.db;
  
  import java.sql.Connection;
  import java.sql.DriverManager;
  import java.sql.SQLException;
  
  import org.apache.log4j.spi.ErrorCode;
  
  
  /**
   *  The UrlConnectionSource is an implementation of {@link ConnectionSource}
   *  that obtains the Connection in the traditional JDBC manner based on the
   *  connection URL.
   *  <p>
   *  Note that this class will establish a new Connection for each call to
   *  {@link #getConnection()}.  It is recommended that you either use a JDBC
   *  driver that natively supported Connection pooling or that you create
   *  your own implementation of {@link ConnectionSource} that taps into whatever
   *  pooling mechanism you are already using.  (If you have access to a JNDI
   *  implementation that supports {@link javax.sql.DataSource}s, e.g. within
   *  a J2EE application server, see {@link JNDIConnectionSource}).  See
   *  <a href="#dbcp">below</a> for a configuration example that uses the
   *  <a href="http://jakarta.apache.org/commons/dbcp/index.html">commons-dbcp</a>
   *  package from Apache.
   *  <p>
   *  Sample configuration:<br>
   *  <pre>
   *     &lt;connectionSource class="org.apache.log4j.jdbc.UrlConnectionSource"&gt;
   *        &lt;param name="driver" value="com.mysql.jdbc.Driver" /&gt;
   *        &lt;param name="url" value="jdbc:mysql://localhost:3306/mydb" /&gt;
   *        &lt;param name="username" value="myUser" /&gt;
   *        &lt;param name="password" value="myPassword" /&gt;
   *     &lt;/connectionSource&gt;
   *  </pre>
   *  <p>
   *  <a name="dbcp">If</a> you do not have another connection pooling mechanism
   *  built into your application, you can use  the
   *  <a href="http://jakarta.apache.org/commons/dbcp/index.html">commons-dbcp</a>
   *  package from Apache:<br>
   *  <pre>
   *     &lt;connectionSource class="org.apache.log4j.jdbc.UrlConnectionSource"&gt;
   *        &lt;param name="driver" value="org.apache.commons.dbcp.PoolingDriver" /&gt;
   *        &lt;param name="url" value="jdbc:apache:commons:dbcp:/myPoolingDriver" /&gt;
   *     &lt;/connectionSource&gt;
   *  </pre>
   *  Then the configuration information for the commons-dbcp package goes into
   *  the file myPoolingDriver.jocl and is placed in the classpath.  See the
   *  <a href="http://jakarta.apache.org/commons/dbcp/index.html">commons-dbcp</a>
   *  documentation for details.
   *
   *  @author <a href="mailto:rdecampo@twcny.rr.com">Ray DeCampo</a>
   */
  public class UrlConnectionSource
         extends ConnectionSourceSkeleton {
    static private final String POSTGRES_PART = "postgresql";
    static private final String MYSQL_PART = "mysql";
    private String driverClass = null;
    protected String url = null;
  
    public void activateOptions() {
      try {
        if (driverClass != null) {
          Class.forName(driverClass);
        } else if (errorHandler != null) {
          errorHandler.error("WARNING: No JDBC driver specified for log4j " + "UrlConnectionSource.");
        }
      } catch (final ClassNotFoundException cnfe) {
        if (errorHandler != null) {
          errorHandler.error("Could not load JDBC driver class: " + driverClass, cnfe, ErrorCode.GENERIC_FAILURE);
        } else {
          cnfe.printStackTrace();
        }
      }
    }
  
  
    /**
     * @see org.apache.log4j.db.ConnectionSource#getConnection()
     */
    public Connection getConnection()
           throws SQLException {
      if (user == null) {
        return DriverManager.getConnection(url);
      } else {
        return DriverManager.getConnection(url, user, password);
      }
    }
  
  
    /**
     * Returns the url.
     * @return String
     */
    public String getUrl() {
      return url;
    }
  
  
    /**
     * Sets the url.
     * @param url The url to set
     */
    public void setUrl(String url) {
      this.url = url;
    }
  
  
    /**
     * Returns the name of the driver class.
     * @return String
     */
    public String getDriverClass() {
      return driverClass;
    }
  
  
    /**
     * Sets the driver class.
     * @param driver The driver to set
     */
    public void setDriverClass(String driverClass) {
      this.driverClass = driverClass;
    }
  
  
    public int getSQLDialect() {
      int dialectCode = 0;
  
      if (url == null) {
        return ConnectionSource.UNKNOWN_DIALECT;
      }
  
      if (url.indexOf(POSTGRES_PART) != -1) {
        return ConnectionSource.POSTGRES_DIALECT;
      } else if (url.indexOf(MYSQL_PART) != -1) {
        return ConnectionSource.MYSQL_DIALECT;
      } else {
        return ConnectionSource.UNKNOWN_DIALECT;
      }
    }
  }
  
  
  
  1.1                  logging-log4j/src/java/org/apache/log4j/db/ConnectionSource.java
  
  Index: ConnectionSource.java
  ===================================================================
  /*
   * Copyright 1999,2004 The Apache Software Foundation.
   * 
   * Licensed 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.db;
  
  import org.apache.log4j.spi.ErrorHandler;
  import org.apache.log4j.spi.OptionHandler;
  
  import java.sql.Connection;
  import java.sql.SQLException;
  
  
  /**
   *  The <id>ConnectionSource</id> interface provides a pluggable means of
   *  transparently obtaining JDBC {@link java.sql.Connection}s for log4j classes
   *  that require the use of a {@link java.sql.Connection}.
   *
   *  @author <a href="mailto:rdecampo@twcny.rr.com">Ray DeCampo</a>
   */
  public interface ConnectionSource extends OptionHandler {
  
    final int UNKNOWN_DIALECT = 0;
    final int POSTGRES_DIALECT = 1;
    final int MYSQL_DIALECT = 2;
  
    
    /**
     *  Obtain a {@link java.sql.Connection} for use.  The client is
     *  responsible for closing the {@link java.sql.Connection} when it is no
     *  longer required.
     *
     *  @throws SQLException  if a {@link java.sql.Connection} could not be
     *                        obtained
     */
    Connection getConnection() throws SQLException;
  
    /**
     *  Set the error handler.
     *
     *  @param errorHandler  the new error handler
     */
    void setErrorHandler(ErrorHandler errorHandler);
    
    /**
     * 
     * Get the SQL dialect that should be used for this connection.
     *
     */
    int getSQLDialect();
  }
  
  
  
  1.1                  logging-log4j/src/java/org/apache/log4j/db/DBAppender.java
  
  Index: DBAppender.java
  ===================================================================
  package org.apache.log4j.db;
  
  import java.sql.Connection;
  import java.sql.PreparedStatement;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.sql.Statement;
  import java.util.Iterator;
  import java.util.Map;
  import java.util.Set;
  
  import org.apache.log4j.AppenderSkeleton;
  import org.apache.log4j.db.dialect.MySQLDialect;
  import org.apache.log4j.db.dialect.PostgreSQLDialect;
  import org.apache.log4j.db.dialect.SQLDialect;
  import org.apache.log4j.helpers.LogLog;
  import org.apache.log4j.spi.LoggingEvent;
  
  
  /**
   *
   * @author Ceki G&uuml;lc&uuml;
   *
   */
  public class DBAppender
         extends AppenderSkeleton {
    ConnectionSource connectionSource;
    SQLDialect sqlDialect;
  
    public void activateOptions() {
      if (connectionSource != null) {
        connectionSource.activateOptions();
      } else {
        throw new IllegalStateException("DBAppender cannot function without a connection source");
      }
  
      switch (connectionSource.getSQLDialect()) {
      case ConnectionSource.POSTGRES_DIALECT :
        sqlDialect = new PostgreSQLDialect();
        break;
      case ConnectionSource.MYSQL_DIALECT :
        sqlDialect = new MySQLDialect();
        break;
      }
    }
  
  
    /**
     * @return Returns the connectionSource.
     */
    public ConnectionSource getConnectionSource() {
      return connectionSource;
    }
  
  
    /**
     * @param connectionSource The connectionSource to set.
     */
    public void setConnectionSource(ConnectionSource connectionSource) {
      this.connectionSource = connectionSource;
    }
  
  
    protected void append(LoggingEvent event) {
      try {
        Connection connection = connectionSource.getConnection();
        connection.setAutoCommit(false);
  
  //      sequence_number BIGINT NOT NULL,
  //      timestamp         BIGINT NOT NULL,
  //      rendered_message  TEXT NOT NULL,
  //      logger_name       VARCHAR(254) NOT NULL,
  //      ndc               TEXT,
  //      thread_name       VARCHAR(254),
  //      id                INT NOT NULL AUTO_INCREMENT PRIMARY KEY
        StringBuffer sql = new StringBuffer();
        sql.append("INSERT INTO logging_event (");
        sql.append("sequence_number, timestamp, rendered_message, ");
        sql.append("logger_name, ndc, thread_name) ");
        sql.append(" VALUES (?, ?, ? ,?, ?, ?)");
  
        PreparedStatement insertStatement = connection.prepareStatement(sql.toString());
        insertStatement.setLong(1, event.getSequenceNumber());
        insertStatement.setLong(2, event.getTimeStamp());
        insertStatement.setString(3, event.getRenderedMessage());
        insertStatement.setString(4, event.getLoggerName());
        insertStatement.setString(5, event.getNDC());
        insertStatement.setString(6, event.getThreadName());
  
        int updateCount = insertStatement.executeUpdate();
  
        if (updateCount != 1) {
          LogLog.warn("Failed to insert loggingEvent");
        }
  
        Statement idStatement = connection.createStatement();
        idStatement.setMaxRows(1);
  
        ResultSet rs = idStatement.executeQuery(sqlDialect.getSelectInsertId());
        rs.first();
  
        int eventId = rs.getInt(1);
        LogLog.info("inserted id is " + eventId);
  
  //      event_id        INT NOT NULL,
  //      mapped_key        VARCHAR(254) NOT NULL,
  //      mapped_value      VARCHAR(254),
        Set mdcKeys = event.getMDCKeySet();
  
        if (mdcKeys.size() > 0) {
          String insertMDCSQL = "INSERT INTO mdc (event_id, mapped_key, mapped_value) VALUES (?, ?, ?)";
          PreparedStatement insertMDCStatement = connection.prepareStatement(insertMDCSQL);
  
          for (Iterator i = mdcKeys.iterator(); i.hasNext();) {
            String key = (String)i.next();
            String value = (String)event.getMDC(key);
            LogLog.debug("id " + eventId + ", key " + key + ", value " + value);
            insertMDCStatement.setInt(1, eventId);
            insertMDCStatement.setString(2, key);
            insertMDCStatement.setString(3, value);
            insertMDCStatement.addBatch();
          }
          insertMDCStatement.executeBatch();
        }
  
        connection.commit();
      } catch (SQLException sqle) {
        LogLog.error("problem appending event", sqle);
      }
    }
  
  
    public void close() {
      // TODO Auto-generated method st  
    }
  
  
    /*
     * The DBAppender does not require a layout.
     */
    public boolean requiresLayout() {
      return false;
    }
  }
  
  
  
  1.1                  logging-log4j/src/java/org/apache/log4j/db/JNDIConnectionSource.java
  
  Index: JNDIConnectionSource.java
  ===================================================================
  /*
   * Copyright 1999,2004 The Apache Software Foundation.
   *
   * Licensed 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.db;
  
  import java.sql.Connection;
  import java.sql.SQLException;
  
  import javax.naming.Context;
  import javax.naming.InitialContext;
  import javax.naming.NamingException;
  import javax.rmi.PortableRemoteObject;
  import javax.sql.DataSource;
  
  /**
   *  The <id>JNDIConnectionSource</id> is an implementation of
   *  {@link ConnectionSource} that obtains a {@link javax.sql.DataSource} from a
   *  JNDI provider and uses it to obtain a {@link java.sql.Connection}.  It is
   *  primarily designed to be used inside of J2EE application servers or
   *  application server clients, assuming the application server supports remote
   *  access of {@link javax.sql.DataSource}s.  In this way one can take
   *  advantage of  connection pooling and whatever other goodies the application
   *  server provides.
   *  <p>
   *  Sample configuration:<br>
   *  <pre>
   *    &lt;connectionSource class="org.apache.log4j.jdbc.JNDIConnectionSource"&gt;
   *        &lt;param name="jndiLocation" value="jdbc/MySQLDS" /&gt;
   *    &lt;/connectionSource&gt;
   *  </pre>
   *  <p>
   *  Sample configuration (with username and password):<br>
   *  <pre>
   *    &lt;connectionSource class="org.apache.log4j.jdbc.JNDIConnectionSource"&gt;
   *        &lt;param name="jndiLocation" value="jdbc/MySQLDS" /&gt;
   *        &lt;param name="username" value="myUser" /&gt;
   *        &lt;param name="password" value="myPassword" /&gt;
   *    &lt;/connectionSource&gt;
   *  </pre>
   *  <p>
   *  Note that this class will obtain an {@link javax.naming.InitialContext}
   *  using the no-argument constructor.  This will usually work when executing
   *  within a J2EE environment.  When outside the J2EE environment, make sure
   *  that you provide a jndi.properties file as described by your JNDI
   *  provider's documentation.
   *
   *  @author <a href="mailto:rdecampo@twcny.rr.com">Ray DeCampo</a>
   */
  public class JNDIConnectionSource
         extends ConnectionSourceSkeleton {
    private String jndiLocation = null;
    private DataSource dataSource = null;
  
    /**
     * @see org.apache.log4j.db.ConnectionSource#getConnection()
     */
    public Connection getConnection()
           throws SQLException {
      Connection conn = null;
      try {
  
        if(dataSource == null) {
          dataSource = lookupDataSource();
        }
        if (user == null) {
          conn = dataSource.getConnection();
        } else {
          conn = dataSource.getConnection(user, password);
        }
      } catch (final NamingException ne) {
        if (errorHandler != null) {
          errorHandler.error(ne.getMessage(), ne, 0);
        }
  
        throw new SQLException("NamingException while looking up DataSource: " + ne.getMessage());
      } catch (final ClassCastException cce) {
        if (errorHandler != null) {
          errorHandler.error(cce.getMessage(), cce, 0);
        }
  
        throw new SQLException("ClassCastException while looking up DataSource: " + cce.getMessage());
      }
  
      return conn;
    }
  
  
    /**
     * @see org.apache.log4j.spi.OptionHandler#activateOptions()
     */
    public void activateOptions() {
      if (jndiLocation == null && errorHandler != null) {
        errorHandler.error("No JNDI location specified for JNDIConnectionSource.");
      }
    }
  
  
    /**
     * Returns the jndiLocation.
     * @return String
     */
    public String getJndiLocation() {
      return jndiLocation;
    }
  
  
    /**
     * Sets the jndiLocation.
     * @param jndiLocation The jndiLocation to set
     */
    public void setJndiLocation(String jndiLocation) {
      this.jndiLocation = jndiLocation;
    }
  
  
    /**
     * Sets the password.
     * @param password The password to set
     */
    public void setPassword(String password) {
      this.password = password;
    }
  
  
    public int getSQLDialect() {
      return 0;
    }
    
    private DataSource lookupDataSource()
           throws NamingException, SQLException {
      DataSource ds;
  
      Context ctx = new InitialContext();
      Object obj = ctx.lookup(jndiLocation);
      ds = (DataSource)PortableRemoteObject.narrow(obj, DataSource.class);
  
      if (ds == null) {
        throw new SQLException("Failed to obtain data source from JNDI location " + jndiLocation);
      } else {
        return ds;
      }
    }
  }
  
  
  
  1.1                  logging-log4j/src/java/org/apache/log4j/db/ConnectionSourceSkeleton.java
  
  Index: ConnectionSourceSkeleton.java
  ===================================================================
  package org.apache.log4j.db;
  
  import org.apache.log4j.spi.ErrorHandler;
  
  
  /**
   * @author Ceki G&uuml;lc&uuml;
   */
  abstract public class ConnectionSourceSkeleton
         implements ConnectionSource {
    protected String user = null;
    protected String password = null;
    protected ErrorHandler errorHandler = null;
  
    /**
     * Get teh errorHandler for this connection source
     */
    public ErrorHandler getErrorHandler() {
      return errorHandler;
    }
  
    /**
     * Sets the error handler.
     * @param errorHandler  the error handler to set
     */
    public void setErrorHandler(ErrorHandler errorHandler) {
      this.errorHandler = errorHandler;
    }
  
  
  
    /**
     * Get the password for this connection source.
     */
    public String getPassword() {
      return password;
    }
  
  
    /**
     * Sets the password.
     * @param password The password to set
     */
    public void setPassword(String password) {
      this.password = password;
    }
  
    /**
     * Get the user for this connection source.
     */
    public String getUser() {
      return user;
    }
  
    /**
     * Sets the username.
     * @param username The username to set
     */
    public void setUser(String username) {
      this.user = username;
    }
  
  }
  
  
  
  1.1                  logging-log4j/src/java/org/apache/log4j/db/dialect/postgresql.sql
  
  Index: postgresql.sql
  ===================================================================
  
  DROP TABLE    mdc;
  DROP TABLE    logging_event;
  DROP SEQUENCE logging_event_id_seq;
  
  CREATE SEQUENCE logging_event_id_seq MINVALUE 1 START 1;
  
  
  CREATE TABLE logging_event 
    (
      sequence_number   BIGINT NOT NULL,
      timestamp         BIGINT NOT NULL,
      rendered_message  TEXT NOT NULL,
      logger_name       VARCHAR(254) NOT NULL,
      ndc               TEXT,
      thread_name       VARCHAR(254),
      id                INT DEFAULT nextval('logging_event_id_seq') PRIMARY KEY
    );
  
  CREATE TABLE mdc
    (
      event_id	      INT NOT NULL,
      mapped_key        VARCHAR(254) NOT NULL,
      mapped_value      VARCHAR(254),
      PRIMARY KEY(event_id, mapped_key),
      FOREIGN KEY (event_id) REFERENCES logging_event(id)
    );
  
  
  
  
  1.1                  logging-log4j/src/java/org/apache/log4j/db/dialect/MySQLDialect.java
  
  Index: MySQLDialect.java
  ===================================================================
  package org.apache.log4j.db.dialect;
  
  /**
   * 
   * 
   * @author Ceki
   *
   */
  public class MySQLDialect implements SQLDialect {
    public static final String SELECT_LAST_INSERT_ID = "SELECT LAST_INSERT_ID()";
    
    public String getSelectInsertId() {
      return SELECT_LAST_INSERT_ID;
    }
  }
  
  
  
  1.1                  logging-log4j/src/java/org/apache/log4j/db/dialect/mysql.sql
  
  Index: mysql.sql
  ===================================================================
  
  BEGIN;
  DROP TABLE IF EXISTS loggging_event;
  DROP TABLE IF EXISTS mdc;
  
  COMMIT;
  
  
  BEGIN;
  CREATE TABLE logging_event 
    (
      sequence_number BIGINT NOT NULL,
      timestamp         BIGINT NOT NULL,
      rendered_message  TEXT NOT NULL,
      logger_name       VARCHAR(254) NOT NULL,
      ndc               TEXT,
      thread_name       VARCHAR(254),
      id                INT NOT NULL AUTO_INCREMENT PRIMARY KEY
    );
  COMMIT;
  
  BEGIN;
  CREATE TABLE mdc
    (
      event_id	      INT NOT NULL,
      mapped_key        VARCHAR(254) NOT NULL,
      mapped_value      VARCHAR(254),
      PRIMARY KEY(event_id, mapped_key),
      FOREIGN KEY (event_id) REFERENCES logging_event(id)
    );
  COMMIT;
  
  
  
  1.1                  logging-log4j/src/java/org/apache/log4j/db/dialect/oracle.sql
  
  Index: oracle.sql
  ===================================================================
  
  CREATE SEQUENCE event_id_seq;
  
  CREATE OR REPLACE TRIGGER event_id_seq_trig
    BEFORE INSERT ON logging_event
    FOR EACH ROW  
    BEGIN  
      SELECT logging_event_seq.NEXTVAL 
      INTO   :NEW.id 
      FROM   DUAL;  
    END event_id_seq_trig;
  
  
  1.1                  logging-log4j/src/java/org/apache/log4j/db/dialect/SQLDialect.java
  
  Index: SQLDialect.java
  ===================================================================
  package org.apache.log4j.db.dialect;
  
  
  /**
   * @author ceki
   *
   */
  public interface SQLDialect {
    
    public String getSelectInsertId();
    
  }
  
  
  
  1.1                  logging-log4j/src/java/org/apache/log4j/db/dialect/PostgreSQLDialect.java
  
  Index: PostgreSQLDialect.java
  ===================================================================
  /*
   * Created on May 4, 2004
   *
   * To change the template for this generated file go to
   * Window>Preferences>Java>Code Generation>Code and Comments
   */
  package org.apache.log4j.db.dialect;
  
  
  /**
   * @author ceki
   *
   * To change the template for this generated type comment go to
   * Window>Preferences>Java>Code Generation>Code and Comments
   */
  public class PostgreSQLDialect
         implements SQLDialect {
    public static final String SELECT_CURRVAL = "SELECT currval('logging_event_id_seq')";
  
    public String getSelectInsertId() {
      return SELECT_CURRVAL;
    }
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org