diff --git a/spring-data-mongodb-log4j/src/main/java/org/springframework/data/mongodb/log4j/MongoLog4jAppender.java b/spring-data-mongodb-log4j/src/main/java/org/springframework/data/mongodb/log4j/MongoLog4jAppender.java index 9b2555019..0474c56dd 100644 --- a/spring-data-mongodb-log4j/src/main/java/org/springframework/data/mongodb/log4j/MongoLog4jAppender.java +++ b/spring-data-mongodb-log4j/src/main/java/org/springframework/data/mongodb/log4j/MongoLog4jAppender.java @@ -15,15 +15,27 @@ */ package org.springframework.data.mongodb.log4j; -import com.mongodb.*; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.List; +import java.util.Map; + import org.apache.log4j.AppenderSkeleton; import org.apache.log4j.Level; import org.apache.log4j.MDC; import org.apache.log4j.PatternLayout; import org.apache.log4j.spi.LoggingEvent; -import java.net.UnknownHostException; -import java.util.*; +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.DB; +import com.mongodb.Mongo; +import com.mongodb.MongoClient; +import com.mongodb.MongoCredential; +import com.mongodb.ServerAddress; +import com.mongodb.WriteConcern; /** * Log4j appender writing log entries into a MongoDB instance. @@ -51,6 +63,7 @@ public class MongoLog4jAppender extends AppenderSkeleton { protected int port = 27017; protected String username; protected String password; + protected String authenticationDatabase; protected String database = "logs"; protected String collectionPattern = "%c"; protected PatternLayout collectionLayout = new PatternLayout(collectionPattern); @@ -60,8 +73,7 @@ public class MongoLog4jAppender extends AppenderSkeleton { protected Mongo mongo; protected DB db; - public MongoLog4jAppender() { - } + public MongoLog4jAppender() {} public MongoLog4jAppender(boolean isActive) { super(isActive); @@ -83,22 +95,53 @@ public class MongoLog4jAppender extends AppenderSkeleton { this.port = port; } - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - + /** + * @return + * @since 1.10 + */ public String getUsername() { return username; } + /** + * @param username may be {@literal null} for unauthenticated access. + * @since 1.10 + */ public void setUsername(String username) { this.username = username; } + /** + * @return + * @since 1.10 + */ + public String getPassword() { + return password; + } + + /** + * @param password may be {@literal null} for unauthenticated access. + * @since 1.10 + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * @return + */ + public String getAuthenticationDatabase() { + return authenticationDatabase; + } + + /** + * @param authenticationDatabase may be {@literal null} to use {@link #getDatabase()} as authentication database. + * @since 1.10 + */ + public void setAuthenticationDatabase(String authenticationDatabase) { + this.authenticationDatabase = authenticationDatabase; + } + public String getDatabase() { return database; } @@ -141,26 +184,33 @@ public class MongoLog4jAppender extends AppenderSkeleton { } protected void connectToMongo() throws UnknownHostException { - ServerAddress serverAddress = new ServerAddress(host, port); - connectToMongoHandlingCredentials(serverAddress); + + this.mongo = createMongoClient(); this.db = mongo.getDB(database); } - private void connectToMongoHandlingCredentials(ServerAddress serverAddress) { + private MongoClient createMongoClient() throws UnknownHostException { + + ServerAddress serverAddress = new ServerAddress(host, port); + if (null == password || null == username) { - this.mongo = new MongoClient(serverAddress); - } else { - MongoCredential mongoCredential = MongoCredential.createCredential(username, database, password.toCharArray()); - List credentials = Collections.singletonList(mongoCredential); - this.mongo = new MongoClient(serverAddress, credentials); + return new MongoClient(serverAddress); } + + String authenticationDatabaseToUse = authenticationDatabase == null ? this.database : authenticationDatabase; + MongoCredential mongoCredential = MongoCredential.createCredential(username, + authenticationDatabaseToUse, password.toCharArray()); + List credentials = Collections.singletonList(mongoCredential); + return new MongoClient(serverAddress, credentials); } /* * (non-Javadoc) * @see org.apache.log4j.AppenderSkeleton#append(org.apache.log4j.spi.LoggingEvent) */ - @Override @SuppressWarnings({ "unchecked" }) protected void append(final LoggingEvent event) { + @Override + @SuppressWarnings({ "unchecked" }) + protected void append(final LoggingEvent event) { if (null == db) { try { connectToMongo(); diff --git a/spring-data-mongodb-log4j/src/test/java/org/springframework/data/mongodb/log4j/MongoLog4jAppenderAuthenticationIntegrationTests.java b/spring-data-mongodb-log4j/src/test/java/org/springframework/data/mongodb/log4j/MongoLog4jAppenderAuthenticationIntegrationTests.java new file mode 100644 index 000000000..6cf2818de --- /dev/null +++ b/spring-data-mongodb-log4j/src/test/java/org/springframework/data/mongodb/log4j/MongoLog4jAppenderAuthenticationIntegrationTests.java @@ -0,0 +1,114 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.log4j; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.Calendar; +import java.util.Collections; + +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.apache.log4j.MDC; +import org.apache.log4j.PropertyConfigurator; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.BasicDBObjectBuilder; +import com.mongodb.DB; +import com.mongodb.DBCursor; +import com.mongodb.MongoClient; +import com.mongodb.MongoCredential; +import com.mongodb.ServerAddress; + +/** + * Integration tests for {@link MongoLog4jAppender} using authentication. + * + * @author Mark Paluch + */ +public class MongoLog4jAppenderAuthenticationIntegrationTests { + + private final static String username = "admin"; + private final static String password = "test"; + private final static String authenticationDatabase = "logs"; + + MongoClient mongo; + DB db; + String collection; + ServerAddress serverLocation; + + Logger log; + + @Before + public void setUp() throws Exception { + + serverLocation = new ServerAddress("localhost", 27017); + + mongo = new MongoClient(serverLocation); + db = mongo.getDB("logs"); + + BasicDBList roles = new BasicDBList(); + roles.add("dbOwner"); + db.command(new BasicDBObjectBuilder().add("createUser", username).add("pwd", password).add("roles", roles).get()); + mongo.close(); + + mongo = new MongoClient(serverLocation, Collections + .singletonList(MongoCredential.createCredential(username, authenticationDatabase, password.toCharArray()))); + db = mongo.getDB("logs"); + + Calendar now = Calendar.getInstance(); + collection = String.valueOf(now.get(Calendar.YEAR)) + String.format("%1$02d", now.get(Calendar.MONTH) + 1); + + LogManager.resetConfiguration(); + PropertyConfigurator.configure(getClass().getResource("/log4j-with-authentication.properties")); + + log = Logger.getLogger(MongoLog4jAppenderIntegrationTests.class.getName()); + } + + @After + public void tearDown() { + + if (db != null) { + db.getCollection(collection).remove(new BasicDBObject()); + db.command(new BasicDBObject("dropUser", username)); + } + + LogManager.resetConfiguration(); + PropertyConfigurator.configure(getClass().getResource("/log4j.properties")); + } + + @Test + public void testLogging() { + + log.debug("DEBUG message"); + log.info("INFO message"); + log.warn("WARN message"); + log.error("ERROR message"); + + DBCursor msgs = db.getCollection(collection).find(); + assertThat(msgs.count(), is(4)); + } + + @Test + public void testProperties() { + MDC.put("property", "one"); + log.debug("DEBUG message"); + } +} diff --git a/spring-data-mongodb-log4j/src/test/java/org/springframework/data/mongodb/log4j/MongoLog4jAppenderIntegrationTests.java b/spring-data-mongodb-log4j/src/test/java/org/springframework/data/mongodb/log4j/MongoLog4jAppenderIntegrationTests.java index caf395c66..0271d47cd 100644 --- a/spring-data-mongodb-log4j/src/test/java/org/springframework/data/mongodb/log4j/MongoLog4jAppenderIntegrationTests.java +++ b/spring-data-mongodb-log4j/src/test/java/org/springframework/data/mongodb/log4j/MongoLog4jAppenderIntegrationTests.java @@ -15,19 +15,24 @@ */ package org.springframework.data.mongodb.log4j; -import com.mongodb.*; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.util.Calendar; + +import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.apache.log4j.MDC; +import org.apache.log4j.PropertyConfigurator; import org.junit.After; import org.junit.Before; import org.junit.Test; -import java.net.UnknownHostException; -import java.util.Calendar; -import java.util.Collections; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; +import com.mongodb.BasicDBObject; +import com.mongodb.DB; +import com.mongodb.DBCursor; +import com.mongodb.MongoClient; +import com.mongodb.ServerAddress; /** * Integration tests for {@link MongoLog4jAppender}. @@ -38,13 +43,14 @@ import static org.junit.Assert.assertThat; */ public class MongoLog4jAppenderIntegrationTests { - private static final Logger log = Logger.getLogger(MongoLog4jAppenderIntegrationTests.class.getName()); - private MongoClient mongo; - private DB db; - private String collection; - private ServerAddress serverLocation; + MongoClient mongo; + DB db; + String collection; + ServerAddress serverLocation; + Logger log; - @Before public void setUp() throws Exception { + @Before + public void setUp() throws Exception { serverLocation = new ServerAddress("localhost", 27017); mongo = new MongoClient(serverLocation); @@ -52,13 +58,18 @@ public class MongoLog4jAppenderIntegrationTests { Calendar now = Calendar.getInstance(); collection = String.valueOf(now.get(Calendar.YEAR)) + String.format("%1$02d", now.get(Calendar.MONTH) + 1); + + log = Logger.getLogger(MongoLog4jAppenderIntegrationTests.class.getName()); } - @After public void tearDown() { + @After + public void tearDown() { db.getCollection(collection).remove(new BasicDBObject()); } - @Test public void testLogging() { + @Test + public void testLogging() { + log.debug("DEBUG message"); log.info("INFO message"); log.warn("WARN message"); @@ -68,18 +79,9 @@ public class MongoLog4jAppenderIntegrationTests { assertThat(msgs.count(), is(4)); } - /** - * @see DATAMONGO-442 - */ - @Test public void testLoggingWithCredentials() throws UnknownHostException { - MongoCredential credential = MongoCredential.createCredential("username", "logs", "password".toCharArray()); - mongo = new MongoClient(serverLocation, Collections.singletonList(credential)); - testLogging(); - } - - @Test public void testProperties() { + @Test + public void testProperties() { MDC.put("property", "one"); log.debug("DEBUG message"); } - } diff --git a/spring-data-mongodb-log4j/src/test/resources/log4j-with-authentication.properties b/spring-data-mongodb-log4j/src/test/resources/log4j-with-authentication.properties new file mode 100644 index 000000000..d83684463 --- /dev/null +++ b/spring-data-mongodb-log4j/src/test/resources/log4j-with-authentication.properties @@ -0,0 +1,16 @@ +log4j.rootCategory=INFO, mongo + +log4j.appender.mongo=org.springframework.data.mongodb.log4j.MongoLog4jAppender +log4j.appender.mongo.layout=org.apache.log4j.PatternLayout +log4j.appender.mongo.layout.ConversionPattern=%d %p [%c] - <%m>%n +log4j.appender.mongo.host = localhost +log4j.appender.mongo.port = 27017 +log4j.appender.mongo.database = logs +log4j.appender.mongo.username = admin +log4j.appender.mongo.password = test +log4j.appender.mongo.authenticationDatabase = logs +log4j.appender.mongo.collectionPattern = %X{year}%X{month} +log4j.appender.mongo.applicationId = my.application +log4j.appender.mongo.warnOrHigherWriteConcern = FSYNC_SAFE + +log4j.category.org.springframework.data.mongodb=DEBUG diff --git a/spring-data-mongodb-log4j/src/test/resources/log4j.properties b/spring-data-mongodb-log4j/src/test/resources/log4j.properties index 88459b3ff..75136af0a 100644 --- a/spring-data-mongodb-log4j/src/test/resources/log4j.properties +++ b/spring-data-mongodb-log4j/src/test/resources/log4j.properties @@ -1,13 +1,13 @@ -log4j.rootCategory=INFO, stdout +log4j.rootCategory=INFO, mongo -log4j.appender.stdout=org.springframework.data.mongodb.log4j.MongoLog4jAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n -log4j.appender.stdout.host = localhost -log4j.appender.stdout.port = 27017 -log4j.appender.stdout.database = logs -log4j.appender.stdout.collectionPattern = %X{year}%X{month} -log4j.appender.stdout.applicationId = my.application -log4j.appender.stdout.warnOrHigherWriteConcern = FSYNC_SAFE +log4j.appender.mongo=org.springframework.data.mongodb.log4j.MongoLog4jAppender +log4j.appender.mongo.layout=org.apache.log4j.PatternLayout +log4j.appender.mongo.layout.ConversionPattern=%d %p [%c] - <%m>%n +log4j.appender.mongo.host = localhost +log4j.appender.mongo.port = 27017 +log4j.appender.mongo.database = logs +log4j.appender.mongo.collectionPattern = %X{year}%X{month} +log4j.appender.mongo.applicationId = my.application +log4j.appender.mongo.warnOrHigherWriteConcern = FSYNC_SAFE log4j.category.org.springframework.data.mongodb=DEBUG diff --git a/src/main/asciidoc/reference/logging.adoc b/src/main/asciidoc/reference/logging.adoc index 5cd3c5d40..d1cdbe05a 100644 --- a/src/main/asciidoc/reference/logging.adoc +++ b/src/main/asciidoc/reference/logging.adoc @@ -10,17 +10,17 @@ Here is an example configuration [source] ---- -log4j.rootCategory=INFO, stdout +log4j.rootCategory=INFO, mongo -log4j.appender.stdout=org.springframework.data.document.mongodb.log4j.MongoLog4jAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n -log4j.appender.stdout.host = localhost -log4j.appender.stdout.port = 27017 -log4j.appender.stdout.database = logs -log4j.appender.stdout.collectionPattern = %X{year}%X{month} -log4j.appender.stdout.applicationId = my.application -log4j.appender.stdout.warnOrHigherWriteConcern = FSYNC_SAFE +log4j.appender.mongo=org.springframework.data.document.mongodb.log4j.MongoLog4jAppender +log4j.appender.mongo.layout=org.apache.log4j.PatternLayout +log4j.appender.mongo.layout.ConversionPattern=%d %p [%c] - <%m>%n +log4j.appender.mongo.host = localhost +log4j.appender.mongo.port = 27017 +log4j.appender.mongo.database = logs +log4j.appender.mongo.collectionPattern = %X{year}%X{month} +log4j.appender.mongo.applicationId = my.application +log4j.appender.mongo.warnOrHigherWriteConcern = FSYNC_SAFE log4j.category.org.apache.activemq=ERROR log4j.category.org.springframework.batch=DEBUG @@ -28,6 +28,23 @@ log4j.category.org.springframework.data.document.mongodb=DEBUG log4j.category.org.springframework.transaction=INFO ---- -The important configuration to look at aside from host and port is the database and collectionPattern. The variables year, month, day and hour are available for you to use in forming a collection name. This is to support the common convention of grouping log information in a collection that corresponds to a specific time period, for example a collection per day. +The important configuration to look at aside from host and port is the database and `collectionPattern`. The variables `year`, `month`, `day` and `hour` are available for you to use in forming a collection name. This is to support the common convention of grouping log information in a collection that corresponds to a specific time period, for example a collection per day. -There is also an applicationId which is put into the stored message. The document stored from logging as the following keys: level, name, applicationId, timestamp, properties, traceback, and message. +There is also an `applicationId` which is put into the stored message. The document stored from logging as the following keys: `level`, `name`, `applicationId`, `timestamp`, `properties`, `traceback`, and `message`. + +[[mongodb:logging-configuration:authentication]] +=== Using authentication + +The MongoDB Log4j appender can be configured to use username/password authentication. +Authentication is performed using the specified database. A different `authenticationDatabase` can be specified to override the default behavior. + +[source] +---- +# ... +log4j.appender.mongo.username = admin +log4j.appender.mongo.password = test +log4j.appender.mongo.authenticationDatabase = logs +# ... +---- + +NOTE: Authentication failures lead to exceptions during logging and are propagated to the caller of the logging method.