LOG4J2-2076 Rename packages
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/44db87d0 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/44db87d0 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/44db87d0 Branch: refs/heads/master Commit: 44db87d0e921bac3b207f575889618ced5288e5f Parents: 98a66a1 Author: Mikael Ståldal <mik...@staldal.nu> Authored: Thu Oct 19 21:47:31 2017 +0200 Committer: Mikael Ståldal <mik...@staldal.nu> Committed: Thu Oct 19 21:47:31 2017 +0200 ---------------------------------------------------------------------- .../log4j/cassandra/CassandraAppender.java | 185 ++++++++++ .../log4j/cassandra/CassandraManager.java | 218 ++++++++++++ .../cassandra/ClockTimestampGenerator.java | 34 ++ .../logging/log4j/cassandra/package-info.java | 23 ++ .../appender/cassandra/CassandraAppender.java | 185 ---------- .../appender/cassandra/CassandraManager.java | 218 ------------ .../cassandra/ClockTimestampGenerator.java | 34 -- .../nosql/appender/cassandra/package-info.java | 23 -- .../log4j/cassandra/CassandraAppenderIT.java | 96 +++++ .../logging/log4j/cassandra/CassandraRule.java | 141 ++++++++ .../appender/cassandra/CassandraAppenderIT.java | 96 ----- .../nosql/appender/cassandra/CassandraRule.java | 141 -------- .../test/resources/CassandraAppenderTest.xml | 2 +- .../log4j/couchdb/CouchDbConnection.java | 69 ++++ .../logging/log4j/couchdb/CouchDbProvider.java | 161 +++++++++ .../logging/log4j/couchdb/package-info.java | 20 ++ .../appender/couchdb/CouchDbConnection.java | 69 ---- .../nosql/appender/couchdb/CouchDbProvider.java | 161 --------- .../nosql/appender/couchdb/package-info.java | 20 -- .../log4j/mongodb/MongoDbConnection.java | 96 +++++ .../logging/log4j/mongodb/MongoDbObject.java | 66 ++++ .../logging/log4j/mongodb/MongoDbProvider.java | 351 +++++++++++++++++++ .../logging/log4j/mongodb/package-info.java | 20 ++ .../appender/mongodb/MongoDbConnection.java | 96 ----- .../nosql/appender/mongodb/MongoDbObject.java | 66 ---- .../nosql/appender/mongodb/MongoDbProvider.java | 351 ------------------- .../nosql/appender/mongodb/package-info.java | 20 -- .../logging/log4j/mongodb/MongoDbAuthTest.java | 40 +++ .../log4j/mongodb/MongoDbCappedTest.java | 40 +++ .../logging/log4j/mongodb/MongoDbTest.java | 40 +++ .../nosql/appender/mongodb/MongoDbAuthTest.java | 40 --- .../appender/mongodb/MongoDbCappedTest.java | 40 --- .../nosql/appender/mongodb/MongoDbTest.java | 40 --- src/site/xdoc/manual/appenders.xml | 2 +- 34 files changed, 1602 insertions(+), 1602 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/CassandraAppender.java ---------------------------------------------------------------------- diff --git a/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/CassandraAppender.java b/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/CassandraAppender.java new file mode 100644 index 0000000..7a9ade9 --- /dev/null +++ b/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/CassandraAppender.java @@ -0,0 +1,185 @@ +/* + * 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.cassandra; + +import com.datastax.driver.core.BatchStatement; +import org.apache.logging.log4j.core.Core; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.appender.db.AbstractDatabaseAppender; +import org.apache.logging.log4j.core.appender.db.ColumnMapping; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; +import org.apache.logging.log4j.core.net.SocketAddress; +import org.apache.logging.log4j.core.util.Clock; + +/** + * Appender plugin that uses a Cassandra database. + * + * @see SocketAddress + * @see ColumnMapping + */ +@Plugin(name = "Cassandra", category = Core.CATEGORY_NAME, elementType = CassandraAppender.ELEMENT_TYPE, printObject = true) +public class CassandraAppender extends AbstractDatabaseAppender<CassandraManager> { + + private CassandraAppender(final String name, final Filter filter, final boolean ignoreExceptions, + final CassandraManager manager) { + super(name, filter, ignoreExceptions, manager); + } + + @PluginBuilderFactory + public static <B extends Builder<B>> B newBuilder() { + return new Builder<B>().asBuilder(); + } + + public static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B> + implements org.apache.logging.log4j.core.util.Builder<CassandraAppender> { + + /** + * List of Cassandra node contact points. Addresses without a port (or port set to 0) will use the default + * Cassandra port (9042). + */ + @PluginElement("ContactPoints") + @Required(message = "No Cassandra servers provided") + private SocketAddress[] contactPoints = new SocketAddress[]{SocketAddress.getLoopback()}; + + /** + * List of column mappings to convert a LogEvent into a database row. + */ + @PluginElement("Columns") + @Required(message = "No Cassandra columns provided") + private ColumnMapping[] columns; + + @PluginBuilderAttribute + private boolean useTls; + + @PluginBuilderAttribute + @Required(message = "No cluster name provided") + private String clusterName; + + @PluginBuilderAttribute + @Required(message = "No keyspace provided") + private String keyspace; + + @PluginBuilderAttribute + @Required(message = "No table name provided") + private String table; + + @PluginBuilderAttribute + private String username; + + @PluginBuilderAttribute(sensitive = true) + private String password; + + /** + * Override the default TimestampGenerator with one based on the configured {@link Clock}. + */ + @PluginBuilderAttribute + private boolean useClockForTimestampGenerator; + + /** + * Number of LogEvents to buffer before writing. Can be used with or without batch statements. + */ + @PluginBuilderAttribute + private int bufferSize; + + /** + * Whether or not to use batch statements when inserting records. + */ + @PluginBuilderAttribute + private boolean batched; + + /** + * If batch statements are enabled, use this type of batch statement. + */ + @PluginBuilderAttribute + private BatchStatement.Type batchType = BatchStatement.Type.LOGGED; + + public B setContactPoints(final SocketAddress... contactPoints) { + this.contactPoints = contactPoints; + return asBuilder(); + } + + public B setColumns(final ColumnMapping... columns) { + this.columns = columns; + return asBuilder(); + } + + public B setUseTls(final boolean useTls) { + this.useTls = useTls; + return asBuilder(); + } + + public B setClusterName(final String clusterName) { + this.clusterName = clusterName; + return asBuilder(); + } + + public B setKeyspace(final String keyspace) { + this.keyspace = keyspace; + return asBuilder(); + } + + public B setTable(final String table) { + this.table = table; + return asBuilder(); + } + + public B setUsername(final String username) { + this.username = username; + return asBuilder(); + } + + public B setPassword(final String password) { + this.password = password; + return asBuilder(); + } + + public B setUseClockForTimestampGenerator(final boolean useClockForTimestampGenerator) { + this.useClockForTimestampGenerator = useClockForTimestampGenerator; + return asBuilder(); + } + + public B setBufferSize(final int bufferSize) { + this.bufferSize = bufferSize; + return asBuilder(); + } + + public B setBatched(final boolean batched) { + this.batched = batched; + return asBuilder(); + } + + public B setBatchType(final BatchStatement.Type batchType) { + this.batchType = batchType; + return asBuilder(); + } + + @Override + public CassandraAppender build() { + final CassandraManager manager = CassandraManager.getManager(getName(), contactPoints, columns, useTls, + clusterName, keyspace, table, username, password, useClockForTimestampGenerator, bufferSize, batched, + batchType); + return new CassandraAppender(getName(), getFilter(), isIgnoreExceptions(), manager); + } + + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/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 new file mode 100644 index 0000000..5ca82d3 --- /dev/null +++ b/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/CassandraManager.java @@ -0,0 +1,218 @@ +/* + * 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.cassandra; + +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import com.datastax.driver.core.BatchStatement; +import com.datastax.driver.core.BoundStatement; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.Session; +import org.apache.logging.log4j.core.LogEvent; +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.config.plugins.convert.DateTypeConverter; +import org.apache.logging.log4j.core.config.plugins.convert.TypeConverters; +import org.apache.logging.log4j.core.net.SocketAddress; +import org.apache.logging.log4j.spi.ThreadContextMap; +import org.apache.logging.log4j.spi.ThreadContextStack; +import org.apache.logging.log4j.util.ReadOnlyStringMap; +import org.apache.logging.log4j.util.Strings; + +/** + * Manager for a Cassandra appender instance. + */ +public class CassandraManager extends AbstractDatabaseManager { + + private static final int DEFAULT_PORT = 9042; + + private final Cluster cluster; + private final String keyspace; + private final String insertQueryTemplate; + private final List<ColumnMapping> columnMappings; + private final BatchStatement batchStatement; + // re-usable argument binding array + private final Object[] values; + + private Session session; + private PreparedStatement preparedStatement; + + private CassandraManager(final String name, final int bufferSize, final Cluster cluster, + final String keyspace, final String insertQueryTemplate, + final List<ColumnMapping> columnMappings, final BatchStatement batchStatement) { + super(name, bufferSize); + this.cluster = cluster; + this.keyspace = keyspace; + this.insertQueryTemplate = insertQueryTemplate; + this.columnMappings = columnMappings; + this.batchStatement = batchStatement; + this.values = new Object[columnMappings.size()]; + } + + @Override + protected void startupInternal() throws Exception { + session = cluster.connect(keyspace); + preparedStatement = session.prepare(insertQueryTemplate); + } + + @Override + protected boolean shutdownInternal() throws Exception { + session.close(); + cluster.close(); + return true; + } + + @Override + protected void connectAndStart() { + // a Session automatically manages connections for us + } + + @Override + protected void writeInternal(final LogEvent event) { + for (int i = 0; i < columnMappings.size(); i++) { + final ColumnMapping columnMapping = columnMappings.get(i); + if (ThreadContextMap.class.isAssignableFrom(columnMapping.getType()) + || ReadOnlyStringMap.class.isAssignableFrom(columnMapping.getType())) { + values[i] = event.getContextData().toMap(); + } else if (ThreadContextStack.class.isAssignableFrom(columnMapping.getType())) { + values[i] = event.getContextStack().asList(); + } else if (Date.class.isAssignableFrom(columnMapping.getType())) { + values[i] = DateTypeConverter.fromMillis(event.getTimeMillis(), columnMapping.getType().asSubclass(Date.class)); + } else { + values[i] = TypeConverters.convert(columnMapping.getLayout().toSerializable(event), + columnMapping.getType(), null); + } + } + final BoundStatement boundStatement = preparedStatement.bind(values); + if (batchStatement == null) { + session.execute(boundStatement); + } else { + batchStatement.add(boundStatement); + } + } + + @Override + protected boolean commitAndClose() { + if (batchStatement != null) { + session.execute(batchStatement); + } + return true; + } + + public static CassandraManager getManager(final String name, final SocketAddress[] contactPoints, + final ColumnMapping[] columns, final boolean useTls, + 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) { + return getManager(name, + new FactoryData(contactPoints, columns, useTls, clusterName, keyspace, table, username, password, + useClockForTimestampGenerator, bufferSize, batched, batchType), CassandraManagerFactory.INSTANCE); + } + + private static class CassandraManagerFactory implements ManagerFactory<CassandraManager, FactoryData> { + + private static final CassandraManagerFactory INSTANCE = new CassandraManagerFactory(); + + @Override + public CassandraManager createManager(final String name, final FactoryData data) { + final Cluster.Builder builder = Cluster.builder() + .addContactPointsWithPorts(data.contactPoints) + .withClusterName(data.clusterName); + if (data.useTls) { + builder.withSSL(); + } + if (Strings.isNotBlank(data.username)) { + builder.withCredentials(data.username, data.password); + } + if (data.useClockForTimestampGenerator) { + builder.withTimestampGenerator(new ClockTimestampGenerator()); + } + final Cluster cluster = builder.build(); + + final StringBuilder sb = new StringBuilder("INSERT INTO ").append(data.table).append(" ("); + for (final ColumnMapping column : data.columns) { + sb.append(column.getName()).append(','); + } + sb.setCharAt(sb.length() - 1, ')'); + sb.append(" VALUES ("); + final List<ColumnMapping> columnMappings = new ArrayList<>(data.columns.length); + for (final ColumnMapping column : data.columns) { + if (Strings.isNotEmpty(column.getLiteralValue())) { + sb.append(column.getLiteralValue()); + } else { + sb.append('?'); + columnMappings.add(column); + } + sb.append(','); + } + sb.setCharAt(sb.length() - 1, ')'); + final String insertQueryTemplate = sb.toString(); + LOGGER.debug("Using CQL for appender {}: {}", name, insertQueryTemplate); + return new CassandraManager(name, data.getBufferSize(), cluster, data.keyspace, insertQueryTemplate, + columnMappings, data.batched ? new BatchStatement(data.batchType) : null); + } + } + + private static class FactoryData extends AbstractFactoryData { + private final InetSocketAddress[] contactPoints; + private final ColumnMapping[] columns; + private final boolean useTls; + private final String clusterName; + private final String keyspace; + private final String table; + private final String username; + private final String password; + private final boolean useClockForTimestampGenerator; + private final boolean batched; + private final BatchStatement.Type batchType; + + private FactoryData(final SocketAddress[] contactPoints, final ColumnMapping[] columns, final boolean useTls, + 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); + this.contactPoints = convertAndAddDefaultPorts(contactPoints); + this.columns = columns; + this.useTls = useTls; + this.clusterName = clusterName; + this.keyspace = keyspace; + this.table = table; + this.username = username; + this.password = password; + this.useClockForTimestampGenerator = useClockForTimestampGenerator; + this.batched = batched; + this.batchType = batchType; + } + + private static InetSocketAddress[] convertAndAddDefaultPorts(final SocketAddress... socketAddresses) { + final InetSocketAddress[] inetSocketAddresses = new InetSocketAddress[socketAddresses.length]; + for (int i = 0; i < inetSocketAddresses.length; i++) { + final SocketAddress socketAddress = socketAddresses[i]; + inetSocketAddresses[i] = socketAddress.getPort() == 0 + ? new InetSocketAddress(socketAddress.getAddress(), DEFAULT_PORT) + : socketAddress.getSocketAddress(); + } + return inetSocketAddresses; + } + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/ClockTimestampGenerator.java ---------------------------------------------------------------------- diff --git a/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/ClockTimestampGenerator.java b/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/ClockTimestampGenerator.java new file mode 100644 index 0000000..1c7328b --- /dev/null +++ b/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/ClockTimestampGenerator.java @@ -0,0 +1,34 @@ +/* + * 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.cassandra; + +import com.datastax.driver.core.TimestampGenerator; +import org.apache.logging.log4j.core.util.Clock; +import org.apache.logging.log4j.core.util.ClockFactory; + +/** + * A {@link TimestampGenerator} implementation using the configured {@link Clock}. + */ +public class ClockTimestampGenerator implements TimestampGenerator { + + private final Clock clock = ClockFactory.getClock(); + + @Override + public long next() { + return clock.currentTimeMillis(); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/package-info.java ---------------------------------------------------------------------- diff --git a/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/package-info.java b/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/package-info.java new file mode 100644 index 0000000..0336dd0 --- /dev/null +++ b/log4j-cassandra/src/main/java/org/apache/logging/log4j/cassandra/package-info.java @@ -0,0 +1,23 @@ +/* + * 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. + */ +/** + * Log4j appender plugin and supporting classes for Apache Cassandra. + * + * @see <a href="https://logging.apache.org/log4j/2.x/manual/appenders.html#CassandraAppender">Cassandra Appender manual</a> + * @since 2.8 + */ +package org.apache.logging.log4j.cassandra; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/log4j-cassandra/src/main/java/org/apache/logging/log4j/nosql/appender/cassandra/CassandraAppender.java ---------------------------------------------------------------------- diff --git a/log4j-cassandra/src/main/java/org/apache/logging/log4j/nosql/appender/cassandra/CassandraAppender.java b/log4j-cassandra/src/main/java/org/apache/logging/log4j/nosql/appender/cassandra/CassandraAppender.java deleted file mode 100644 index fb344c2..0000000 --- a/log4j-cassandra/src/main/java/org/apache/logging/log4j/nosql/appender/cassandra/CassandraAppender.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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.nosql.appender.cassandra; - -import com.datastax.driver.core.BatchStatement; -import org.apache.logging.log4j.core.Core; -import org.apache.logging.log4j.core.Filter; -import org.apache.logging.log4j.core.appender.AbstractAppender; -import org.apache.logging.log4j.core.appender.db.AbstractDatabaseAppender; -import org.apache.logging.log4j.core.appender.db.ColumnMapping; -import org.apache.logging.log4j.core.config.plugins.Plugin; -import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; -import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; -import org.apache.logging.log4j.core.config.plugins.PluginElement; -import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; -import org.apache.logging.log4j.core.net.SocketAddress; -import org.apache.logging.log4j.core.util.Clock; - -/** - * Appender plugin that uses a Cassandra database. - * - * @see SocketAddress - * @see ColumnMapping - */ -@Plugin(name = "Cassandra", category = Core.CATEGORY_NAME, elementType = CassandraAppender.ELEMENT_TYPE, printObject = true) -public class CassandraAppender extends AbstractDatabaseAppender<CassandraManager> { - - private CassandraAppender(final String name, final Filter filter, final boolean ignoreExceptions, - final CassandraManager manager) { - super(name, filter, ignoreExceptions, manager); - } - - @PluginBuilderFactory - public static <B extends Builder<B>> B newBuilder() { - return new Builder<B>().asBuilder(); - } - - public static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B> - implements org.apache.logging.log4j.core.util.Builder<CassandraAppender> { - - /** - * List of Cassandra node contact points. Addresses without a port (or port set to 0) will use the default - * Cassandra port (9042). - */ - @PluginElement("ContactPoints") - @Required(message = "No Cassandra servers provided") - private SocketAddress[] contactPoints = new SocketAddress[]{SocketAddress.getLoopback()}; - - /** - * List of column mappings to convert a LogEvent into a database row. - */ - @PluginElement("Columns") - @Required(message = "No Cassandra columns provided") - private ColumnMapping[] columns; - - @PluginBuilderAttribute - private boolean useTls; - - @PluginBuilderAttribute - @Required(message = "No cluster name provided") - private String clusterName; - - @PluginBuilderAttribute - @Required(message = "No keyspace provided") - private String keyspace; - - @PluginBuilderAttribute - @Required(message = "No table name provided") - private String table; - - @PluginBuilderAttribute - private String username; - - @PluginBuilderAttribute(sensitive = true) - private String password; - - /** - * Override the default TimestampGenerator with one based on the configured {@link Clock}. - */ - @PluginBuilderAttribute - private boolean useClockForTimestampGenerator; - - /** - * Number of LogEvents to buffer before writing. Can be used with or without batch statements. - */ - @PluginBuilderAttribute - private int bufferSize; - - /** - * Whether or not to use batch statements when inserting records. - */ - @PluginBuilderAttribute - private boolean batched; - - /** - * If batch statements are enabled, use this type of batch statement. - */ - @PluginBuilderAttribute - private BatchStatement.Type batchType = BatchStatement.Type.LOGGED; - - public B setContactPoints(final SocketAddress... contactPoints) { - this.contactPoints = contactPoints; - return asBuilder(); - } - - public B setColumns(final ColumnMapping... columns) { - this.columns = columns; - return asBuilder(); - } - - public B setUseTls(final boolean useTls) { - this.useTls = useTls; - return asBuilder(); - } - - public B setClusterName(final String clusterName) { - this.clusterName = clusterName; - return asBuilder(); - } - - public B setKeyspace(final String keyspace) { - this.keyspace = keyspace; - return asBuilder(); - } - - public B setTable(final String table) { - this.table = table; - return asBuilder(); - } - - public B setUsername(final String username) { - this.username = username; - return asBuilder(); - } - - public B setPassword(final String password) { - this.password = password; - return asBuilder(); - } - - public B setUseClockForTimestampGenerator(final boolean useClockForTimestampGenerator) { - this.useClockForTimestampGenerator = useClockForTimestampGenerator; - return asBuilder(); - } - - public B setBufferSize(final int bufferSize) { - this.bufferSize = bufferSize; - return asBuilder(); - } - - public B setBatched(final boolean batched) { - this.batched = batched; - return asBuilder(); - } - - public B setBatchType(final BatchStatement.Type batchType) { - this.batchType = batchType; - return asBuilder(); - } - - @Override - public CassandraAppender build() { - final CassandraManager manager = CassandraManager.getManager(getName(), contactPoints, columns, useTls, - clusterName, keyspace, table, username, password, useClockForTimestampGenerator, bufferSize, batched, - batchType); - return new CassandraAppender(getName(), getFilter(), isIgnoreExceptions(), manager); - } - - } - -} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/log4j-cassandra/src/main/java/org/apache/logging/log4j/nosql/appender/cassandra/CassandraManager.java ---------------------------------------------------------------------- diff --git a/log4j-cassandra/src/main/java/org/apache/logging/log4j/nosql/appender/cassandra/CassandraManager.java b/log4j-cassandra/src/main/java/org/apache/logging/log4j/nosql/appender/cassandra/CassandraManager.java deleted file mode 100644 index cfbc528..0000000 --- a/log4j-cassandra/src/main/java/org/apache/logging/log4j/nosql/appender/cassandra/CassandraManager.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * 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.nosql.appender.cassandra; - -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import com.datastax.driver.core.BatchStatement; -import com.datastax.driver.core.BoundStatement; -import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.PreparedStatement; -import com.datastax.driver.core.Session; -import org.apache.logging.log4j.core.LogEvent; -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.config.plugins.convert.DateTypeConverter; -import org.apache.logging.log4j.core.config.plugins.convert.TypeConverters; -import org.apache.logging.log4j.core.net.SocketAddress; -import org.apache.logging.log4j.spi.ThreadContextMap; -import org.apache.logging.log4j.spi.ThreadContextStack; -import org.apache.logging.log4j.util.ReadOnlyStringMap; -import org.apache.logging.log4j.util.Strings; - -/** - * Manager for a Cassandra appender instance. - */ -public class CassandraManager extends AbstractDatabaseManager { - - private static final int DEFAULT_PORT = 9042; - - private final Cluster cluster; - private final String keyspace; - private final String insertQueryTemplate; - private final List<ColumnMapping> columnMappings; - private final BatchStatement batchStatement; - // re-usable argument binding array - private final Object[] values; - - private Session session; - private PreparedStatement preparedStatement; - - private CassandraManager(final String name, final int bufferSize, final Cluster cluster, - final String keyspace, final String insertQueryTemplate, - final List<ColumnMapping> columnMappings, final BatchStatement batchStatement) { - super(name, bufferSize); - this.cluster = cluster; - this.keyspace = keyspace; - this.insertQueryTemplate = insertQueryTemplate; - this.columnMappings = columnMappings; - this.batchStatement = batchStatement; - this.values = new Object[columnMappings.size()]; - } - - @Override - protected void startupInternal() throws Exception { - session = cluster.connect(keyspace); - preparedStatement = session.prepare(insertQueryTemplate); - } - - @Override - protected boolean shutdownInternal() throws Exception { - session.close(); - cluster.close(); - return true; - } - - @Override - protected void connectAndStart() { - // a Session automatically manages connections for us - } - - @Override - protected void writeInternal(final LogEvent event) { - for (int i = 0; i < columnMappings.size(); i++) { - final ColumnMapping columnMapping = columnMappings.get(i); - if (ThreadContextMap.class.isAssignableFrom(columnMapping.getType()) - || ReadOnlyStringMap.class.isAssignableFrom(columnMapping.getType())) { - values[i] = event.getContextData().toMap(); - } else if (ThreadContextStack.class.isAssignableFrom(columnMapping.getType())) { - values[i] = event.getContextStack().asList(); - } else if (Date.class.isAssignableFrom(columnMapping.getType())) { - values[i] = DateTypeConverter.fromMillis(event.getTimeMillis(), columnMapping.getType().asSubclass(Date.class)); - } else { - values[i] = TypeConverters.convert(columnMapping.getLayout().toSerializable(event), - columnMapping.getType(), null); - } - } - final BoundStatement boundStatement = preparedStatement.bind(values); - if (batchStatement == null) { - session.execute(boundStatement); - } else { - batchStatement.add(boundStatement); - } - } - - @Override - protected boolean commitAndClose() { - if (batchStatement != null) { - session.execute(batchStatement); - } - return true; - } - - public static CassandraManager getManager(final String name, final SocketAddress[] contactPoints, - final ColumnMapping[] columns, final boolean useTls, - 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) { - return getManager(name, - new FactoryData(contactPoints, columns, useTls, clusterName, keyspace, table, username, password, - useClockForTimestampGenerator, bufferSize, batched, batchType), CassandraManagerFactory.INSTANCE); - } - - private static class CassandraManagerFactory implements ManagerFactory<CassandraManager, FactoryData> { - - private static final CassandraManagerFactory INSTANCE = new CassandraManagerFactory(); - - @Override - public CassandraManager createManager(final String name, final FactoryData data) { - final Cluster.Builder builder = Cluster.builder() - .addContactPointsWithPorts(data.contactPoints) - .withClusterName(data.clusterName); - if (data.useTls) { - builder.withSSL(); - } - if (Strings.isNotBlank(data.username)) { - builder.withCredentials(data.username, data.password); - } - if (data.useClockForTimestampGenerator) { - builder.withTimestampGenerator(new ClockTimestampGenerator()); - } - final Cluster cluster = builder.build(); - - final StringBuilder sb = new StringBuilder("INSERT INTO ").append(data.table).append(" ("); - for (final ColumnMapping column : data.columns) { - sb.append(column.getName()).append(','); - } - sb.setCharAt(sb.length() - 1, ')'); - sb.append(" VALUES ("); - final List<ColumnMapping> columnMappings = new ArrayList<>(data.columns.length); - for (final ColumnMapping column : data.columns) { - if (Strings.isNotEmpty(column.getLiteralValue())) { - sb.append(column.getLiteralValue()); - } else { - sb.append('?'); - columnMappings.add(column); - } - sb.append(','); - } - sb.setCharAt(sb.length() - 1, ')'); - final String insertQueryTemplate = sb.toString(); - LOGGER.debug("Using CQL for appender {}: {}", name, insertQueryTemplate); - return new CassandraManager(name, data.getBufferSize(), cluster, data.keyspace, insertQueryTemplate, - columnMappings, data.batched ? new BatchStatement(data.batchType) : null); - } - } - - private static class FactoryData extends AbstractFactoryData { - private final InetSocketAddress[] contactPoints; - private final ColumnMapping[] columns; - private final boolean useTls; - private final String clusterName; - private final String keyspace; - private final String table; - private final String username; - private final String password; - private final boolean useClockForTimestampGenerator; - private final boolean batched; - private final BatchStatement.Type batchType; - - private FactoryData(final SocketAddress[] contactPoints, final ColumnMapping[] columns, final boolean useTls, - 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); - this.contactPoints = convertAndAddDefaultPorts(contactPoints); - this.columns = columns; - this.useTls = useTls; - this.clusterName = clusterName; - this.keyspace = keyspace; - this.table = table; - this.username = username; - this.password = password; - this.useClockForTimestampGenerator = useClockForTimestampGenerator; - this.batched = batched; - this.batchType = batchType; - } - - private static InetSocketAddress[] convertAndAddDefaultPorts(final SocketAddress... socketAddresses) { - final InetSocketAddress[] inetSocketAddresses = new InetSocketAddress[socketAddresses.length]; - for (int i = 0; i < inetSocketAddresses.length; i++) { - final SocketAddress socketAddress = socketAddresses[i]; - inetSocketAddresses[i] = socketAddress.getPort() == 0 - ? new InetSocketAddress(socketAddress.getAddress(), DEFAULT_PORT) - : socketAddress.getSocketAddress(); - } - return inetSocketAddresses; - } - } -} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/log4j-cassandra/src/main/java/org/apache/logging/log4j/nosql/appender/cassandra/ClockTimestampGenerator.java ---------------------------------------------------------------------- diff --git a/log4j-cassandra/src/main/java/org/apache/logging/log4j/nosql/appender/cassandra/ClockTimestampGenerator.java b/log4j-cassandra/src/main/java/org/apache/logging/log4j/nosql/appender/cassandra/ClockTimestampGenerator.java deleted file mode 100644 index 06758dd..0000000 --- a/log4j-cassandra/src/main/java/org/apache/logging/log4j/nosql/appender/cassandra/ClockTimestampGenerator.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.nosql.appender.cassandra; - -import com.datastax.driver.core.TimestampGenerator; -import org.apache.logging.log4j.core.util.Clock; -import org.apache.logging.log4j.core.util.ClockFactory; - -/** - * A {@link TimestampGenerator} implementation using the configured {@link Clock}. - */ -public class ClockTimestampGenerator implements TimestampGenerator { - - private final Clock clock = ClockFactory.getClock(); - - @Override - public long next() { - return clock.currentTimeMillis(); - } -} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/log4j-cassandra/src/main/java/org/apache/logging/log4j/nosql/appender/cassandra/package-info.java ---------------------------------------------------------------------- diff --git a/log4j-cassandra/src/main/java/org/apache/logging/log4j/nosql/appender/cassandra/package-info.java b/log4j-cassandra/src/main/java/org/apache/logging/log4j/nosql/appender/cassandra/package-info.java deleted file mode 100644 index 79107c9..0000000 --- a/log4j-cassandra/src/main/java/org/apache/logging/log4j/nosql/appender/cassandra/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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. - */ -/** - * Log4j appender plugin and supporting classes for Apache Cassandra. - * - * @see <a href="https://logging.apache.org/log4j/2.x/manual/appenders.html#CassandraAppender">Cassandra Appender manual</a> - * @since 2.8 - */ -package org.apache.logging.log4j.nosql.appender.cassandra; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/log4j-cassandra/src/test/java/org/apache/logging/log4j/cassandra/CassandraAppenderIT.java ---------------------------------------------------------------------- diff --git a/log4j-cassandra/src/test/java/org/apache/logging/log4j/cassandra/CassandraAppenderIT.java b/log4j-cassandra/src/test/java/org/apache/logging/log4j/cassandra/CassandraAppenderIT.java new file mode 100644 index 0000000..9cd0794 --- /dev/null +++ b/log4j-cassandra/src/test/java/org/apache/logging/log4j/cassandra/CassandraAppenderIT.java @@ -0,0 +1,96 @@ +/* + * 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.cassandra; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.MarkerManager; +import org.apache.logging.log4j.ThreadContext; +import org.apache.logging.log4j.categories.Appenders; +import org.apache.logging.log4j.junit.LoggerContextRule; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.RuleChain; + +import static org.junit.Assert.*; + +/** + * Integration test for CassandraAppender. + */ +@Category(Appenders.Cassandra.class) +public class CassandraAppenderIT { + + private static final String DDL = "CREATE TABLE logs (" + + "id timeuuid PRIMARY KEY," + + "timeid timeuuid," + + "message text," + + "level text," + + "marker text," + + "logger text," + + "timestamp timestamp," + + "mdc map<text,text>," + + "ndc list<text>" + + ")"; + + private static final LoggerContextRule CTX = new LoggerContextRule("CassandraAppenderTest.xml"); + private static final CassandraRule CASSANDRA = new CassandraRule("test", DDL); + + @ClassRule + public static RuleChain rules = RuleChain.outerRule(CASSANDRA).around(CTX); + + @Test + public void appendManyEvents() throws Exception { + final Logger logger = CTX.getLogger(); + ThreadContext.put("test", "mdc"); + ThreadContext.push("ndc"); + for (int i = 0; i < 20; i++) { + logger.info(MarkerManager.getMarker("MARKER"), "Test log message"); + } + ThreadContext.clearAll(); + + TimeUnit.SECONDS.sleep(3); + + int i = 0; + try (final Session session = CASSANDRA.connect()) { + for (final Row row : session.execute("SELECT * FROM logs")) { + assertNotNull(row.get("id", UUID.class)); + assertNotNull(row.get("timeid", UUID.class)); + assertNotNull(row.get("timestamp", Date.class)); + assertEquals("Test log message", row.getString("message")); + assertEquals("MARKER", row.getString("marker")); + assertEquals("INFO", row.getString("level")); + assertEquals(getClass().getName(), row.getString("logger")); + final Map<String, String> mdc = row.getMap("mdc", String.class, String.class); + assertEquals(1, mdc.size()); + assertEquals("mdc", mdc.get("test")); + final List<String> ndc = row.getList("ndc", String.class); + assertEquals(1, ndc.size()); + assertEquals("ndc", ndc.get(0)); + ++i; + } + } + assertEquals(20, i); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/log4j-cassandra/src/test/java/org/apache/logging/log4j/cassandra/CassandraRule.java ---------------------------------------------------------------------- diff --git a/log4j-cassandra/src/test/java/org/apache/logging/log4j/cassandra/CassandraRule.java b/log4j-cassandra/src/test/java/org/apache/logging/log4j/cassandra/CassandraRule.java new file mode 100644 index 0000000..2939d07 --- /dev/null +++ b/log4j-cassandra/src/test/java/org/apache/logging/log4j/cassandra/CassandraRule.java @@ -0,0 +1,141 @@ +/* + * 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.cassandra; + +import java.io.IOException; +import java.net.InetAddress; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.Permission; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadFactory; + +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.Session; +import org.apache.cassandra.service.CassandraDaemon; +import org.apache.logging.log4j.LoggingException; +import org.apache.logging.log4j.core.util.Cancellable; +import org.apache.logging.log4j.core.util.Closer; +import org.apache.logging.log4j.core.util.Log4jThreadFactory; +import org.apache.logging.log4j.util.PropertiesUtil; +import org.junit.rules.ExternalResource; + +/** + * JUnit rule to set up and tear down a Cassandra database instance. + */ +public class CassandraRule extends ExternalResource { + + private static final ThreadFactory THREAD_FACTORY = Log4jThreadFactory.createThreadFactory("Cassandra"); + + private final CountDownLatch latch = new CountDownLatch(1); + private final Cancellable embeddedCassandra = new EmbeddedCassandra(latch); + private final String keyspace; + private final String tableDdl; + private Cluster cluster; + + public CassandraRule(final String keyspace, final String tableDdl) { + this.keyspace = keyspace; + this.tableDdl = tableDdl; + } + + public Cluster getCluster() { + return cluster; + } + + public Session connect() { + return cluster.connect(keyspace); + } + + @Override + protected void before() throws Throwable { + final Path root = Files.createTempDirectory("cassandra"); + Files.createDirectories(root.resolve("data")); + final Path config = root.resolve("cassandra.yml"); + Files.copy(getClass().getResourceAsStream("/cassandra.yaml"), config); + System.setProperty("cassandra.config", "file:" + config.toString()); + System.setProperty("cassandra.storagedir", root.toString()); + System.setProperty("cassandra-foreground", "true"); // prevents Cassandra from closing stdout/stderr + THREAD_FACTORY.newThread(embeddedCassandra).start(); + latch.await(); + cluster = Cluster.builder().addContactPoints(InetAddress.getLoopbackAddress()).build(); + try (final Session session = cluster.connect()) { + session.execute("CREATE KEYSPACE " + keyspace + " WITH REPLICATION = " + + "{ 'class': 'SimpleStrategy', 'replication_factor': 2 };"); + } + try (final Session session = connect()) { + session.execute(tableDdl); + } + } + + @Override + protected void after() { + Closer.closeSilently(cluster); + embeddedCassandra.cancel(); + } + + private static class EmbeddedCassandra implements Cancellable { + + private final CassandraDaemon daemon = new CassandraDaemon(); + private final CountDownLatch latch; + + private EmbeddedCassandra(final CountDownLatch latch) { + this.latch = latch; + } + + @Override + public void cancel() { + // LOG4J2-1850 Cassandra on Windows calls System.exit in the daemon stop method + if (PropertiesUtil.getProperties().isOsWindows()) { + cancelOnWindows(); + } else { + daemon.stop(); + } + } + + private void cancelOnWindows() { + final SecurityManager currentSecurityManager = System.getSecurityManager(); + try { + final SecurityManager securityManager = new SecurityManager() { + @Override + public void checkPermission(final Permission permission) { + final String permissionName = permission.getName(); + if (permissionName != null && permissionName.startsWith("exitVM")) { + throw new SecurityException("test"); + } + } + }; + System.setSecurityManager(securityManager); + daemon.stop(); + } catch (final SecurityException ex) { + // ignore + } finally { + System.setSecurityManager(currentSecurityManager); + } + } + + @Override + public void run() { + try { + daemon.init(null); + } catch (final IOException e) { + throw new LoggingException("Cannot initialize embedded Cassandra instance", e); + } + daemon.start(); + latch.countDown(); + } + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/log4j-cassandra/src/test/java/org/apache/logging/log4j/nosql/appender/cassandra/CassandraAppenderIT.java ---------------------------------------------------------------------- diff --git a/log4j-cassandra/src/test/java/org/apache/logging/log4j/nosql/appender/cassandra/CassandraAppenderIT.java b/log4j-cassandra/src/test/java/org/apache/logging/log4j/nosql/appender/cassandra/CassandraAppenderIT.java deleted file mode 100644 index caf72d5..0000000 --- a/log4j-cassandra/src/test/java/org/apache/logging/log4j/nosql/appender/cassandra/CassandraAppenderIT.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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.nosql.appender.cassandra; - -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -import com.datastax.driver.core.Row; -import com.datastax.driver.core.Session; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.MarkerManager; -import org.apache.logging.log4j.ThreadContext; -import org.apache.logging.log4j.categories.Appenders; -import org.apache.logging.log4j.junit.LoggerContextRule; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.RuleChain; - -import static org.junit.Assert.*; - -/** - * Integration test for CassandraAppender. - */ -@Category(Appenders.Cassandra.class) -public class CassandraAppenderIT { - - private static final String DDL = "CREATE TABLE logs (" + - "id timeuuid PRIMARY KEY," + - "timeid timeuuid," + - "message text," + - "level text," + - "marker text," + - "logger text," + - "timestamp timestamp," + - "mdc map<text,text>," + - "ndc list<text>" + - ")"; - - private static final LoggerContextRule CTX = new LoggerContextRule("CassandraAppenderTest.xml"); - private static final CassandraRule CASSANDRA = new CassandraRule("test", DDL); - - @ClassRule - public static RuleChain rules = RuleChain.outerRule(CASSANDRA).around(CTX); - - @Test - public void appendManyEvents() throws Exception { - final Logger logger = CTX.getLogger(); - ThreadContext.put("test", "mdc"); - ThreadContext.push("ndc"); - for (int i = 0; i < 20; i++) { - logger.info(MarkerManager.getMarker("MARKER"), "Test log message"); - } - ThreadContext.clearAll(); - - TimeUnit.SECONDS.sleep(3); - - int i = 0; - try (final Session session = CASSANDRA.connect()) { - for (final Row row : session.execute("SELECT * FROM logs")) { - assertNotNull(row.get("id", UUID.class)); - assertNotNull(row.get("timeid", UUID.class)); - assertNotNull(row.get("timestamp", Date.class)); - assertEquals("Test log message", row.getString("message")); - assertEquals("MARKER", row.getString("marker")); - assertEquals("INFO", row.getString("level")); - assertEquals(getClass().getName(), row.getString("logger")); - final Map<String, String> mdc = row.getMap("mdc", String.class, String.class); - assertEquals(1, mdc.size()); - assertEquals("mdc", mdc.get("test")); - final List<String> ndc = row.getList("ndc", String.class); - assertEquals(1, ndc.size()); - assertEquals("ndc", ndc.get(0)); - ++i; - } - } - assertEquals(20, i); - } -} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/log4j-cassandra/src/test/java/org/apache/logging/log4j/nosql/appender/cassandra/CassandraRule.java ---------------------------------------------------------------------- diff --git a/log4j-cassandra/src/test/java/org/apache/logging/log4j/nosql/appender/cassandra/CassandraRule.java b/log4j-cassandra/src/test/java/org/apache/logging/log4j/nosql/appender/cassandra/CassandraRule.java deleted file mode 100644 index 36e2672..0000000 --- a/log4j-cassandra/src/test/java/org/apache/logging/log4j/nosql/appender/cassandra/CassandraRule.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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.nosql.appender.cassandra; - -import java.io.IOException; -import java.net.InetAddress; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.Permission; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ThreadFactory; - -import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.Session; -import org.apache.cassandra.service.CassandraDaemon; -import org.apache.logging.log4j.LoggingException; -import org.apache.logging.log4j.core.util.Cancellable; -import org.apache.logging.log4j.core.util.Closer; -import org.apache.logging.log4j.core.util.Log4jThreadFactory; -import org.apache.logging.log4j.util.PropertiesUtil; -import org.junit.rules.ExternalResource; - -/** - * JUnit rule to set up and tear down a Cassandra database instance. - */ -public class CassandraRule extends ExternalResource { - - private static final ThreadFactory THREAD_FACTORY = Log4jThreadFactory.createThreadFactory("Cassandra"); - - private final CountDownLatch latch = new CountDownLatch(1); - private final Cancellable embeddedCassandra = new EmbeddedCassandra(latch); - private final String keyspace; - private final String tableDdl; - private Cluster cluster; - - public CassandraRule(final String keyspace, final String tableDdl) { - this.keyspace = keyspace; - this.tableDdl = tableDdl; - } - - public Cluster getCluster() { - return cluster; - } - - public Session connect() { - return cluster.connect(keyspace); - } - - @Override - protected void before() throws Throwable { - final Path root = Files.createTempDirectory("cassandra"); - Files.createDirectories(root.resolve("data")); - final Path config = root.resolve("cassandra.yml"); - Files.copy(getClass().getResourceAsStream("/cassandra.yaml"), config); - System.setProperty("cassandra.config", "file:" + config.toString()); - System.setProperty("cassandra.storagedir", root.toString()); - System.setProperty("cassandra-foreground", "true"); // prevents Cassandra from closing stdout/stderr - THREAD_FACTORY.newThread(embeddedCassandra).start(); - latch.await(); - cluster = Cluster.builder().addContactPoints(InetAddress.getLoopbackAddress()).build(); - try (final Session session = cluster.connect()) { - session.execute("CREATE KEYSPACE " + keyspace + " WITH REPLICATION = " + - "{ 'class': 'SimpleStrategy', 'replication_factor': 2 };"); - } - try (final Session session = connect()) { - session.execute(tableDdl); - } - } - - @Override - protected void after() { - Closer.closeSilently(cluster); - embeddedCassandra.cancel(); - } - - private static class EmbeddedCassandra implements Cancellable { - - private final CassandraDaemon daemon = new CassandraDaemon(); - private final CountDownLatch latch; - - private EmbeddedCassandra(final CountDownLatch latch) { - this.latch = latch; - } - - @Override - public void cancel() { - // LOG4J2-1850 Cassandra on Windows calls System.exit in the daemon stop method - if (PropertiesUtil.getProperties().isOsWindows()) { - cancelOnWindows(); - } else { - daemon.stop(); - } - } - - private void cancelOnWindows() { - final SecurityManager currentSecurityManager = System.getSecurityManager(); - try { - final SecurityManager securityManager = new SecurityManager() { - @Override - public void checkPermission(final Permission permission) { - final String permissionName = permission.getName(); - if (permissionName != null && permissionName.startsWith("exitVM")) { - throw new SecurityException("test"); - } - } - }; - System.setSecurityManager(securityManager); - daemon.stop(); - } catch (final SecurityException ex) { - // ignore - } finally { - System.setSecurityManager(currentSecurityManager); - } - } - - @Override - public void run() { - try { - daemon.init(null); - } catch (final IOException e) { - throw new LoggingException("Cannot initialize embedded Cassandra instance", e); - } - daemon.start(); - latch.countDown(); - } - } -} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/log4j-cassandra/src/test/resources/CassandraAppenderTest.xml ---------------------------------------------------------------------- diff --git a/log4j-cassandra/src/test/resources/CassandraAppenderTest.xml b/log4j-cassandra/src/test/resources/CassandraAppenderTest.xml index b3956d1..e1f37e9 100644 --- a/log4j-cassandra/src/test/resources/CassandraAppenderTest.xml +++ b/log4j-cassandra/src/test/resources/CassandraAppenderTest.xml @@ -32,7 +32,7 @@ </Cassandra> </Appenders> <Loggers> - <Logger name="org.apache.logging.log4j.nosql.appender.cassandra" level="DEBUG"> + <Logger name="org.apache.logging.log4j.cassandra" level="DEBUG"> <AppenderRef ref="Cassandra"/> </Logger> <Root level="ERROR"/> http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/log4j-couchdb/src/main/java/org/apache/logging/log4j/couchdb/CouchDbConnection.java ---------------------------------------------------------------------- diff --git a/log4j-couchdb/src/main/java/org/apache/logging/log4j/couchdb/CouchDbConnection.java b/log4j-couchdb/src/main/java/org/apache/logging/log4j/couchdb/CouchDbConnection.java new file mode 100644 index 0000000..822f4a2 --- /dev/null +++ b/log4j-couchdb/src/main/java/org/apache/logging/log4j/couchdb/CouchDbConnection.java @@ -0,0 +1,69 @@ +/* + * 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.couchdb; + +import java.util.Map; + +import org.apache.logging.log4j.core.appender.AppenderLoggingException; +import org.apache.logging.log4j.core.appender.nosql.AbstractNoSqlConnection; +import org.apache.logging.log4j.core.appender.nosql.DefaultNoSqlObject; +import org.apache.logging.log4j.core.appender.nosql.NoSqlConnection; +import org.apache.logging.log4j.core.appender.nosql.NoSqlObject; +import org.apache.logging.log4j.util.Strings; +import org.lightcouch.CouchDbClient; +import org.lightcouch.Response; + +/** + * The Apache CouchDB implementation of {@link NoSqlConnection}. + */ +public final class CouchDbConnection extends AbstractNoSqlConnection<Map<String, Object>, DefaultNoSqlObject> { + private final CouchDbClient client; + + public CouchDbConnection(final CouchDbClient client) { + this.client = client; + } + + @Override + public DefaultNoSqlObject createObject() { + return new DefaultNoSqlObject(); + } + + @Override + public DefaultNoSqlObject[] createList(final int length) { + return new DefaultNoSqlObject[length]; + } + + @Override + public void insertObject(final NoSqlObject<Map<String, Object>> object) { + try { + final Response response = this.client.save(object.unwrap()); + if (Strings.isNotEmpty(response.getError())) { + throw new AppenderLoggingException( + "Failed to write log event to CouchDB due to error: " + response.getError() + '.'); + } + } catch (final Exception e) { + throw new AppenderLoggingException("Failed to write log event to CouchDB due to error: " + e.getMessage(), + e); + } + } + + @Override + protected void closeImpl() { + this.client.shutdown(); + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/log4j-couchdb/src/main/java/org/apache/logging/log4j/couchdb/CouchDbProvider.java ---------------------------------------------------------------------- diff --git a/log4j-couchdb/src/main/java/org/apache/logging/log4j/couchdb/CouchDbProvider.java b/log4j-couchdb/src/main/java/org/apache/logging/log4j/couchdb/CouchDbProvider.java new file mode 100644 index 0000000..2b233a8 --- /dev/null +++ b/log4j-couchdb/src/main/java/org/apache/logging/log4j/couchdb/CouchDbProvider.java @@ -0,0 +1,161 @@ +/* + * 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.couchdb; + +import java.lang.reflect.Method; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.config.plugins.convert.TypeConverters; +import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidHost; +import org.apache.logging.log4j.core.config.plugins.validation.constraints.ValidPort; +import org.apache.logging.log4j.core.util.NameUtil; +import org.apache.logging.log4j.core.appender.nosql.NoSqlProvider; +import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.LoaderUtil; +import org.apache.logging.log4j.util.Strings; +import org.lightcouch.CouchDbClient; +import org.lightcouch.CouchDbProperties; + +/** + * The Apache CouchDB implementation of {@link NoSqlProvider}. + */ +@Plugin(name = "CouchDB", category = "Core", printObject = true) +public final class CouchDbProvider implements NoSqlProvider<CouchDbConnection> { + private static final int HTTP = 80; + private static final int HTTPS = 443; + private static final Logger LOGGER = StatusLogger.getLogger(); + + private final CouchDbClient client; + private final String description; + + private CouchDbProvider(final CouchDbClient client, final String description) { + this.client = client; + this.description = "couchDb{ " + description + " }"; + } + + @Override + public CouchDbConnection getConnection() { + return new CouchDbConnection(this.client); + } + + @Override + public String toString() { + return this.description; + } + + /** + * Factory method for creating an Apache CouchDB provider within the plugin manager. + * + * @param databaseName The name of the database to which log event documents will be written. + * @param protocol Either "http" or "https," defaults to "http" and mutually exclusive with + * {@code factoryClassName&factoryMethodName!=null}. + * @param server The host name of the CouchDB server, defaults to localhost and mutually exclusive with + * {@code factoryClassName&factoryMethodName!=null}. + * @param port The port that CouchDB is listening on, defaults to 80 if {@code protocol} is "http" and 443 if + * {@code protocol} is "https," and mutually exclusive with + * {@code factoryClassName&factoryMethodName!=null}. + * @param username The username to authenticate against the MongoDB server with, mutually exclusive with + * {@code factoryClassName&factoryMethodName!=null}. + * @param password The password to authenticate against the MongoDB server with, mutually exclusive with + * {@code factoryClassName&factoryMethodName!=null}. + * @param factoryClassName A fully qualified class name containing a static factory method capable of returning a + * {@link CouchDbClient} or {@link CouchDbProperties}. + * @param factoryMethodName The name of the public static factory method belonging to the aforementioned factory + * class. + * @return a new Apache CouchDB provider. + */ + @PluginFactory + public static CouchDbProvider createNoSqlProvider( + @PluginAttribute("databaseName") final String databaseName, + @PluginAttribute("protocol") String protocol, + @PluginAttribute(value = "server", defaultString = "localhost") @ValidHost final String server, + @PluginAttribute(value = "port", defaultString = "0") @ValidPort final String port, + @PluginAttribute("username") final String username, + @PluginAttribute(value = "password", sensitive = true) final String password, + @PluginAttribute("factoryClassName") final String factoryClassName, + @PluginAttribute("factoryMethodName") final String factoryMethodName) { + CouchDbClient client; + String description; + if (Strings.isNotEmpty(factoryClassName) && Strings.isNotEmpty(factoryMethodName)) { + try { + final Class<?> factoryClass = LoaderUtil.loadClass(factoryClassName); + final Method method = factoryClass.getMethod(factoryMethodName); + final Object object = method.invoke(null); + + if (object instanceof CouchDbClient) { + client = (CouchDbClient) object; + description = "uri=" + client.getDBUri(); + } else if (object instanceof CouchDbProperties) { + final CouchDbProperties properties = (CouchDbProperties) object; + client = new CouchDbClient(properties); + description = "uri=" + client.getDBUri() + ", username=" + properties.getUsername() + + ", passwordHash=" + NameUtil.md5(password + CouchDbProvider.class.getName()) + + ", maxConnections=" + properties.getMaxConnections() + ", connectionTimeout=" + + properties.getConnectionTimeout() + ", socketTimeout=" + properties.getSocketTimeout(); + } else if (object == null) { + LOGGER.error("The factory method [{}.{}()] returned null.", factoryClassName, factoryMethodName); + return null; + } else { + LOGGER.error("The factory method [{}.{}()] returned an unsupported type [{}].", factoryClassName, + factoryMethodName, object.getClass().getName()); + return null; + } + } catch (final ClassNotFoundException e) { + LOGGER.error("The factory class [{}] could not be loaded.", factoryClassName, e); + return null; + } catch (final NoSuchMethodException e) { + LOGGER.error("The factory class [{}] does not have a no-arg method named [{}].", factoryClassName, + factoryMethodName, e); + return null; + } catch (final Exception e) { + LOGGER.error("The factory method [{}.{}()] could not be invoked.", factoryClassName, factoryMethodName, + e); + return null; + } + } else if (Strings.isNotEmpty(databaseName)) { + if (protocol != null && protocol.length() > 0) { + protocol = protocol.toLowerCase(); + if (!protocol.equals("http") && !protocol.equals("https")) { + LOGGER.error("Only protocols [http] and [https] are supported, [{}] specified.", protocol); + return null; + } + } else { + protocol = "http"; + LOGGER.warn("No protocol specified, using default port [http]."); + } + + final int portInt = TypeConverters.convert(port, int.class, protocol.equals("https") ? HTTPS : HTTP); + + if (Strings.isEmpty(username) || Strings.isEmpty(password)) { + LOGGER.error("You must provide a username and password for the CouchDB provider."); + return null; + } + + client = new CouchDbClient(databaseName, false, protocol, server, portInt, username, password); + description = "uri=" + client.getDBUri() + ", username=" + username + ", passwordHash=" + + NameUtil.md5(password + CouchDbProvider.class.getName()); + } else { + LOGGER.error("No factory method was provided so the database name is required."); + return null; + } + + return new CouchDbProvider(client, description); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/log4j-couchdb/src/main/java/org/apache/logging/log4j/couchdb/package-info.java ---------------------------------------------------------------------- diff --git a/log4j-couchdb/src/main/java/org/apache/logging/log4j/couchdb/package-info.java b/log4j-couchdb/src/main/java/org/apache/logging/log4j/couchdb/package-info.java new file mode 100644 index 0000000..9fc2513 --- /dev/null +++ b/log4j-couchdb/src/main/java/org/apache/logging/log4j/couchdb/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ +/** + * The classes in this package contain the Apache CouchDB provider for the NoSQL Appender. + */ +package org.apache.logging.log4j.couchdb; http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/44db87d0/log4j-couchdb/src/main/java/org/apache/logging/log4j/nosql/appender/couchdb/CouchDbConnection.java ---------------------------------------------------------------------- diff --git a/log4j-couchdb/src/main/java/org/apache/logging/log4j/nosql/appender/couchdb/CouchDbConnection.java b/log4j-couchdb/src/main/java/org/apache/logging/log4j/nosql/appender/couchdb/CouchDbConnection.java deleted file mode 100644 index 2c3248d..0000000 --- a/log4j-couchdb/src/main/java/org/apache/logging/log4j/nosql/appender/couchdb/CouchDbConnection.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.nosql.appender.couchdb; - -import java.util.Map; - -import org.apache.logging.log4j.core.appender.AppenderLoggingException; -import org.apache.logging.log4j.core.appender.nosql.AbstractNoSqlConnection; -import org.apache.logging.log4j.core.appender.nosql.DefaultNoSqlObject; -import org.apache.logging.log4j.core.appender.nosql.NoSqlConnection; -import org.apache.logging.log4j.core.appender.nosql.NoSqlObject; -import org.apache.logging.log4j.util.Strings; -import org.lightcouch.CouchDbClient; -import org.lightcouch.Response; - -/** - * The Apache CouchDB implementation of {@link NoSqlConnection}. - */ -public final class CouchDbConnection extends AbstractNoSqlConnection<Map<String, Object>, DefaultNoSqlObject> { - private final CouchDbClient client; - - public CouchDbConnection(final CouchDbClient client) { - this.client = client; - } - - @Override - public DefaultNoSqlObject createObject() { - return new DefaultNoSqlObject(); - } - - @Override - public DefaultNoSqlObject[] createList(final int length) { - return new DefaultNoSqlObject[length]; - } - - @Override - public void insertObject(final NoSqlObject<Map<String, Object>> object) { - try { - final Response response = this.client.save(object.unwrap()); - if (Strings.isNotEmpty(response.getError())) { - throw new AppenderLoggingException( - "Failed to write log event to CouchDB due to error: " + response.getError() + '.'); - } - } catch (final Exception e) { - throw new AppenderLoggingException("Failed to write log event to CouchDB due to error: " + e.getMessage(), - e); - } - } - - @Override - protected void closeImpl() { - this.client.shutdown(); - } - -}