DATAMONGO-442 - Polishing.

Reformat code according to Spring Data style. Add test for authenticated use. Add JavaDoc to newly introduced methods. Allow configuration of an authentication database. Update reference documentation.

Original pull request: #419.
This commit is contained in:
Mark Paluch
2016-12-13 15:53:19 +01:00
parent 9737464f9a
commit aa1e91c761
6 changed files with 269 additions and 70 deletions

View File

@@ -15,15 +15,27 @@
*/ */
package org.springframework.data.mongodb.log4j; 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.AppenderSkeleton;
import org.apache.log4j.Level; import org.apache.log4j.Level;
import org.apache.log4j.MDC; import org.apache.log4j.MDC;
import org.apache.log4j.PatternLayout; import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.spi.LoggingEvent;
import java.net.UnknownHostException; import com.mongodb.BasicDBList;
import java.util.*; 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. * Log4j appender writing log entries into a MongoDB instance.
@@ -51,6 +63,7 @@ public class MongoLog4jAppender extends AppenderSkeleton {
protected int port = 27017; protected int port = 27017;
protected String username; protected String username;
protected String password; protected String password;
protected String authenticationDatabase;
protected String database = "logs"; protected String database = "logs";
protected String collectionPattern = "%c"; protected String collectionPattern = "%c";
protected PatternLayout collectionLayout = new PatternLayout(collectionPattern); protected PatternLayout collectionLayout = new PatternLayout(collectionPattern);
@@ -60,8 +73,7 @@ public class MongoLog4jAppender extends AppenderSkeleton {
protected Mongo mongo; protected Mongo mongo;
protected DB db; protected DB db;
public MongoLog4jAppender() { public MongoLog4jAppender() {}
}
public MongoLog4jAppender(boolean isActive) { public MongoLog4jAppender(boolean isActive) {
super(isActive); super(isActive);
@@ -83,22 +95,53 @@ public class MongoLog4jAppender extends AppenderSkeleton {
this.port = port; this.port = port;
} }
public String getPassword() { /**
return password; * @return
} * @since 1.10
*/
public void setPassword(String password) {
this.password = password;
}
public String getUsername() { public String getUsername() {
return username; return username;
} }
/**
* @param username may be {@literal null} for unauthenticated access.
* @since 1.10
*/
public void setUsername(String username) { public void setUsername(String username) {
this.username = 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() { public String getDatabase() {
return database; return database;
} }
@@ -141,26 +184,33 @@ public class MongoLog4jAppender extends AppenderSkeleton {
} }
protected void connectToMongo() throws UnknownHostException { protected void connectToMongo() throws UnknownHostException {
ServerAddress serverAddress = new ServerAddress(host, port);
connectToMongoHandlingCredentials(serverAddress); this.mongo = createMongoClient();
this.db = mongo.getDB(database); 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) { if (null == password || null == username) {
this.mongo = new MongoClient(serverAddress); return new MongoClient(serverAddress);
} else {
MongoCredential mongoCredential = MongoCredential.createCredential(username, database, password.toCharArray());
List<MongoCredential> credentials = Collections.singletonList(mongoCredential);
this.mongo = new MongoClient(serverAddress, credentials);
} }
String authenticationDatabaseToUse = authenticationDatabase == null ? this.database : authenticationDatabase;
MongoCredential mongoCredential = MongoCredential.createCredential(username,
authenticationDatabaseToUse, password.toCharArray());
List<MongoCredential> credentials = Collections.singletonList(mongoCredential);
return new MongoClient(serverAddress, credentials);
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* @see org.apache.log4j.AppenderSkeleton#append(org.apache.log4j.spi.LoggingEvent) * @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) { if (null == db) {
try { try {
connectToMongo(); connectToMongo();

View File

@@ -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");
}
}

View File

@@ -15,19 +15,24 @@
*/ */
package org.springframework.data.mongodb.log4j; 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.Logger;
import org.apache.log4j.MDC; import org.apache.log4j.MDC;
import org.apache.log4j.PropertyConfigurator;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.net.UnknownHostException; import com.mongodb.BasicDBObject;
import java.util.Calendar; import com.mongodb.DB;
import java.util.Collections; import com.mongodb.DBCursor;
import com.mongodb.MongoClient;
import static org.hamcrest.Matchers.is; import com.mongodb.ServerAddress;
import static org.junit.Assert.assertThat;
/** /**
* Integration tests for {@link MongoLog4jAppender}. * Integration tests for {@link MongoLog4jAppender}.
@@ -38,13 +43,14 @@ import static org.junit.Assert.assertThat;
*/ */
public class MongoLog4jAppenderIntegrationTests { public class MongoLog4jAppenderIntegrationTests {
private static final Logger log = Logger.getLogger(MongoLog4jAppenderIntegrationTests.class.getName()); MongoClient mongo;
private MongoClient mongo; DB db;
private DB db; String collection;
private String collection; ServerAddress serverLocation;
private ServerAddress serverLocation; Logger log;
@Before public void setUp() throws Exception { @Before
public void setUp() throws Exception {
serverLocation = new ServerAddress("localhost", 27017); serverLocation = new ServerAddress("localhost", 27017);
mongo = new MongoClient(serverLocation); mongo = new MongoClient(serverLocation);
@@ -52,13 +58,18 @@ public class MongoLog4jAppenderIntegrationTests {
Calendar now = Calendar.getInstance(); Calendar now = Calendar.getInstance();
collection = String.valueOf(now.get(Calendar.YEAR)) + String.format("%1$02d", now.get(Calendar.MONTH) + 1); 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()); db.getCollection(collection).remove(new BasicDBObject());
} }
@Test public void testLogging() { @Test
public void testLogging() {
log.debug("DEBUG message"); log.debug("DEBUG message");
log.info("INFO message"); log.info("INFO message");
log.warn("WARN message"); log.warn("WARN message");
@@ -68,18 +79,9 @@ public class MongoLog4jAppenderIntegrationTests {
assertThat(msgs.count(), is(4)); assertThat(msgs.count(), is(4));
} }
/** @Test
* @see DATAMONGO-442 public void testProperties() {
*/
@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() {
MDC.put("property", "one"); MDC.put("property", "one");
log.debug("DEBUG message"); log.debug("DEBUG message");
} }
} }

View File

@@ -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

View File

@@ -1,13 +1,13 @@
log4j.rootCategory=INFO, stdout log4j.rootCategory=INFO, mongo
log4j.appender.stdout=org.springframework.data.mongodb.log4j.MongoLog4jAppender log4j.appender.mongo=org.springframework.data.mongodb.log4j.MongoLog4jAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.mongo.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n log4j.appender.mongo.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.appender.stdout.host = localhost log4j.appender.mongo.host = localhost
log4j.appender.stdout.port = 27017 log4j.appender.mongo.port = 27017
log4j.appender.stdout.database = logs log4j.appender.mongo.database = logs
log4j.appender.stdout.collectionPattern = %X{year}%X{month} log4j.appender.mongo.collectionPattern = %X{year}%X{month}
log4j.appender.stdout.applicationId = my.application log4j.appender.mongo.applicationId = my.application
log4j.appender.stdout.warnOrHigherWriteConcern = FSYNC_SAFE log4j.appender.mongo.warnOrHigherWriteConcern = FSYNC_SAFE
log4j.category.org.springframework.data.mongodb=DEBUG log4j.category.org.springframework.data.mongodb=DEBUG

View File

@@ -10,17 +10,17 @@ Here is an example configuration
[source] [source]
---- ----
log4j.rootCategory=INFO, stdout log4j.rootCategory=INFO, mongo
log4j.appender.stdout=org.springframework.data.document.mongodb.log4j.MongoLog4jAppender log4j.appender.mongo=org.springframework.data.document.mongodb.log4j.MongoLog4jAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.mongo.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n log4j.appender.mongo.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.appender.stdout.host = localhost log4j.appender.mongo.host = localhost
log4j.appender.stdout.port = 27017 log4j.appender.mongo.port = 27017
log4j.appender.stdout.database = logs log4j.appender.mongo.database = logs
log4j.appender.stdout.collectionPattern = %X{year}%X{month} log4j.appender.mongo.collectionPattern = %X{year}%X{month}
log4j.appender.stdout.applicationId = my.application log4j.appender.mongo.applicationId = my.application
log4j.appender.stdout.warnOrHigherWriteConcern = FSYNC_SAFE log4j.appender.mongo.warnOrHigherWriteConcern = FSYNC_SAFE
log4j.category.org.apache.activemq=ERROR log4j.category.org.apache.activemq=ERROR
log4j.category.org.springframework.batch=DEBUG log4j.category.org.springframework.batch=DEBUG
@@ -28,6 +28,23 @@ log4j.category.org.springframework.data.document.mongodb=DEBUG
log4j.category.org.springframework.transaction=INFO 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.