Again 6 commits for the same Jira? Ralph
> On Jan 11, 2018, at 5:52 PM, [email protected] wrote: > > Repository: logging-log4j2 > Updated Branches: > refs/heads/master f530d4c8c -> 187e236ff > > > [LOG4J2-2181] The JDBC Appender should use keys and values from a Log4j > MapMessage. Implementation and documentation. > > Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo > Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/187e236f > Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/187e236f > Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/187e236f > > Branch: refs/heads/master > Commit: 187e236ff168238cd2477fad2e78eaeaae5137d5 > Parents: f530d4c > Author: Gary Gregory <[email protected]> > Authored: Thu Jan 11 17:52:03 2018 -0700 > Committer: Gary Gregory <[email protected]> > Committed: Thu Jan 11 17:52:03 2018 -0700 > > ---------------------------------------------------------------------- > .../log4j/cassandra/CassandraManager.java | 2 +- > .../appender/db/AbstractDatabaseManager.java | 31 ++++- > .../log4j/core/appender/db/ColumnMapping.java | 4 +- > .../core/appender/db/jdbc/JdbcAppender.java | 52 +++----- > .../appender/db/jdbc/JdbcDatabaseManager.java | 97 +++++++++++---- > .../appender/db/jpa/JpaDatabaseManager.java | 2 +- > .../appender/nosql/NoSqlDatabaseManager.java | 2 +- > .../db/AbstractDatabaseManagerTest.java | 20 ++-- > .../JdbcAppenderMapMessageDataSourceTest.java | 118 +++++++++++++++++++ > .../db/jdbc/log4j2-data-source-map-message.xml | 43 +++++++ > .../mongodb/MongoDbMapMessageTestJava8.java | 8 +- > src/site/xdoc/manual/appenders.xml | 77 ++++++++++++ > src/site/xdoc/manual/messages.xml | 4 + > 13 files changed, 383 insertions(+), 77 deletions(-) > ---------------------------------------------------------------------- > > > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/187e236f/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/CassandraManager.java > ---------------------------------------------------------------------- > diff --git > a/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/CassandraManager.java > > b/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/CassandraManager.java > index e9926df..65ee60e 100644 > --- > a/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/CassandraManager.java > +++ > b/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/CassandraManager.java > @@ -197,7 +197,7 @@ public class CassandraManager extends > AbstractDatabaseManager { > final String clusterName, final String keyspace, > final String table, final String username, > final String password, final boolean > useClockForTimestampGenerator, final int bufferSize, > final boolean batched, final BatchStatement.Type > batchType) { > - super(bufferSize); > + super(bufferSize, null); > this.contactPoints = convertAndAddDefaultPorts(contactPoints); > this.columns = columns; > this.useTls = useTls; > > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/187e236f/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManager.java > ---------------------------------------------------------------------- > diff --git > a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManager.java > > b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManager.java > index 54c33d1..7624c5e 100644 > --- > a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManager.java > +++ > b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManager.java > @@ -22,6 +22,7 @@ import java.io.Serializable; > import java.util.ArrayList; > import java.util.concurrent.TimeUnit; > > +import org.apache.logging.log4j.core.Layout; > import org.apache.logging.log4j.core.LogEvent; > import org.apache.logging.log4j.core.appender.AbstractManager; > import org.apache.logging.log4j.core.appender.ManagerFactory; > @@ -32,6 +33,7 @@ import > org.apache.logging.log4j.core.appender.ManagerFactory; > public abstract class AbstractDatabaseManager extends AbstractManager > implements Flushable { > private final ArrayList<LogEvent> buffer; > private final int bufferSize; > + private final Layout<? extends Serializable> layout; > > private boolean running = false; > > @@ -43,9 +45,22 @@ public abstract class AbstractDatabaseManager extends > AbstractManager implements > * @param bufferSize The size of the log event buffer. > */ > protected AbstractDatabaseManager(final String name, final int > bufferSize) { > + this(name, bufferSize, null); > + } > + > + /** > + * Instantiates the base manager. > + * > + * @param name The manager name, which should include any configuration > details that one might want to be able to > + * reconfigure at runtime, such as database name, username, > (hashed) password, etc. > + * @param layout the Appender-level layout. > + * @param bufferSize The size of the log event buffer. > + */ > + protected AbstractDatabaseManager(final String name, final int > bufferSize, final Layout<? extends Serializable> layout) { > super(null, name); > this.bufferSize = bufferSize; > this.buffer = new ArrayList<>(bufferSize + 1); > + this.layout = layout; > } > > /** > @@ -156,7 +171,7 @@ public abstract class AbstractDatabaseManager extends > AbstractManager implements > this.connectAndStart(); > try { > for (final LogEvent event : this.buffer) { > - this.writeInternal(event); > + this.writeInternal(event, layout != null ? > layout.toSerializable(event) : null); > } > } finally { > this.commitAndClose(); > @@ -233,14 +248,17 @@ public abstract class AbstractDatabaseManager extends > AbstractManager implements > */ > protected abstract static class AbstractFactoryData { > private final int bufferSize; > + private final Layout<? extends Serializable> layout; > > /** > * Constructs the base factory data. > * > * @param bufferSize The size of the buffer. > + * @param bufferSize The appender-level layout > */ > - protected AbstractFactoryData(final int bufferSize) { > + protected AbstractFactoryData(final int bufferSize, final Layout<? > extends Serializable> layout) { > this.bufferSize = bufferSize; > + this.layout = layout; > } > > /** > @@ -251,5 +269,14 @@ public abstract class AbstractDatabaseManager extends > AbstractManager implements > public int getBufferSize() { > return bufferSize; > } > + > + /** > + * Gets the layout. > + * > + * @return the layout. > + */ > + public Layout<? extends Serializable> getLayout() { > + return layout; > + } > } > } > > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/187e236f/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/ColumnMapping.java > ---------------------------------------------------------------------- > diff --git > a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/ColumnMapping.java > > b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/ColumnMapping.java > index 6c106bd..fca50d4 100644 > --- > a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/ColumnMapping.java > +++ > b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/ColumnMapping.java > @@ -167,8 +167,8 @@ public class ColumnMapping { > .withConfiguration(configuration) > .build(); > } > - if (!(layout != null > - || literal != null > + if (!(layout == null > + || literal == null > || Date.class.isAssignableFrom(type) > || ReadOnlyStringMap.class.isAssignableFrom(type) > || ThreadContextMap.class.isAssignableFrom(type) > > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/187e236f/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcAppender.java > ---------------------------------------------------------------------- > diff --git > a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcAppender.java > > b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcAppender.java > index af0f93f..974d87e 100644 > --- > a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcAppender.java > +++ > b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcAppender.java > @@ -17,7 +17,6 @@ > package org.apache.logging.log4j.core.appender.db.jdbc; > > import java.io.Serializable; > -import java.nio.charset.Charset; > import java.sql.PreparedStatement; > import java.util.Arrays; > import java.util.Objects; > @@ -55,9 +54,9 @@ public final class JdbcAppender extends > AbstractDatabaseAppender<JdbcDatabaseMan > > private final String description; > > - private JdbcAppender(final String name, final Filter filter, final > boolean ignoreExceptions, > - final JdbcDatabaseManager manager) { > - super(name, filter, ignoreExceptions, manager); > + private JdbcAppender(final String name, final Filter filter, final > Layout<? extends Serializable> layout, > + final boolean ignoreExceptions, final JdbcDatabaseManager > manager) { > + super(name, filter, layout, ignoreExceptions, manager); > this.description = this.getName() + "{ manager=" + this.getManager() > + " }"; > } > > @@ -124,6 +123,8 @@ public final class JdbcAppender extends > AbstractDatabaseAppender<JdbcDatabaseMan > > /** > * The connections source from which database connections should be > retrieved. > + * > + * @return this > */ > public B setConnectionSource(final ConnectionSource connectionSource) > { > this.connectionSource = connectionSource; > @@ -133,6 +134,8 @@ public final class JdbcAppender extends > AbstractDatabaseAppender<JdbcDatabaseMan > /** > * If an integer greater than 0, this causes the appender to buffer > log events and flush whenever the buffer > * reaches this size. > + * > + * @return this > */ > public B setBufferSize(final int bufferSize) { > this.bufferSize = bufferSize; > @@ -141,6 +144,8 @@ public final class JdbcAppender extends > AbstractDatabaseAppender<JdbcDatabaseMan > > /** > * The name of the database table to insert log events into. > + * > + * @return this > */ > public B setTableName(final String tableName) { > this.tableName = tableName; > @@ -149,6 +154,8 @@ public final class JdbcAppender extends > AbstractDatabaseAppender<JdbcDatabaseMan > > /** > * Information about the columns that log event data should be > inserted into and how to insert that data. > + * > + * @return this > */ > public B setColumnConfigs(final ColumnConfig... columnConfigs) { > this.columnConfigs = columnConfigs; > @@ -163,42 +170,19 @@ public final class JdbcAppender extends > AbstractDatabaseAppender<JdbcDatabaseMan > @Override > public JdbcAppender build() { > if (Assert.isEmpty(columnConfigs) && > Assert.isEmpty(columnMappings)) { > - LOGGER.error("Cannot create JdbcAppender without any columns > configured."); > + LOGGER.error("Cannot create JdbcAppender without any > columns."); > return null; > } > - final String managerName = "JdbcManager{name=" + getName() + ", > bufferSize=" + bufferSize + ", tableName=" + > - tableName + ", columnConfigs=" + > Arrays.toString(columnConfigs) + ", columnMappings=" + > - Arrays.toString(columnMappings) + '}'; > - final JdbcDatabaseManager manager = > JdbcDatabaseManager.getManager(managerName, bufferSize, > - connectionSource, tableName, columnConfigs, columnMappings); > + final String managerName = "JdbcManager{name=" + getName() + ", > bufferSize=" + bufferSize + ", tableName=" > + + tableName + ", columnConfigs=" + > Arrays.toString(columnConfigs) + ", columnMappings=" > + + Arrays.toString(columnMappings) + '}'; > + final JdbcDatabaseManager manager = > JdbcDatabaseManager.getManager(managerName, bufferSize, getLayout(), > + connectionSource, tableName, columnConfigs, > columnMappings); > if (manager == null) { > return null; > } > - return new JdbcAppender(getName(), getFilter(), > isIgnoreExceptions(), manager); > + return new JdbcAppender(getName(), getFilter(), getLayout(), > isIgnoreExceptions(), manager); > } > > - @Override > - @Deprecated > - public Layout<? extends Serializable> getLayout() { > - throw new UnsupportedOperationException(); > - } > - > - @Override > - @Deprecated > - public B withLayout(final Layout<? extends Serializable> layout) { > - throw new UnsupportedOperationException(); > - } > - > - @Override > - @Deprecated > - public Layout<? extends Serializable> getOrCreateLayout() { > - throw new UnsupportedOperationException(); > - } > - > - @Override > - @Deprecated > - public Layout<? extends Serializable> getOrCreateLayout(final > Charset charset) { > - throw new UnsupportedOperationException(); > - } > } > } > > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/187e236f/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcDatabaseManager.java > ---------------------------------------------------------------------- > diff --git > a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcDatabaseManager.java > > b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcDatabaseManager.java > index a652c2c..6c3090a 100644 > --- > a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcDatabaseManager.java > +++ > b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcDatabaseManager.java > @@ -30,16 +30,23 @@ import java.util.ArrayList; > import java.util.Date; > import java.util.List; > > +import org.apache.logging.log4j.core.Layout; > import org.apache.logging.log4j.core.LogEvent; > +import org.apache.logging.log4j.core.StringLayout; > import org.apache.logging.log4j.core.appender.AppenderLoggingException; > import org.apache.logging.log4j.core.appender.ManagerFactory; > import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager; > import org.apache.logging.log4j.core.appender.db.ColumnMapping; > +import org.apache.logging.log4j.core.appender.nosql.NoSqlObject; > import org.apache.logging.log4j.core.config.plugins.convert.DateTypeConverter; > import org.apache.logging.log4j.core.config.plugins.convert.TypeConverters; > import org.apache.logging.log4j.core.util.Closer; > +import org.apache.logging.log4j.message.MapMessage; > import org.apache.logging.log4j.spi.ThreadContextMap; > import org.apache.logging.log4j.spi.ThreadContextStack; > +import org.apache.logging.log4j.status.StatusLogger; > +import org.apache.logging.log4j.util.BiConsumer; > +import org.apache.logging.log4j.util.IndexedReadOnlyStringMap; > import org.apache.logging.log4j.util.ReadOnlyStringMap; > import org.apache.logging.log4j.util.Strings; > > @@ -48,6 +55,10 @@ import org.apache.logging.log4j.util.Strings; > */ > public final class JdbcDatabaseManager extends AbstractDatabaseManager { > > + private static StatusLogger logger() { > + return StatusLogger.getLogger(); > + } > + > private static final JdbcDatabaseManagerFactory INSTANCE = new > JdbcDatabaseManagerFactory(); > > // NOTE: prepared statements are prepared in this order: column mappings, > then column configs > @@ -91,11 +102,11 @@ public final class JdbcDatabaseManager extends > AbstractDatabaseManager { > try { > this.connection = this.connectionSource.getConnection(); > this.connection.setAutoCommit(false); > + logger().debug("Preparing SQL: {}", this.sqlStatement); > this.statement = > this.connection.prepareStatement(this.sqlStatement); > } catch (final SQLException e) { > throw new AppenderLoggingException( > - "Cannot write logging event or flush buffer; JDBC > manager cannot connect to the database.", e > - ); > + "Cannot write logging event or flush buffer; JDBC > manager cannot connect to the database.", e); > } > } > > @@ -105,6 +116,14 @@ public final class JdbcDatabaseManager extends > AbstractDatabaseManager { > writeInternal(event, null); > } > > + private void setFields(final MapMessage<?, ?> mapMessage) throws > SQLException { > + final IndexedReadOnlyStringMap map = > mapMessage.getIndexedReadOnlyStringMap(); > + int i = 1; // JDBC indices start at 1 > + for (final ColumnMapping mapping : this.columnMappings) { > + statement.setObject(i++, map.getValue(mapping.getName())); > + } > + } > + > @Override > protected void writeInternal(final LogEvent event, final Serializable > serializable) { > StringReader reader = null; > @@ -115,27 +134,35 @@ public final class JdbcDatabaseManager extends > AbstractDatabaseManager { > "Cannot write logging event; JDBC manager not > connected to the database."); > } > > - int i = 1; > + if (serializable instanceof MapMessage) { > + setFields((MapMessage<?, ?>) serializable); > + } > + int i = 1; // JDBC indices start at 1 > for (final ColumnMapping mapping : this.columnMappings) { > if (ThreadContextMap.class.isAssignableFrom(mapping.getType()) > - || > ReadOnlyStringMap.class.isAssignableFrom(mapping.getType())) { > + || > ReadOnlyStringMap.class.isAssignableFrom(mapping.getType())) { > this.statement.setObject(i++, > event.getContextData().toMap()); > } else if > (ThreadContextStack.class.isAssignableFrom(mapping.getType())) { > this.statement.setObject(i++, > event.getContextStack().asList()); > } else if (Date.class.isAssignableFrom(mapping.getType())) { > - this.statement.setObject(i++, > - DateTypeConverter.fromMillis(event.getTimeMillis(), > mapping.getType().asSubclass(Date.class))); > - } else if (Clob.class.isAssignableFrom(mapping.getType())) { > - this.statement.setClob(i++, new > StringReader(mapping.getLayout().toSerializable(event))); > - } else if (NClob.class.isAssignableFrom(mapping.getType())) { > - this.statement.setNClob(i++, new > StringReader(mapping.getLayout().toSerializable(event))); > + this.statement.setObject(i++, > DateTypeConverter.fromMillis(event.getTimeMillis(), > + mapping.getType().asSubclass(Date.class))); > } else { > - final Object value = > TypeConverters.convert(mapping.getLayout().toSerializable(event), > - mapping.getType(), null); > - if (value == null) { > - this.statement.setNull(i++, Types.NULL); > - } else { > - this.statement.setObject(i++, value); > + StringLayout layout = mapping.getLayout(); > + if (layout != null) { > + if (Clob.class.isAssignableFrom(mapping.getType())) { > + this.statement.setClob(i++, new > StringReader(layout.toSerializable(event))); > + } else if > (NClob.class.isAssignableFrom(mapping.getType())) { > + this.statement.setNClob(i++, new > StringReader(layout.toSerializable(event))); > + } else { > + final Object value = > TypeConverters.convert(layout.toSerializable(event), mapping.getType(), > + null); > + if (value == null) { > + this.statement.setNull(i++, Types.NULL); > + } else { > + this.statement.setObject(i++, value); > + } > + } > } > } > } > @@ -213,7 +240,7 @@ public final class JdbcDatabaseManager extends > AbstractDatabaseManager { > * @param tableName The name of the database table to insert log events > into. > * @param columnConfigs Configuration information about the log table > columns. > * @return a new or existing JDBC manager as applicable. > - * @deprecated use {@link #getManager(String, int, ConnectionSource, > String, ColumnConfig[], ColumnMapping[])} > + * @deprecated use {@link #getManager(String, int, Layout, > ConnectionSource, String, ColumnConfig[], ColumnMapping[])} > */ > @Deprecated > public static JdbcDatabaseManager getJDBCDatabaseManager(final String > name, final int bufferSize, > @@ -222,7 +249,30 @@ public final class JdbcDatabaseManager extends > AbstractDatabaseManager { > final > ColumnConfig[] columnConfigs) { > > return getManager(name, > - new FactoryData(bufferSize, connectionSource, tableName, > columnConfigs, new ColumnMapping[0]), > + new FactoryData(bufferSize, null, connectionSource, tableName, > columnConfigs, new ColumnMapping[0]), > + getFactory()); > + } > + > + /** > + * Creates a JDBC manager for use within the {@link JdbcAppender}, or > returns a suitable one if it already exists. > + * > + * @param name The name of the manager, which should include connection > details and hashed passwords where possible. > + * @param bufferSize The size of the log event buffer. > + * @param connectionSource The source for connections to the database. > + * @param tableName The name of the database table to insert log events > into. > + * @param columnConfigs Configuration information about the log table > columns. > + * @param columnMappings column mapping configuration (including type > conversion). > + * @return a new or existing JDBC manager as applicable. > + * @deprecated use {@link #getManager(String, int, Layout, > ConnectionSource, String, ColumnConfig[], ColumnMapping[])} > + */ > + @Deprecated > + public static JdbcDatabaseManager getManager(final String name, > + final int bufferSize, > + final ConnectionSource > connectionSource, > + final String tableName, > + final ColumnConfig[] > columnConfigs, > + final ColumnMapping[] > columnMappings) { > + return getManager(name, new FactoryData(bufferSize, null, > connectionSource, tableName, columnConfigs, columnMappings), > getFactory()); > } > > @@ -231,6 +281,7 @@ public final class JdbcDatabaseManager extends > AbstractDatabaseManager { > * > * @param name The name of the manager, which should include connection > details and hashed passwords where possible. > * @param bufferSize The size of the log event buffer. > + * @param layout The Appender-level layout > * @param connectionSource The source for connections to the database. > * @param tableName The name of the database table to insert log events > into. > * @param columnConfigs Configuration information about the log table > columns. > @@ -239,11 +290,12 @@ public final class JdbcDatabaseManager extends > AbstractDatabaseManager { > */ > public static JdbcDatabaseManager getManager(final String name, > final int bufferSize, > + final Layout<? extends > Serializable> layout, > final ConnectionSource > connectionSource, > final String tableName, > final ColumnConfig[] > columnConfigs, > final ColumnMapping[] > columnMappings) { > - return getManager(name, new FactoryData(bufferSize, > connectionSource, tableName, columnConfigs, columnMappings), > + return getManager(name, new FactoryData(bufferSize, layout, > connectionSource, tableName, columnConfigs, columnMappings), > getFactory()); > } > > @@ -260,9 +312,10 @@ public final class JdbcDatabaseManager extends > AbstractDatabaseManager { > private final ColumnConfig[] columnConfigs; > private final ColumnMapping[] columnMappings; > > - protected FactoryData(final int bufferSize, final ConnectionSource > connectionSource, final String tableName, > - final ColumnConfig[] columnConfigs, final > ColumnMapping[] columnMappings) { > - super(bufferSize); > + protected FactoryData(final int bufferSize, final Layout<? extends > Serializable> layout, > + final ConnectionSource connectionSource, final String > tableName, final ColumnConfig[] columnConfigs, > + final ColumnMapping[] columnMappings) { > + super(bufferSize, layout); > this.connectionSource = connectionSource; > this.tableName = tableName; > this.columnConfigs = columnConfigs; > > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/187e236f/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaDatabaseManager.java > ---------------------------------------------------------------------- > diff --git > a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaDatabaseManager.java > > b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaDatabaseManager.java > index ddfa18e..b2d36a8 100644 > --- > a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaDatabaseManager.java > +++ > b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jpa/JpaDatabaseManager.java > @@ -178,7 +178,7 @@ public final class JpaDatabaseManager extends > AbstractDatabaseManager { > protected FactoryData(final int bufferSize, final Class<? extends > AbstractLogEventWrapperEntity> entityClass, > final Constructor<? extends > AbstractLogEventWrapperEntity> entityConstructor, > final String persistenceUnitName) { > - super(bufferSize); > + super(bufferSize, null); > > this.entityClass = entityClass; > this.entityConstructor = entityConstructor; > > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/187e236f/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/nosql/NoSqlDatabaseManager.java > ---------------------------------------------------------------------- > diff --git > a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/nosql/NoSqlDatabaseManager.java > > b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/nosql/NoSqlDatabaseManager.java > index 361ae2b..80acae7 100644 > --- > a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/nosql/NoSqlDatabaseManager.java > +++ > b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/nosql/NoSqlDatabaseManager.java > @@ -234,7 +234,7 @@ public final class NoSqlDatabaseManager<W> extends > AbstractDatabaseManager { > private final NoSqlProvider<?> provider; > > protected FactoryData(final int bufferSize, final NoSqlProvider<?> > provider) { > - super(bufferSize); > + super(bufferSize, null); > this.provider = provider; > } > } > > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/187e236f/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManagerTest.java > ---------------------------------------------------------------------- > diff --git > a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManagerTest.java > > b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManagerTest.java > index ae2104b..932724d 100644 > --- > a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManagerTest.java > +++ > b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/AbstractDatabaseManagerTest.java > @@ -144,10 +144,10 @@ public class AbstractDatabaseManagerTest { > manager.write(event4, null); > > then(manager).should().connectAndStart(); > - then(manager).should().writeInternal(same(event1copy)); > - then(manager).should().writeInternal(same(event2copy)); > - then(manager).should().writeInternal(same(event3copy)); > - then(manager).should().writeInternal(same(event4copy)); > + then(manager).should().writeInternal(same(event1copy), > (Serializable) isNull()); > + then(manager).should().writeInternal(same(event2copy), > (Serializable) isNull()); > + then(manager).should().writeInternal(same(event3copy), > (Serializable) isNull()); > + then(manager).should().writeInternal(same(event4copy), > (Serializable) isNull()); > then(manager).should().commitAndClose(); > then(manager).shouldHaveNoMoreInteractions(); > } > @@ -177,9 +177,9 @@ public class AbstractDatabaseManagerTest { > manager.flush(); > > then(manager).should().connectAndStart(); > - then(manager).should().writeInternal(same(event1copy)); > - then(manager).should().writeInternal(same(event2copy)); > - then(manager).should().writeInternal(same(event3copy)); > + then(manager).should().writeInternal(same(event1copy), > (Serializable) isNull()); > + then(manager).should().writeInternal(same(event2copy), > (Serializable) isNull()); > + then(manager).should().writeInternal(same(event3copy), > (Serializable) isNull()); > then(manager).should().commitAndClose(); > then(manager).shouldHaveNoMoreInteractions(); > } > @@ -209,9 +209,9 @@ public class AbstractDatabaseManagerTest { > manager.shutdown(); > > then(manager).should().connectAndStart(); > - then(manager).should().writeInternal(same(event1copy)); > - then(manager).should().writeInternal(same(event2copy)); > - then(manager).should().writeInternal(same(event3copy)); > + then(manager).should().writeInternal(same(event1copy), > (Serializable) isNull()); > + then(manager).should().writeInternal(same(event2copy), > (Serializable) isNull()); > + then(manager).should().writeInternal(same(event3copy), > (Serializable) isNull()); > then(manager).should().commitAndClose(); > then(manager).should().shutdownInternal(); > then(manager).shouldHaveNoMoreInteractions(); > > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/187e236f/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcAppenderMapMessageDataSourceTest.java > ---------------------------------------------------------------------- > diff --git > a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcAppenderMapMessageDataSourceTest.java > > b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcAppenderMapMessageDataSourceTest.java > new file mode 100644 > index 0000000..c186537 > --- /dev/null > +++ > b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jdbc/JdbcAppenderMapMessageDataSourceTest.java > @@ -0,0 +1,118 @@ > +/* > + * Licensed to the Apache Software Foundation (ASF) under one or more > + * contributor license agreements. See the NOTICE file distributed with > + * this work for additional information regarding copyright ownership. > + * The ASF licenses this file to You under the Apache license, Version 2.0 > + * (the "License"); you may not use this file except in compliance with > + * the License. You may obtain a copy of the License at > + * > + * http://www.apache.org/licenses/LICENSE-2.0 > + * > + * Unless required by applicable law or agreed to in writing, software > + * distributed under the License is distributed on an "AS IS" BASIS, > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + * See the license for the specific language governing permissions and > + * limitations under the license. > + */ > +package org.apache.logging.log4j.core.appender.db.jdbc; > + > +import static org.junit.Assert.assertFalse; > +import static org.junit.Assert.assertTrue; > +import static org.mockito.BDDMockito.given; > +import static org.mockito.Mockito.mock; > + > +import java.io.ByteArrayOutputStream; > +import java.io.PrintWriter; > +import java.sql.Connection; > +import java.sql.ResultSet; > +import java.sql.SQLException; > +import java.sql.Statement; > + > +import javax.sql.DataSource; > + > +import org.apache.logging.log4j.LogManager; > +import org.apache.logging.log4j.Logger; > +import org.apache.logging.log4j.core.util.Throwables; > +import org.apache.logging.log4j.junit.JdbcRule; > +import org.apache.logging.log4j.junit.JndiRule; > +import org.apache.logging.log4j.junit.LoggerContextRule; > +import org.apache.logging.log4j.message.MapMessage; > +import org.junit.Assert; > +import org.junit.Rule; > +import org.junit.Test; > +import org.junit.rules.RuleChain; > +import org.mockito.invocation.InvocationOnMock; > +import org.mockito.stubbing.Answer; > + > +/** > + * Unit tests {@link MapMessage}s for JdbcAppender using a {@link > DataSource} configuration. > + */ > +public class JdbcAppenderMapMessageDataSourceTest { > + > + @Rule > + public final RuleChain rules; > + private final JdbcRule jdbcRule; > + > + public JdbcAppenderMapMessageDataSourceTest() { > + this(new JdbcRule(JdbcH2TestHelper.TEST_CONFIGURATION_SOURCE, > + // @formatter:off > + "CREATE TABLE dsLogEntry (Id INTEGER IDENTITY, ColumnA > VARCHAR(255), ColumnB VARCHAR(255))", > + "DROP TABLE dsLogEntry")); > + // @formatter:on > + } > + > + protected JdbcAppenderMapMessageDataSourceTest(final JdbcRule jdbcRule) { > + // @formatter:off > + this.rules = RuleChain.emptyRuleChain() > + .around(new > JndiRule("java:/comp/env/jdbc/TestDataSourceAppender", > createMockDataSource())) > + .around(jdbcRule) > + .around(new > LoggerContextRule("org/apache/logging/log4j/core/appender/db/jdbc/log4j2-data-source-map-message.xml")); > + // @formatter:on > + this.jdbcRule = jdbcRule; > + } > + > + private DataSource createMockDataSource() { > + try { > + final DataSource dataSource = mock(DataSource.class); > + given(dataSource.getConnection()).willAnswer(new > Answer<Connection>() { > + @Override > + public Connection answer(final InvocationOnMock invocation) > throws Throwable { > + return jdbcRule.getConnectionSource().getConnection(); > + } > + }); > + return dataSource; > + } catch (final SQLException e) { > + Throwables.rethrow(e); > + throw new InternalError("unreachable"); > + } > + } > + > + @Test > + public void testDataSourceConfig() throws Exception { > + try (final Connection connection = > jdbcRule.getConnectionSource().getConnection()) { > + final Error exception = new Error("Final error massage is > fatal!"); > + final ByteArrayOutputStream outputStream = new > ByteArrayOutputStream(); > + final PrintWriter writer = new PrintWriter(outputStream); > + exception.printStackTrace(writer); > + writer.close(); > + > + final Logger logger = > LogManager.getLogger(this.getClass().getName() + ".testDataSourceConfig"); > + MapMessage mapMessage = new MapMessage(); > + mapMessage.with("Id", 1); > + mapMessage.with("ColumnA", "ValueA"); > + mapMessage.with("ColumnB", "ValueB"); > + logger.info(mapMessage); > + > + try (final Statement statement = connection.createStatement(); > + final ResultSet resultSet = statement > + .executeQuery("SELECT Id, ColumnA, ColumnB FROM > dsLogEntry ORDER BY Id")) { > + > + assertTrue("There should be at least one row.", > resultSet.next()); > + > + Assert.assertEquals(1, resultSet.getInt("Id")); > + > + assertFalse("There should not be two rows.", > resultSet.next()); > + } > + } > + } > +} > > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/187e236f/log4j-core/src/test/resources/org/apache/logging/log4j/core/appender/db/jdbc/log4j2-data-source-map-message.xml > ---------------------------------------------------------------------- > diff --git > a/log4j-core/src/test/resources/org/apache/logging/log4j/core/appender/db/jdbc/log4j2-data-source-map-message.xml > > b/log4j-core/src/test/resources/org/apache/logging/log4j/core/appender/db/jdbc/log4j2-data-source-map-message.xml > new file mode 100644 > index 0000000..a50bbf6 > --- /dev/null > +++ > b/log4j-core/src/test/resources/org/apache/logging/log4j/core/appender/db/jdbc/log4j2-data-source-map-message.xml > @@ -0,0 +1,43 @@ > +<?xml version="1.0" encoding="UTF-8"?> > +<!-- > + Licensed to the Apache Software Foundation (ASF) under one or more > + contributor license agreements. See the NOTICE file distributed with > + this work for additional information regarding copyright ownership. > + The ASF licenses this file to You under the Apache License, Version 2.0 > + (the "License"); you may not use this file except in compliance with > + the License. You may obtain a copy of the License at > + > + http://www.apache.org/licenses/LICENSE-2.0 > + > + Unless required by applicable law or agreed to in writing, software > + distributed under the License is distributed on an "AS IS" BASIS, > + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > + See the License for the specific language governing permissions and > + limitations under the License. > +--> > +<Configuration status="ERROR"> > + > + <Appenders> > + <Console name="STDOUT"> > + <PatternLayout pattern="%C{1.} %m %level MDC%X%n"/> > + </Console> > + <Jdbc name="databaseAppender" tableName="dsLogEntry" > ignoreExceptions="false"> > + <DataSource jndiName="java:/comp/env/jdbc/TestDataSourceAppender" /> > + <ColumnMapping name="Id" /> > + <ColumnMapping name="ColumnA" /> > + <ColumnMapping name="ColumnB" /> > + <MessageLayout /> > + </Jdbc> > + </Appenders> > + > + <Loggers> > + <Logger name="org.apache.logging.log4j.core.appender.db" level="debug" > additivity="false"> > + <AppenderRef ref="databaseAppender" /> > + </Logger> > + > + <Root level="fatal"> > + <AppenderRef ref="STDOUT"/> > + </Root> > + </Loggers> > + > +</Configuration> > > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/187e236f/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbMapMessageTestJava8.java > ---------------------------------------------------------------------- > diff --git > a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbMapMessageTestJava8.java > > b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbMapMessageTestJava8.java > index b245ac9..dbcae44 100644 > --- > a/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbMapMessageTestJava8.java > +++ > b/log4j-mongodb/src/test/java/org/apache/logging/log4j/mongodb/MongoDbMapMessageTestJava8.java > @@ -54,10 +54,10 @@ public class MongoDbMapMessageTestJava8 { > @Test > public void test() { > final Logger logger = LogManager.getLogger(); > - final MapMessage map = new MapMessage(); > - map.with("SomeName", "SomeValue"); > - map.with("SomeInt", 1); > - logger.info(map); > + final MapMessage mapMessage = new MapMessage(); > + mapMessage.with("SomeName", "SomeValue"); > + mapMessage.with("SomeInt", 1); > + logger.info(mapMessage); > // > try (final MongoClient mongoClient = > mongoDbTestRule.getMongoClient()) { > final MongoDatabase database = mongoClient.getDatabase("test"); > > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/187e236f/src/site/xdoc/manual/appenders.xml > ---------------------------------------------------------------------- > diff --git a/src/site/xdoc/manual/appenders.xml > b/src/site/xdoc/manual/appenders.xml > index 7ecd9d8..d266347 100644 > --- a/src/site/xdoc/manual/appenders.xml > +++ b/src/site/xdoc/manual/appenders.xml > @@ -1163,6 +1163,48 @@ CREATE TABLE logs ( > </td> > </tr> > </table> > + <table> > + <caption align="top">ColumnMapping Parameters</caption> > + <tr> > + <th>Parameter Name</th> > + <th>Type</th> > + <th>Description</th> > + </tr> > + <tr> > + <td>name</td> > + <td>String</td> > + <td><em>Required.</em> The name of the database column.</td> > + </tr> > + <tr> > + <td>pattern</td> > + <td>String</td> > + <td>Use this attribute to insert a value or values from the > log event in this column using a > + <code>PatternLayout</code> pattern. Simply specify any legal > pattern in this attribute. Either this > + attribute, <code>literal</code>, or > <code>isEventTimestamp="true"</code> must be specified, but not more > + than one of these.</td> > + </tr> > + <tr> > + <td>literal</td> > + <td>String</td> > + <td>Use this attribute to insert a literal value in this > column. The value will be included directly in > + the insert SQL, without any quoting (which means that if you > want this to be a string, your value should > + contain single quotes around it like this: > <code>literal="'Literal String'"</code>). This is especially > + useful for databases that don't support identity columns. > For example, if you are using Oracle you could > + specify <code>literal="NAME_OF_YOUR_SEQUENCE.NEXTVAL"</code> > to insert a unique ID in an ID column. > + Either this attribute, <code>pattern</code>, or > <code>isEventTimestamp="true"</code> must be specified, > + but not more than one of these.</td> > + </tr> > + <tr> > + <td>layout</td> > + <td>Layout</td> > + <td>The Layout to format the LogEvent.</td> > + </tr> > + <tr> > + <td>type</td> > + <td>String</td> > + <td>Conversion type name, a fully-qualified class name.</td> > + </tr> > + </table> > <p> > Here are a couple sample configurations for the JDBCAppender, as > well as a sample factory implementation > that uses Commons Pooling and Commons DBCP to pool database > connections: > @@ -1247,6 +1289,41 @@ public class ConnectionFactory { > return Singleton.INSTANCE.dataSource.getConnection(); > } > }]]></pre> > + <p> > + This appender is <a > href="messages.html#MapMessage">MapMessage</a>-aware. > + </p> > + <p> > + The following configuration uses a <code>MessageLayout</code> to > indicate that the Appender should match > + the keys of a <code>MapMessage</code> to the names of > <code>ColumnMapping</code>s when setting the > + values of the Appender's SQL INSERT statement. This let you > insert rows for custom values in a > + database table based on a Log4j <code>MapMessage</code> instead > of values from <code>LogEvent</code>s. > + </p> > + <pre class="prettyprint linenums lang-xml"><![CDATA[<Configuration > status="debug"> > + > + <Appenders> > + <Console name="STDOUT"> > + <PatternLayout pattern="%C{1.} %m %level MDC%X%n"/> > + </Console> > + <Jdbc name="databaseAppender" tableName="dsLogEntry" > ignoreExceptions="false"> > + <DataSource jndiName="java:/comp/env/jdbc/TestDataSourceAppender" /> > + <ColumnMapping name="Id" /> > + <ColumnMapping name="ColumnA" /> > + <ColumnMapping name="ColumnB" /> > + <MessageLayout /> > + </Jdbc> > + </Appenders> > + > + <Loggers> > + <Logger name="org.apache.logging.log4j.core.appender.db" level="debug" > additivity="false"> > + <AppenderRef ref="databaseAppender" /> > + </Logger> > + > + <Root level="fatal"> > + <AppenderRef ref="STDOUT"/> > + </Root> > + </Loggers> > + > +</Configuration>]]></pre> > </subsection> > <a name="JMSAppender"/> > <!-- cool URLs don't change, so here are some old anchors --> > > http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/187e236f/src/site/xdoc/manual/messages.xml > ---------------------------------------------------------------------- > diff --git a/src/site/xdoc/manual/messages.xml > b/src/site/xdoc/manual/messages.xml > index c243026..41005c8 100644 > --- a/src/site/xdoc/manual/messages.xml > +++ b/src/site/xdoc/manual/messages.xml > @@ -214,6 +214,10 @@ public class MyApp { > <code>MapMessage</code> to a JMS > <code>javax.jms.MapMessage</code>. > </li> > <li> > + When a <a href="appenders.html#JDBCAppender">JDBC Appender</a> > is configured with a <code>MessageLayout</code>, it converts a Log4j > + <code>MapMessage</code> to values in a SQL INSERT statement. > + </li> > + <li> > When a <a href="appenders.html#NoSQLAppenderMongoDB">MongoDB > Appender</a> is configured with a <code>MessageLayout</code>, it converts a > Log4j > <code>MapMessage</code> to fields in a MongoDB object. > </li> > >
