You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@logging.apache.org by Ralph Goers <ra...@dslextreme.com> on 2018/01/14 02:17:43 UTC
Re: logging-log4j2 git commit: [LOG4J2-2181] The JDBC Appender should
use keys and values from a Log4j MapMessage. Implementation and
documentation.
Again 6 commits for the same Jira?
Ralph
> On Jan 11, 2018, at 5:52 PM, ggregory@apache.org 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 <gg...@apache.org>
> Authored: Thu Jan 11 17:52:03 2018 -0700
> Committer: Gary Gregory <gg...@apache.org>
> 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>
>
>