svn commit: r598 - in logback/trunk/logback-classic/src: main/java/ch/qos/logback/classic/db main/java/ch/qos/logback/classic/db/dialect test/input/db test/java/ch/qos/logback/classic/db

Author: seb Date: Fri Sep 22 17:12:36 2006 New Revision: 598 Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/ConnectionSource.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/ConnectionSourceBase.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DBAppender.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DBHelper.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DataSourceConnectionSource.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DriverManagerConnectionSource.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/JNDIConnectionSource.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/DBUtil.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/HSQLDBDialect.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/MsSQLDialect.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/MySQLDialect.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/OracleDialect.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/PostgreSQLDialect.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/SQLDialect.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/db2.sql logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/db2l.sql logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/hsqldb.sql logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/mssql.sql logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/mysql.sql logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/oracle.sql logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/postgresql.sql logback/trunk/logback-classic/src/test/input/db/ logback/trunk/logback-classic/src/test/input/db/dbAppenderUsingConnectionSource.xml logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/db/ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderTest.java Log: Work in progress: - added DBAppender and related classes. - added a configuration example - added a empty-for-now test case. A simple logback to mysql test worked with this first version Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/ConnectionSource.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/ConnectionSource.java Fri Sep 22 17:12:36 2006 @@ -0,0 +1,66 @@ +/* + * 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 ch.qos.logback.classic.db; +import java.sql.Connection; +import java.sql.SQLException; + +import ch.qos.logback.core.spi.LifeCycle; + + +/** + * 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 LifeCycle { + + final int UNKNOWN_DIALECT = 0; + final int POSTGRES_DIALECT = 1; + final int MYSQL_DIALECT = 2; + final int ORACLE_DIALECT = 3; + final int MSSQL_DIALECT = 4; + final int HSQL_DIALECT = 5; + /** + * 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; + + /** + * Get the SQL dialect that should be used for this connection. Note that the + * dialect is not needed if the JDBC driver supports the getGeneratedKeys + * method. + */ + int getSQLDialectCode(); + + /** + * If the connection supports the JDBC 3.0 getGeneratedKeys method, then + * we do not need any specific dialect support. + */ + boolean supportsGetGeneratedKeys(); + + /** + * If the connection does not support batch updates, we will avoid using them. + */ + public boolean supportsBatchUpdates(); +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/ConnectionSourceBase.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/ConnectionSourceBase.java Fri Sep 22 17:12:36 2006 @@ -0,0 +1,126 @@ +/* + * 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 ch.qos.logback.classic.db; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +import ch.qos.logback.classic.db.dialect.DBUtil; +import ch.qos.logback.core.spi.ContextAwareBase; + + +/** + * @author Ceki Gülcü + */ +public abstract class ConnectionSourceBase extends ContextAwareBase implements ConnectionSource { + + private boolean started; + + private String user = null; + private String password = null; + + // initially we have an unkonw dialect + private int dialectCode = UNKNOWN_DIALECT; + private boolean supportsGetGeneratedKeys = false; + private boolean supportsBatchUpdates = false; + + + /** + * Learn relevant information about this connection source. + * + */ + public void discoverConnnectionProperties() { + try { + Connection connection = getConnection(); + if (connection == null) { + addWarn("Could not get a conneciton"); + return; + } + DatabaseMetaData meta = connection.getMetaData(); + DBUtil util = new DBUtil(); + util.setContext(getContext()); + supportsGetGeneratedKeys = util.supportsGetGeneratedKeys(meta); + supportsBatchUpdates = util.supportsBatchUpdates(meta); + dialectCode = DBUtil.discoverSQLDialect(meta); + } catch (SQLException se) { + addWarn("Could not discover the dialect to use.", se); + } + } + + /** + * Does this connection support the JDBC Connection.getGeneratedKeys method? + */ + public final boolean supportsGetGeneratedKeys() { + return supportsGetGeneratedKeys; + } + + public final int getSQLDialectCode() { + return dialectCode; + } + + /** + * Get the password for this connection source. + */ + public final String getPassword() { + return password; + } + + /** + * Sets the password. + * @param password The password to set + */ + public final void setPassword(final String password) { + this.password = password; + } + + /** + * Get the user for this connection source. + */ + public final String getUser() { + return user; + } + + /** + * Sets the username. + * @param username The username to set + */ + public final void setUser(final String username) { + this.user = username; + } + + /** + * Does this connection support batch updates? + */ + public final boolean supportsBatchUpdates() { + return supportsBatchUpdates; + } + + public boolean isStarted() { + return started; + } + + public void start() { + started = true; + } + + public void stop() { + started = false; + } + + +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DBAppender.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DBAppender.java Fri Sep 22 17:12:36 2006 @@ -0,0 +1,394 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ + +package ch.qos.logback.classic.db; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import ch.qos.logback.classic.db.dialect.DBUtil; +import ch.qos.logback.classic.db.dialect.SQLDialect; +import ch.qos.logback.classic.spi.CallerData; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.classic.spi.ThrowableInformation; +import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.Layout; + +/** + * The DBAppender inserts loggin events into three database tables in a format + * independent of the Java programming language. The three tables that + * DBAppender inserts to must exists before DBAppender can be used. These tables + * may be created with the help of SQL scripts found in the + * <em>src/main/java/ch/qos/logback/classic/db/dialect</em> directory. There + * is a specific script for each of the most popular database systems. If the + * script for your particular type of database system is missing, it should be + * quite easy to write one, taking example on the already existing scripts. If + * you send them to us, we will gladly include missing scripts in future + * releases. + * + * <p> + * If the JDBC driver you are using supports the + * {@link java.sql.Statement#getGeneratedKeys}method introduced in JDBC 3.0 + * specification, then you are all set. Otherwise, there must be an + * {@link SQLDialect}appropriate for your database system. Currently, we have + * dialects for PostgreSQL, MySQL, Oracle and MsSQL. As mentioed previously, an + * SQLDialect is required only if the JDBC driver for your database system does + * not support the {@link java.sql.Statement#getGeneratedKeys getGeneratedKeys} + * method. + * </p> + * + * <table border="1" cellpadding="4"> + * <tr> + * <th>RDBMS</th> + * <th>supports <br/><code>getGeneratedKeys()</code> method</th> + * <th>specific <br/>SQLDialect support</th> + * <tr> + * <tr> + * <td>PostgreSQL</td> + * <td align="center">NO</td> + * <td>present and used</td> + * <tr> + * <tr> + * <td>MySQL</td> + * <td align="center">YES</td> + * <td>present, but not actually needed or used</td> + * <tr> + * <tr> + * <td>Oracle</td> + * <td align="center">YES</td> + * <td>present, but not actually needed or used</td> + * <tr> + * <tr> + * <td>DB2</td> + * <td align="center">YES</td> + * <td>not present, and not needed or used</td> + * <tr> + * <tr> + * <td>MsSQL</td> + * <td align="center">YES</td> + * <td>not present, and not needed or used</td> + * <tr> + * <tr> + * <td>HSQL</td> + * <td align="center">NO</td> + * <td>present and used</td> + * <tr> + * + * </table> + * <p> + * <b>Performance: </b> Experiments show that writing a single event into the + * database takes approximately 50 milliseconds, on a "standard" PC. If pooled + * connections are used, this figure drops to under 10 milliseconds. Note that + * most JDBC drivers already ship with connection pooling support. + * </p> + * + * + * + * <p> + * <b>Configuration </b> DBAppender can be configured programmatically, or using + * {@link ch.qos.logback.classic.joran.JoranConfigurator JoranConfigurator}. + * Example scripts can be found in the <em>tests/input/db</em> directory. + * + * @author Ceki Gülcü + * @author Ray DeCampo + * @author Sébastien Pennec + */ +public class DBAppender extends AppenderBase { + static final String insertPropertiesSQL = "INSERT INTO logging_event_property (event_id, mapped_key, mapped_value) VALUES (?, ?, ?)"; + static final String insertExceptionSQL = "INSERT INTO logging_event_exception (event_id, i, trace_line) VALUES (?, ?, ?)"; + static final String insertSQL; + private static final Method GET_GENERATED_KEYS_METHOD; + + static { + StringBuffer sql = new StringBuffer(); + sql.append("INSERT INTO logging_event ("); + sql.append("timestmp, "); + sql.append("formatted_message, "); + sql.append("logger_name, "); + sql.append("level_string, "); + sql.append("thread_name, "); + sql.append("reference_flag, "); + sql.append("caller_filename, "); + sql.append("caller_class, "); + sql.append("caller_method, "); + sql.append("caller_line) "); + sql.append(" VALUES (?, ?, ? ,?, ?, ?, ?, ?, ?,?)"); + insertSQL = sql.toString(); + // + // PreparedStatement.getGeneratedKeys added in JDK 1.4 + // + Method getGeneratedKeysMethod; + try { + getGeneratedKeysMethod = PreparedStatement.class.getMethod( + "getGeneratedKeys", (Class[]) null); + } catch (Exception ex) { + getGeneratedKeysMethod = null; + } + GET_GENERATED_KEYS_METHOD = getGeneratedKeysMethod; + } + + ConnectionSource connectionSource; + boolean cnxSupportsGetGeneratedKeys = false; + boolean cnxSupportsBatchUpdates = false; + SQLDialect sqlDialect; + boolean locationInfo = false; + + public DBAppender() { + } + + @Override + public void start() { + + if (connectionSource == null) { + throw new IllegalStateException( + "DBAppender cannot function without a connection source"); + } + + sqlDialect = DBUtil + .getDialectFromCode(connectionSource.getSQLDialectCode()); + if (GET_GENERATED_KEYS_METHOD != null) { + cnxSupportsGetGeneratedKeys = connectionSource.supportsGetGeneratedKeys(); + } else { + cnxSupportsGetGeneratedKeys = false; + } + cnxSupportsBatchUpdates = connectionSource.supportsBatchUpdates(); + if (!cnxSupportsGetGeneratedKeys && (sqlDialect == null)) { + throw new IllegalStateException( + "DBAppender cannot function if the JDBC driver does not support getGeneratedKeys method *and* without a specific SQL dialect"); + } + + // all nice and dandy on the eastern front + super.start(); + } + + /** + * @return Returns the connectionSource. + */ + public ConnectionSource getConnectionSource() { + return connectionSource; + } + + /** + * @param connectionSource + * The connectionSource to set. + */ + public void setConnectionSource(ConnectionSource connectionSource) { + this.connectionSource = connectionSource; + } + + @Override + protected void append(Object eventObject) { + LoggingEvent event = (LoggingEvent) eventObject; + Connection connection = null; + try { + connection = connectionSource.getConnection(); + connection.setAutoCommit(false); + + PreparedStatement insertStatement = connection + .prepareStatement(insertSQL); + + addLoggingEvent(insertStatement, event); + // This is very expensive... should we do it every time? + addCallerData(insertStatement, event.getCallerData()); + + int updateCount = insertStatement.executeUpdate(); + if (updateCount != 1) { + addWarn("Failed to insert loggingEvent"); + } + + int eventId = getEventId(insertStatement, connection); + + // we no longer need the insertStatement + if (insertStatement != null) { + insertStatement.close(); + insertStatement = null; + } + + Map<String, String> mergedMap = mergePropertyMaps(event); + insertProperties(mergedMap, connection, eventId); + + insertThrowable(event.getThrowableInformation(), connection, eventId); + + connection.commit(); + } catch (Throwable sqle) { + addError("problem appending event", sqle); + } finally { + DBHelper.closeConnection(connection); + } + } + + void addLoggingEvent(PreparedStatement stmt, LoggingEvent event) + throws SQLException { + stmt.setLong(1, event.getTimeStamp()); + stmt.setString(2, event.getFormattedMessage()); + stmt.setString(3, event.getLoggerRemoteView().getName()); + stmt.setString(4, event.getLevel().toString()); + stmt.setString(5, event.getThreadName()); + stmt.setShort(6, DBHelper.computeReferenceMask(event)); + } + + void addCallerData(PreparedStatement stmt, CallerData[] callerDataArray) + throws SQLException { + CallerData callerData = callerDataArray[0]; + if (callerData != null) { + stmt.setString(7, callerData.getFileName()); + stmt.setString(8, callerData.getClassName()); + stmt.setString(9, callerData.getMethodName()); + stmt.setString(10, Integer.toString(callerData.getLineNumber())); + } + } + + int getEventId(PreparedStatement insertStatement, Connection connection) + throws SQLException, InvocationTargetException { + ResultSet rs = null; + Statement idStatement = null; + boolean gotGeneratedKeys = false; + if (cnxSupportsGetGeneratedKeys) { + try { + rs = (ResultSet) GET_GENERATED_KEYS_METHOD.invoke(insertStatement, + (Object[]) null); + gotGeneratedKeys = true; + } catch (InvocationTargetException ex) { + Throwable target = ex.getTargetException(); + if (target instanceof SQLException) { + throw (SQLException) target; + } + throw ex; + } catch (IllegalAccessException ex) { + addWarn( + "IllegalAccessException invoking PreparedStatement.getGeneratedKeys", + ex); + } + } + + if (!gotGeneratedKeys) { + insertStatement.close(); + insertStatement = null; + + idStatement = connection.createStatement(); + idStatement.setMaxRows(1); + rs = idStatement.executeQuery(sqlDialect.getSelectInsertId()); + } + + // A ResultSet cursor is initially positioned before the first row; + // the + // first call to the method next makes the first row the current row + rs.next(); + int eventId = rs.getInt(1); + + rs.close(); + + if (idStatement != null) { + idStatement.close(); + idStatement = null; + } + + return eventId; + } + + Map<String, String> mergePropertyMaps(LoggingEvent event) { + Map<String, String> mergedMap = new HashMap<String, String>(); + // we add the context properties first, then the event properties, since + // we consider that event-specific properties should have priority over + // context-wide + // properties. + Map<String, String> loggerContextMap = event.getLoggerRemoteView() + .getLoggerContextView().getPropertyMap(); + Map<String, String> mdcMap = event.getMDCPropertyMap(); + if (loggerContextMap != null) { + mergedMap.putAll(loggerContextMap); + } + if (mdcMap != null) { + mergedMap.putAll(mdcMap); + } + + return mergedMap; + } + + void insertProperties(Map<String, String> mergedMap, Connection connection, + int eventId) throws SQLException { + Set propertiesKeys = mergedMap.keySet(); + if (propertiesKeys.size() > 0) { + PreparedStatement insertPropertiesStatement = connection + .prepareStatement(insertPropertiesSQL); + + for (Iterator i = propertiesKeys.iterator(); i.hasNext();) { + String key = (String) i.next(); + String value = (String) mergedMap.get(key); + + insertPropertiesStatement.setInt(1, eventId); + insertPropertiesStatement.setString(2, key); + insertPropertiesStatement.setString(3, value); + + if (cnxSupportsBatchUpdates) { + insertPropertiesStatement.addBatch(); + } else { + insertPropertiesStatement.execute(); + } + } + + if (cnxSupportsBatchUpdates) { + insertPropertiesStatement.executeBatch(); + } + + insertPropertiesStatement.close(); + insertPropertiesStatement = null; + } + } + + void insertThrowable(ThrowableInformation ti, Connection connection, + int eventId) throws SQLException { + String[] strRep = null; + if (ti != null) { + strRep = ti.getThrowableStrRep(); + + PreparedStatement insertExceptionStatement = connection + .prepareStatement(insertExceptionSQL); + + for (short i = 0; i < strRep.length; i++) { + insertExceptionStatement.setInt(1, eventId); + insertExceptionStatement.setShort(2, i); + insertExceptionStatement.setString(3, strRep[i]); + if (cnxSupportsBatchUpdates) { + insertExceptionStatement.addBatch(); + } else { + insertExceptionStatement.execute(); + } + } + if (cnxSupportsBatchUpdates) { + insertExceptionStatement.executeBatch(); + } + insertExceptionStatement.close(); + insertExceptionStatement = null; + } + } + + @Override + public void stop() { + super.stop(); + } + + public Layout getLayout() { + return null; + } + + public void setLayout(Layout layout) { + } + +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DBHelper.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DBHelper.java Fri Sep 22 17:12:36 2006 @@ -0,0 +1,72 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ + +package ch.qos.logback.classic.db; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import ch.qos.logback.classic.spi.LoggingEvent; + +/** + * @author Ceki Gülcü + * + */ +public class DBHelper { + + public static short PROPERTIES_EXIST = 0x01; + public static short EXCEPTION_EXISTS = 0x02; + + public static short computeReferenceMask(LoggingEvent event) { + short mask = 0; + + int mdcPropSize = 0; + if (event.getMDCPropertyMap() != null) { + mdcPropSize = event.getMDCPropertyMap().keySet().size(); + } + int contextPropSize = 0; + if (event.getLoggerRemoteView().getLoggerContextView().getPropertyMap() != null) { + contextPropSize = event.getLoggerRemoteView().getLoggerContextView() + .getPropertyMap().size(); + } + + if (mdcPropSize > 0 || contextPropSize > 0) { + mask = PROPERTIES_EXIST; + } + if (event.getThrowableInformation() != null) { + String[] strRep = event.getThrowableInformation().getThrowableStrRep(); + if (strRep != null) { + mask |= EXCEPTION_EXISTS; + } + } + return mask; + } + + static public void closeConnection(Connection connection) { + if (connection != null) { + try { + connection.close(); + } catch (SQLException sqle) { + // static utility classes should not log without an explicit repository + // reference + } + } + } + + public static void closeStatement(Statement statement) { + if (statement != null) { + try { + statement.close(); + } catch (SQLException sqle) { + } + } + } +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DataSourceConnectionSource.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DataSourceConnectionSource.java Fri Sep 22 17:12:36 2006 @@ -0,0 +1,78 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ + +package ch.qos.logback.classic.db; + + +import java.sql.Connection; +import java.sql.SQLException; + +import javax.sql.DataSource; + + +/** + * The DataSourceConnectionSource is an implementation of {@link ConnectionSource} + * that obtains the Connection in the recommended JDBC manner based on + * a {@link javax.sql.DataSource DataSource}. + * <p> + * + * @author Ray DeCampo + * @author Ceki Gülcü + */ +public class DataSourceConnectionSource extends ConnectionSourceBase { + + private DataSource dataSource; + + @Override + public void start() { + //LogLog.debug("**********DataSourceConnectionSource.activateOptions called"); + if (dataSource == null) { + addWarn("WARNING: No data source specified"); + } else { + Connection connection = null; + try { + connection = getConnection(); + } catch(SQLException se) { + addWarn("Could not get a connection to discover the dialect to use.", se); + } + if(connection != null) { + discoverConnnectionProperties(); + } + if(!supportsGetGeneratedKeys() && getSQLDialectCode() == ConnectionSource.UNKNOWN_DIALECT) { + addWarn("Connection does not support GetGeneratedKey method and could not discover the dialect."); + } + } + super.start(); + } + + /** + * @see org.apache.log4j.db.ConnectionSource#getConnection() + */ + public Connection getConnection() throws SQLException { + if (dataSource == null) { + addError("WARNING: No data source specified"); + return null; + } + + if (getUser() == null) { + return dataSource.getConnection(); + } else { + return dataSource.getConnection(getUser(), getPassword()); + } + } + + public DataSource getDataSource() { + return dataSource; + } + + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DriverManagerConnectionSource.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/DriverManagerConnectionSource.java Fri Sep 22 17:12:36 2006 @@ -0,0 +1,129 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ + +package ch.qos.logback.classic.db; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +/** + * The DriverManagerConnectionSource 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> + * <connectionSource class="org.apache.log4j.jdbc.DriverManagerConnectionSource"> + * <param name="driver" value="com.mysql.jdbc.Driver" /> + * <param name="url" value="jdbc:mysql://localhost:3306/mydb" /> + * <param name="username" value="myUser" /> + * <param name="password" value="myPassword" /> + * </connectionSource> + * </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> + * <connectionSource class="org.apache.log4j.jdbc.DriverManagerConnectionSource"> + * <param name="driver" value="org.apache.commons.dbcp.PoolingDriver" /> + * <param name="url" value="jdbc:apache:commons:dbcp:/myPoolingDriver" /> + * </connectionSource> + * </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 DriverManagerConnectionSource extends ConnectionSourceBase { + private String driverClass = null; + private String url = null; + + public void start() { + try { + if (driverClass != null) { + Class.forName(driverClass); + discoverConnnectionProperties(); + } else { + addError("WARNING: No JDBC driver specified for log4j DriverManagerConnectionSource."); + } + } catch (final ClassNotFoundException cnfe) { + addError("Could not load JDBC driver class: " + driverClass, cnfe); + } + } + + /** + * @see org.apache.log4j.db.ConnectionSource#getConnection() + */ + public Connection getConnection() throws SQLException { + if (getUser() == null) { + return DriverManager.getConnection(url); + } else { + return DriverManager.getConnection(url, getUser(), getPassword()); + } + } + + /** + * 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 driverClass + * The driver class to set + */ + public void setDriverClass(String driverClass) { + this.driverClass = driverClass; + } +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/JNDIConnectionSource.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/JNDIConnectionSource.java Fri Sep 22 17:12:36 2006 @@ -0,0 +1,140 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ +package ch.qos.logback.classic.db; + +import java.sql.Connection; +import java.sql.SQLException; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +// PortableRemoteObject was introduced in JDK 1.3. We won't use it. +// 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> + * <connectionSource class="org.apache.log4j.jdbc.JNDIConnectionSource"> + * <param name="jndiLocation" value="jdbc/MySQLDS" /> + * </connectionSource> + * </pre> + * + * <p> + * Sample configuration (with username and password):<br> + * + * <pre> + * <connectionSource class="org.apache.log4j.jdbc.JNDIConnectionSource"> + * <param name="jndiLocation" value="jdbc/MySQLDS" /> + * <param name="username" value="myUser" /> + * <param name="password" value="myPassword" /> + * </connectionSource> + * </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 ConnectionSourceBase { + private String jndiLocation = null; + private DataSource dataSource = null; + + /** + * @see org.apache.log4j.spi.OptionHandler#activateOptions() + */ + public void start() { + if (jndiLocation == null) { + addError("No JNDI location specified for JNDIConnectionSource."); + } + + discoverConnnectionProperties(); + + } + + /** + * @see org.apache.log4j.db.ConnectionSource#getConnection() + */ + public Connection getConnection() throws SQLException { + Connection conn = null; + try { + + if (dataSource == null) { + dataSource = lookupDataSource(); + } + if (getUser() == null) { + conn = dataSource.getConnection(); + } else { + conn = dataSource.getConnection(getUser(), getPassword()); + } + } catch (final NamingException ne) { + addError("Error while getting data source", ne); + throw new SQLException("NamingException while looking up DataSource: " + + ne.getMessage()); + } catch (final ClassCastException cce) { + addError("ClassCastException while looking up DataSource.", cce); + throw new SQLException("ClassCastException while looking up DataSource: " + + cce.getMessage()); + } + + return conn; + } + + /** + * 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; + } + + private DataSource lookupDataSource() throws NamingException, SQLException { + DataSource ds; + Context ctx = new InitialContext(); + Object obj = ctx.lookup(jndiLocation); + + // PortableRemoteObject was introduced in JDK 1.3. We won't use it. + // ds = (DataSource)PortableRemoteObject.narrow(obj, DataSource.class); + ds = (DataSource) obj; + + if (ds == null) { + throw new SQLException("Failed to obtain data source from JNDI location " + + jndiLocation); + } else { + return ds; + } + } +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/DBUtil.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/DBUtil.java Fri Sep 22 17:12:36 2006 @@ -0,0 +1,125 @@ +/* + * Copyright 1999,2006 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 ch.qos.logback.classic.db.dialect; + +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +import ch.qos.logback.classic.db.ConnectionSource; +import ch.qos.logback.core.spi.ContextAwareBase; + +/** + * + * @author Ceki Gulcu + * + */ +public class DBUtil extends ContextAwareBase { + private static final String POSTGRES_PART = "postgresql"; + private static final String MYSQL_PART = "mysql"; + private static final String ORACLE_PART = "oracle"; + // private static final String MSSQL_PART = "mssqlserver4"; + private static final String MSSQL_PART = "microsoft"; + private static final String HSQL_PART = "hsql"; + + public static int discoverSQLDialect(DatabaseMetaData meta) { + int dialectCode = 0; + + try { + + String dbName = meta.getDatabaseProductName().toLowerCase(); + + if (dbName.indexOf(POSTGRES_PART) != -1) { + return ConnectionSource.POSTGRES_DIALECT; + } else if (dbName.indexOf(MYSQL_PART) != -1) { + return ConnectionSource.MYSQL_DIALECT; + } else if (dbName.indexOf(ORACLE_PART) != -1) { + return ConnectionSource.ORACLE_DIALECT; + } else if (dbName.indexOf(MSSQL_PART) != -1) { + return ConnectionSource.MSSQL_DIALECT; + } else if (dbName.indexOf(HSQL_PART) != -1) { + return ConnectionSource.HSQL_DIALECT; + } else { + return ConnectionSource.UNKNOWN_DIALECT; + } + } catch (SQLException sqle) { + // we can't do much here + } + + return dialectCode; + } + + public static SQLDialect getDialectFromCode(int dialectCode) { + SQLDialect sqlDialect = null; + + switch (dialectCode) { + case ConnectionSource.POSTGRES_DIALECT: + sqlDialect = new PostgreSQLDialect(); + + break; + case ConnectionSource.MYSQL_DIALECT: + sqlDialect = new MySQLDialect(); + + break; + case ConnectionSource.ORACLE_DIALECT: + sqlDialect = new OracleDialect(); + + break; + case ConnectionSource.MSSQL_DIALECT: + sqlDialect = new MsSQLDialect(); + + break; + case ConnectionSource.HSQL_DIALECT: + sqlDialect = new HSQLDBDialect(); + + break; + } + return sqlDialect; + } + + /** + * This method handles cases where the + * {@link DatabaseMetaData#supportsGetGeneratedKeys} method is missing in the + * JDBC driver implementation. + */ + public boolean supportsGetGeneratedKeys(DatabaseMetaData meta) { + try { + // + // invoking JDK 1.4 method by reflection + // + return ((Boolean) DatabaseMetaData.class.getMethod( + "supportsGetGeneratedKeys", (Class[]) null).invoke(meta, + (Object[]) null)).booleanValue(); + } catch (Throwable e) { + addInfo("Could not call supportsGetGeneratedKeys method. This may be recoverable"); + return false; + } + } + + /** + * This method handles cases where the + * {@link DatabaseMetaData#supportsBatchUpdates} method is missing in the JDBC + * driver implementation. + */ + public boolean supportsBatchUpdates(DatabaseMetaData meta) { + try { + return meta.supportsBatchUpdates(); + } catch (Throwable e) { + addInfo("Missing DatabaseMetaData.supportsBatchUpdates method."); + return false; + } + } +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/HSQLDBDialect.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/HSQLDBDialect.java Fri Sep 22 17:12:36 2006 @@ -0,0 +1,23 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ +package ch.qos.logback.classic.db.dialect; + +/** + * The HSQLDB dialect. + * + * @author <a href="http://www.qos.ch/log4j/">Ceki Gülcü</a> +*/ +public class HSQLDBDialect implements SQLDialect { + public static final String SELECT_CURRVAL = "CALL IDENTITY()"; + + public String getSelectInsertId() { + return SELECT_CURRVAL; + } +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/MsSQLDialect.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/MsSQLDialect.java Fri Sep 22 17:12:36 2006 @@ -0,0 +1,27 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ + +package ch.qos.logback.classic.db.dialect; + +/** +* The MS SQL Server dialect is untested. +* +* Note that the dialect is not needed if your JDBC driver supports +* the getGeneratedKeys method introduced in JDBC 3.0 specification. +* +* @author James Stauffer +*/ +public class MsSQLDialect implements SQLDialect { + public static final String SELECT_CURRVAL = "SELECT @@identity id"; + + public String getSelectInsertId() { + return SELECT_CURRVAL; + } +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/MySQLDialect.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/MySQLDialect.java Fri Sep 22 17:12:36 2006 @@ -0,0 +1,24 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ +package ch.qos.logback.classic.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; + } +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/OracleDialect.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/OracleDialect.java Fri Sep 22 17:12:36 2006 @@ -0,0 +1,26 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ + +package ch.qos.logback.classic.db.dialect; + +/** + * The Oracle dialect. Tested successfully on Oracle9i Release 9.2.0.3.0 by + * James Stauffer. + * + * @author Ceki Gülcü + */ +public class OracleDialect implements SQLDialect { + public static final String SELECT_CURRVAL = "SELECT logging_event_id_seq.currval from dual"; + + public String getSelectInsertId() { + return SELECT_CURRVAL; + } + +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/PostgreSQLDialect.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/PostgreSQLDialect.java Fri Sep 22 17:12:36 2006 @@ -0,0 +1,28 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ + +package ch.qos.logback.classic.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; + } +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/SQLDialect.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/SQLDialect.java Fri Sep 22 17:12:36 2006 @@ -0,0 +1,20 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * + * Copyright (C) 1999-2006, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ +package ch.qos.logback.classic.db.dialect; + +/** + * @author ceki + * + */ +public interface SQLDialect { + + public String getSelectInsertId(); + +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/db2.sql ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/db2.sql Fri Sep 22 17:12:36 2006 @@ -0,0 +1,49 @@ +# This SQL script creates the required tables by org.apache.log4j.db.DBAppender and +# org.apache.log4j.db.DBReceiver. +# +# It is intended for IBM DB2 databases. +# +# WARNING WARNING WARNING WARNING +# ================================= +# This SQL script has not been tested on an actual DB2 +# instance. It may contain errors or even invalid SQL +# statements. + +DROP TABLE logging_event_property; +DROP TABLE logging_event_exception; +DROP TABLE logging_event; + +CREATE TABLE logging_event + ( + sequence_number BIGINT NOT NULL, + timestamp BIGINT NOT NULL, + rendered_message VARCHAR(4000) NOT NULL, + logger_name VARCHAR(254) NOT NULL, + level_string VARCHAR(254) NOT NULL, + ndc VARCHAR(4000), + thread_name VARCHAR(254), + reference_flag SMALLINT, + caller_filename VARCHAR(254) NOT NULL, + caller_class VARCHAR(254) NOT NULL, + caller_method VARCHAR(254) NOT NULL, + caller_line CHAR(4) NOT NULL, + event_id INTEGER GENERATED ALWAYS AS IDENTITY (START WITH 1) + ); + +CREATE TABLE logging_event_property + ( + event_id INTEGER NOT NULL, + mapped_key VARCHAR(254) NOT NULL, + mapped_value VARCHAR(1024), + PRIMARY KEY(event_id, mapped_key), + FOREIGN KEY (event_id) REFERENCES logging_event(event_id) + ); + +CREATE TABLE logging_event_exception + ( + event_id INTEGER NOT NULL, + i SMALLINT NOT NULL, + trace_line VARCHAR(254) NOT NULL, + PRIMARY KEY(event_id, i), + FOREIGN KEY (event_id) REFERENCES logging_event(event_id) + ); Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/db2l.sql ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/db2l.sql Fri Sep 22 17:12:36 2006 @@ -0,0 +1,47 @@ +# This SQL script creates the required tables by org.apache.log4j.db.DBAppender and +# org.apache.log4j.db.DBReceiver. +# +# It is intended for PostgreSQL databases. + +DROP TABLE logging_event_property; +DROP TABLE logging_event_exception; +DROP TABLE logging_event; + + +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, + level_string VARCHAR(254) NOT NULL, + ndc TEXT, + thread_name VARCHAR(254), + reference_flag SMALLINT, + caller_filename VARCHAR(254) NOT NULL, + caller_class VARCHAR(254) NOT NULL, + caller_method VARCHAR(254) NOT NULL, + caller_line CHAR(4) NOT NULL, + event_id INT IDENTITY GENERATED ALWAYS PRIMARY KEY + ); + +CREATE TABLE logging_event_property + ( + event_id INT NOT NULL, + mapped_key VARCHAR(254) NOT NULL, + mapped_value VARCHAR(1024), + PRIMARY KEY(event_id, mapped_key), + FOREIGN KEY (event_id) REFERENCES logging_event(event_id) + ); + +CREATE TABLE logging_event_exception + ( + event_id INT NOT NULL, + i SMALLINT NOT NULL, + trace_line VARCHAR(254) NOT NULL, + PRIMARY KEY(event_id, i), + FOREIGN KEY (event_id) REFERENCES logging_event(event_id) + ); Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/hsqldb.sql ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/hsqldb.sql Fri Sep 22 17:12:36 2006 @@ -0,0 +1,46 @@ +// This SQL script creates the required tables by +// org.apache.log4j.db.DBAppender and org.apache.log4j.db.DBReceiver. +// +// It is intended for HSQLDB. +// + +DROP TABLE logging_event_exception IF EXISTS; +DROP TABLE logging_event_property IF EXISTS; +DROP TABLE logging_event IF EXISTS; + + +CREATE TABLE logging_event + ( + sequence_number BIGINT NOT NULL, + timestamp BIGINT NOT NULL, + rendered_message LONGVARCHAR NOT NULL, + logger_name VARCHAR NOT NULL, + level_string VARCHAR NOT NULL, + ndc LONGVARCHAR, + thread_name VARCHAR, + reference_flag SMALLINT, + caller_filename VARCHAR, + caller_class VARCHAR, + caller_method VARCHAR, + caller_line CHAR(4), + event_id INT NOT NULL IDENTITY + ); + + +CREATE TABLE logging_event_property + ( + event_id INT NOT NULL, + mapped_key VARCHAR(254) NOT NULL, + mapped_value LONGVARCHAR, + PRIMARY KEY(event_id, mapped_key), + FOREIGN KEY (event_id) REFERENCES logging_event(event_id) + ); + +CREATE TABLE logging_event_exception + ( + event_id INT NOT NULL, + i SMALLINT NOT NULL, + trace_line VARCHAR NOT NULL, + PRIMARY KEY(event_id, i), + FOREIGN KEY (event_id) REFERENCES logging_event(event_id) + ); Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/mssql.sql ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/mssql.sql Fri Sep 22 17:12:36 2006 @@ -0,0 +1,45 @@ +-- This SQL script creates the required tables by org.apache.log4j.db.DBAppender and +-- org.apache.log4j.db.DBReceiver. +-- +-- It is intended for MS SQL Server databases. This has been tested with version 7.0. + +DROP TABLE logging_event_property +DROP TABLE logging_event_exception +DROP TABLE logging_event + +CREATE TABLE logging_event + ( + sequence_number DECIMAL(20) NOT NULL, + timestamp DECIMAL(20) NOT NULL, + rendered_message VARCHAR(4000) NOT NULL, + logger_name VARCHAR(254) NOT NULL, + level_string VARCHAR(254) NOT NULL, + ndc VARCHAR(4000), + thread_name VARCHAR(254), + reference_flag SMALLINT, + caller_filename VARCHAR(254) NOT NULL, + caller_class VARCHAR(254) NOT NULL, + caller_method VARCHAR(254) NOT NULL, + caller_line CHAR(4) NOT NULL, + event_id INT NOT NULL identity, + PRIMARY KEY(event_id) + ) + +CREATE TABLE logging_event_property + ( + event_id INT NOT NULL, + mapped_key VARCHAR(254) NOT NULL, + mapped_value VARCHAR(1024), + PRIMARY KEY(event_id, mapped_key), + FOREIGN KEY (event_id) REFERENCES logging_event(event_id) + ) + +CREATE TABLE logging_event_exception + ( + event_id INT NOT NULL, + i SMALLINT NOT NULL, + trace_line VARCHAR(254) NOT NULL, + PRIMARY KEY(event_id, i), + FOREIGN KEY (event_id) REFERENCES logging_event(event_id) + ) + Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/mysql.sql ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/mysql.sql Fri Sep 22 17:12:36 2006 @@ -0,0 +1,54 @@ +# This SQL script creates the required tables by org.apache.log4j.db.DBAppender and +# org.apache.log4j.db.DBReceiver. +# +# It is intended for MySQL databases. It has been tested on MySQL 4.1.1 with +# INNODB tables. + + +BEGIN; +DROP TABLE IF EXISTS logging_event_property; +DROP TABLE IF EXISTS logging_event_exception; +DROP TABLE IF EXISTS logging_event; +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, + level_string VARCHAR(254) NOT NULL, + ndc TEXT, + thread_name VARCHAR(254), + reference_flag SMALLINT, + caller_filename VARCHAR(254) NOT NULL, + caller_class VARCHAR(254) NOT NULL, + caller_method VARCHAR(254) NOT NULL, + caller_line CHAR(4) NOT NULL, + event_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY + ); +COMMIT; + +BEGIN; +CREATE TABLE logging_event_property + ( + event_id INT NOT NULL, + mapped_key VARCHAR(254) NOT NULL, + mapped_value TEXT, + PRIMARY KEY(event_id, mapped_key), + FOREIGN KEY (event_id) REFERENCES logging_event(event_id) + ); +COMMIT; + +BEGIN; +CREATE TABLE logging_event_exception + ( + event_id INT NOT NULL, + i SMALLINT NOT NULL, + trace_line VARCHAR(254) NOT NULL, + PRIMARY KEY(event_id, i), + FOREIGN KEY (event_id) REFERENCES logging_event(event_id) + ); +COMMIT; \ No newline at end of file Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/oracle.sql ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/oracle.sql Fri Sep 22 17:12:36 2006 @@ -0,0 +1,67 @@ +-- This SQL script creates the required tables by org.apache.log4j.db.DBAppender and +-- org.apache.log4j.db.DBReceiver. +-- +-- It is intended for Oracle databases. + +-- Tested successfully on Oracle9i Release 9.2.0.3.0 by James Stauffer + +-- The following lines are useful in cleaning any previous tables + +--drop TRIGGER logging_event_id_seq_trig; +--drop SEQUENCE logging_event_id_seq; +--drop table logging_event_property; +--drop table logging_event_exception; +--drop table logging_event; + + +CREATE SEQUENCE logging_event_id_seq MINVALUE 1 START WITH 1; + +CREATE TABLE logging_event + ( + sequence_number NUMBER(20) NOT NULL, + timestamp NUMBER(20) NOT NULL, + rendered_message VARCHAR2(4000) NOT NULL, + logger_name VARCHAR2(254) NOT NULL, + level_string VARCHAR2(254) NOT NULL, + ndc VARCHAR2(4000), + thread_name VARCHAR2(254), + reference_flag SMALLINT, + caller_filename VARCHAR2(254) NOT NULL, + caller_class VARCHAR2(254) NOT NULL, + caller_method VARCHAR2(254) NOT NULL, + caller_line CHAR(4) NOT NULL, + event_id NUMBER(10) PRIMARY KEY + ); + + +CREATE TRIGGER logging_event_id_seq_trig + BEFORE INSERT ON logging_event + FOR EACH ROW + BEGIN + SELECT logging_event_id_seq.NEXTVAL + INTO :NEW.event_id + FROM DUAL; + END logging_event_id_seq_trig; + + +CREATE TABLE logging_event_property + ( + event_id NUMBER(10) NOT NULL, + mapped_key VARCHAR2(254) NOT NULL, + mapped_value VARCHAR2(1024), + PRIMARY KEY(event_id, mapped_key), + FOREIGN KEY (event_id) REFERENCES logging_event(event_id) + ); + +CREATE TABLE logging_event_exception + ( + event_id NUMBER(10) NOT NULL, + i SMALLINT NOT NULL, + trace_line VARCHAR2(254) NOT NULL, + PRIMARY KEY(event_id, i), + FOREIGN KEY (event_id) REFERENCES logging_event(event_id) + ); + + + + Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/postgresql.sql ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/postgresql.sql Fri Sep 22 17:12:36 2006 @@ -0,0 +1,48 @@ +# This SQL script creates the required tables by org.apache.log4j.db.DBAppender and +# org.apache.log4j.db.DBReceiver. +# +# It is intended for PostgreSQL databases. + +DROP TABLE logging_event_property; +DROP TABLE logging_event_exception; +DROP SEQUENCE logging_event_id_seq; +DROP TABLE logging_event; + + +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, + level_string VARCHAR(254) NOT NULL, + ndc TEXT, + thread_name VARCHAR(254), + reference_flag SMALLINT, + caller_filename VARCHAR(254) NOT NULL, + caller_class VARCHAR(254) NOT NULL, + caller_method VARCHAR(254) NOT NULL, + caller_line CHAR(4) NOT NULL, + event_id INT DEFAULT nextval('logging_event_id_seq') PRIMARY KEY + ); + +CREATE TABLE logging_event_property + ( + event_id INT NOT NULL, + mapped_key VARCHAR(254) NOT NULL, + mapped_value VARCHAR(1024), + PRIMARY KEY(event_id, mapped_key), + FOREIGN KEY (event_id) REFERENCES logging_event(event_id) + ); + +CREATE TABLE logging_event_exception + ( + event_id INT NOT NULL, + i SMALLINT NOT NULL, + trace_line VARCHAR(254) NOT NULL, + PRIMARY KEY(event_id, i), + FOREIGN KEY (event_id) REFERENCES logging_event(event_id) + ); Added: logback/trunk/logback-classic/src/test/input/db/dbAppenderUsingConnectionSource.xml ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/test/input/db/dbAppenderUsingConnectionSource.xml Fri Sep 22 17:12:36 2006 @@ -0,0 +1,23 @@ +<configuration> + + <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> + <connectionSource class="ch.qos.logback.classic.db.DriverManagerConnectionSource"> + <param name="driverClass" value="com.mysql.jdbc.Driver"/> + <param name="url" value="jdbc:mysql://host_name:3306/datebase_name"/> + <param name="user" value="logback"/> + <param name="password" value="logback"/> + </connectionSource> + </appender> + + + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <param name="pattern" value="%p %t %c - %m%n"/> + </layout> + </appender> + <root> + <level value="debug"/> + <appender-ref ref="STDOUT"/> + <appender-ref ref="DB"/> + </root> +</configuration> Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderTest.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderTest.java Fri Sep 22 17:12:36 2006 @@ -0,0 +1,7 @@ +package ch.qos.logback.classic.db; + +import junit.framework.TestCase; + +public class DBAppenderTest extends TestCase { + +}
participants (1)
-
noreply.seb@qos.ch