
Author: seb Date: Tue Sep 26 18:14:59 2006 New Revision: 607 Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/ConnectionSource.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/ConnectionSourceBase.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/DBAppenderBase.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/DBHelper.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/DataSourceConnectionSource.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/DriverManagerConnectionSource.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/JNDIConnectionSource.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/DBUtil.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/HSQLDBDialect.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/MsSQLDialect.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/MySQLDialect.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/OracleDialect.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/PostgreSQLDialect.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/SQLDialect.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/db2.sql logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/db2l.sql logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/hsqldb.sql logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/mssql.sql logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/mysql.sql logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/oracle.sql logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/postgresql.sql Log: - Created DBAppenderBase, and modified DBAppender accordingly - Moved classes to Core module when possible. Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/ConnectionSource.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/ConnectionSource.java Tue Sep 26 18:14:59 2006 @@ -0,0 +1,60 @@ +/** + * 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.core.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 logback 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-core/src/main/java/ch/qos/logback/core/db/ConnectionSourceBase.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/ConnectionSourceBase.java Tue Sep 26 18:14:59 2006 @@ -0,0 +1,119 @@ +/** + * 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.core.db; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +import ch.qos.logback.core.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 connection"); + 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-core/src/main/java/ch/qos/logback/core/db/DBAppenderBase.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/DBAppenderBase.java Tue Sep 26 18:14:59 2006 @@ -0,0 +1,261 @@ +/** + * 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.core.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.Iterator; +import java.util.Map; +import java.util.Set; + +import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.Layout; +import ch.qos.logback.core.db.dialect.DBUtil; +import ch.qos.logback.core.db.dialect.SQLDialect; + +/** + * @author Ceki Gülcü + * @author Ray DeCampo + * @author Sébastien Pennec + */ +public abstract class DBAppenderBase extends AppenderBase { + protected static final String insertPropertiesSQL = "INSERT INTO logging_event_property (event_id, mapped_key, mapped_value) VALUES (?, ?, ?)"; + protected static final String insertExceptionSQL = "INSERT INTO logging_event_exception (event_id, i, trace_line) VALUES (?, ?, ?)"; + protected static final String insertSQL; + protected 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; + } + + protected ConnectionSource connectionSource; + protected boolean cnxSupportsGetGeneratedKeys = false; + protected boolean cnxSupportsBatchUpdates = false; + protected SQLDialect sqlDialect; + + public DBAppenderBase() { + } + + @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 + public void append(Object eventObject) { + // LoggingEvent event = (LoggingEvent) eventObject; + Connection connection = null; + try { + connection = connectionSource.getConnection(); + connection.setAutoCommit(false); + + PreparedStatement insertStatement = connection + .prepareStatement(insertSQL); + + subAppend(eventObject, connection, insertStatement); + + // we no longer need the insertStatement + if (insertStatement != null) { + insertStatement.close(); + insertStatement = null; + } + + connection.commit(); + } catch (Throwable sqle) { + addError("problem appending event", sqle); + } finally { + DBHelper.closeConnection(connection); + } + } + + protected abstract void subAppend(Object eventObject, Connection connection, + PreparedStatement statement) throws Throwable; + + protected 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; + } + + protected 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; + } + } + + protected void insertThrowable(String[] strRep, Connection connection, + int eventId) throws SQLException { + + 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-core/src/main/java/ch/qos/logback/core/db/DBHelper.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/DBHelper.java Tue Sep 26 18:14:59 2006 @@ -0,0 +1,42 @@ +/** + * 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.core.db; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * @author Ceki Gülcü + * + */ +public class DBHelper { + + 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-core/src/main/java/ch/qos/logback/core/db/DataSourceConnectionSource.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/DataSourceConnectionSource.java Tue Sep 26 18:14:59 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.core.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 ch.qos.logback.classic.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-core/src/main/java/ch/qos/logback/core/db/DriverManagerConnectionSource.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/DriverManagerConnectionSource.java Tue Sep 26 18:14:59 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.core.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 logback DriverManagerConnectionSource."); + } + } catch (final ClassNotFoundException cnfe) { + addError("Could not load JDBC driver class: " + driverClass, cnfe); + } + } + + /** + * @see ch.qos.logback.classic.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-core/src/main/java/ch/qos/logback/core/db/JNDIConnectionSource.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/JNDIConnectionSource.java Tue Sep 26 18:14:59 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.core.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-core/src/main/java/ch/qos/logback/core/db/dialect/DBUtil.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/DBUtil.java Tue Sep 26 18:14:59 2006 @@ -0,0 +1,118 @@ +/** + * 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.core.db.dialect; + +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +import ch.qos.logback.core.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-core/src/main/java/ch/qos/logback/core/db/dialect/HSQLDBDialect.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/HSQLDBDialect.java Tue Sep 26 18:14:59 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.core.db.dialect; + +/** + * The HSQLDB dialect. + * + * @author Ceki Gülcü +*/ +public class HSQLDBDialect implements SQLDialect { + public static final String SELECT_CURRVAL = "CALL IDENTITY()"; + + public String getSelectInsertId() { + return SELECT_CURRVAL; + } +} Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/MsSQLDialect.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/MsSQLDialect.java Tue Sep 26 18:14:59 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.core.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-core/src/main/java/ch/qos/logback/core/db/dialect/MySQLDialect.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/MySQLDialect.java Tue Sep 26 18:14:59 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.core.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-core/src/main/java/ch/qos/logback/core/db/dialect/OracleDialect.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/OracleDialect.java Tue Sep 26 18:14:59 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.core.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-core/src/main/java/ch/qos/logback/core/db/dialect/PostgreSQLDialect.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/PostgreSQLDialect.java Tue Sep 26 18:14:59 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.core.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-core/src/main/java/ch/qos/logback/core/db/dialect/SQLDialect.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/SQLDialect.java Tue Sep 26 18:14:59 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.core.db.dialect; + +/** + * @author ceki + * + */ +public interface SQLDialect { + + public String getSelectInsertId(); + +} Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/db2.sql ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/db2.sql Tue Sep 26 18:14:59 2006 @@ -0,0 +1,48 @@ +# This SQL script creates the required tables by ch.qos.logback.classic.db.DBAppender +# +# 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-core/src/main/java/ch/qos/logback/core/db/dialect/db2l.sql ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/db2l.sql Tue Sep 26 18:14:59 2006 @@ -0,0 +1,46 @@ +# This SQL script creates the required tables by ch.qos.logback.classic.db.DBAppender +# +# 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-core/src/main/java/ch/qos/logback/core/db/dialect/hsqldb.sql ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/hsqldb.sql Tue Sep 26 18:14:59 2006 @@ -0,0 +1,44 @@ +// This SQL script creates the required tables by +// ch.qos.logback.classic.db.DBAppender. +// +// 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 + ( + timestmp BIGINT NOT NULL, + formatted_message LONGVARCHAR NOT NULL, + logger_name VARCHAR(256) NOT NULL, + level_string VARCHAR(256) NOT NULL, + thread_name VARCHAR(256), + reference_flag SMALLINT, + caller_filename VARCHAR(256), + caller_class VARCHAR(256), + caller_method VARCHAR(256), + 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(256) NOT NULL, + PRIMARY KEY(event_id, i), + FOREIGN KEY (event_id) REFERENCES logging_event(event_id) + ); Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/mssql.sql ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/mssql.sql Tue Sep 26 18:14:59 2006 @@ -0,0 +1,44 @@ +-- This SQL script creates the required tables by ch.qos.logback.classic.db.DBAppender +-- +-- 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-core/src/main/java/ch/qos/logback/core/db/dialect/mysql.sql ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/mysql.sql Tue Sep 26 18:14:59 2006 @@ -0,0 +1,51 @@ +# This SQL script creates the required tables by ch.qos.logback.classic.db.DBAppender. +# +# It is intended for MySQL databases. It has been tested on MySQL 5.0.22 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 + ( + timestmp BIGINT NOT NULL, + formatted_message TEXT NOT NULL, + logger_name VARCHAR(254) NOT NULL, + level_string VARCHAR(254) NOT NULL, + 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-core/src/main/java/ch/qos/logback/core/db/dialect/oracle.sql ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/oracle.sql Tue Sep 26 18:14:59 2006 @@ -0,0 +1,66 @@ +-- This SQL script creates the required tables by ch.qos.logback.classic.db.DBAppender +-- +-- 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-core/src/main/java/ch/qos/logback/core/db/dialect/postgresql.sql ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/db/dialect/postgresql.sql Tue Sep 26 18:14:59 2006 @@ -0,0 +1,47 @@ +# This SQL script creates the required tables by ch.qos.logback.classic.db.DBAppender +# +# 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) + );