6 changed files with 499 additions and 1 deletions
@ -0,0 +1,52 @@ |
|||||||
|
# MongoDB Log4J Appender |
||||||
|
|
||||||
|
This module sets up a Log4J appender that puts logging events in MongoDB. It is fully configurable |
||||||
|
and connects directly to the MongoDB server using the driver. It has no dependency on any Spring package. |
||||||
|
|
||||||
|
To use it, configure a host, port, (optionally) applicationId, and database property in your Log4J configuration: |
||||||
|
|
||||||
|
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.applicationId = my.application |
||||||
|
log4j.appender.stdout.warnOrHigherWriteConcern = FSYNC_SAFE |
||||||
|
|
||||||
|
It will even support properties in your MDC (so long as they're Strings or support .toString()). |
||||||
|
|
||||||
|
The collection name is configurable as well. If you don't specify anything, it will use the Category name. |
||||||
|
If you want to specify a collection name, you can give it a String.format() string which will be passed the |
||||||
|
following parameters: |
||||||
|
|
||||||
|
1. Calendar.YEAR |
||||||
|
2. Calendar.MONTH |
||||||
|
3. Calendar.DAY_OF_MONTH |
||||||
|
4. Calendar.HOUR_OF_DAY |
||||||
|
5. event.getLevel().toString() |
||||||
|
6. event.getLogger().getName() |
||||||
|
|
||||||
|
An example log entry might look like: |
||||||
|
|
||||||
|
{ |
||||||
|
"_id" : ObjectId("4d89341a8ef397e06940d5cd"), |
||||||
|
"applicationId" : "my.application", |
||||||
|
"name" : "org.springframework.data.document.mongodb.log4j.AppenderTest", |
||||||
|
"level" : "DEBUG", |
||||||
|
"timestamp" : "1300837402444", |
||||||
|
"properties" : { |
||||||
|
"property" : "one" |
||||||
|
}, |
||||||
|
"message" : "DEBUG message" |
||||||
|
} |
||||||
|
|
||||||
|
To set WriteConcern levels for WARN or higher messages, set warnOrHigherWriteConcern to one of the following: |
||||||
|
|
||||||
|
* FSYNC_SAFE |
||||||
|
* NONE |
||||||
|
* NORMAL |
||||||
|
* REPLICAS_SAFE |
||||||
|
* SAFE |
||||||
|
|
||||||
|
(http://api.mongodb.org/java/2.5-pre-/com/mongodb/WriteConcern.html#field_detail)[http://api.mongodb.org/java/2.5-pre-/com/mongodb/WriteConcern.html#field_detail] |
||||||
@ -0,0 +1,163 @@ |
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> |
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
<parent> |
||||||
|
<groupId>org.springframework.data</groupId> |
||||||
|
<artifactId>spring-data-document-parent</artifactId> |
||||||
|
<version>1.0.0.BUILD-SNAPSHOT</version> |
||||||
|
<relativePath>../spring-data-document-parent/pom.xml</relativePath> |
||||||
|
</parent> |
||||||
|
<artifactId>spring-data-log4j-appender</artifactId> |
||||||
|
<packaging>jar</packaging> |
||||||
|
<name>Spring Data MongoDB Log4J Appender</name> |
||||||
|
|
||||||
|
<properties> |
||||||
|
<mongo.version>2.3</mongo.version> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
|
||||||
|
<!-- Spring --> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework</groupId> |
||||||
|
<artifactId>spring-beans</artifactId> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework</groupId> |
||||||
|
<artifactId>spring-tx</artifactId> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework</groupId> |
||||||
|
<artifactId>spring-expression</artifactId> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework</groupId> |
||||||
|
<artifactId>spring-test</artifactId> |
||||||
|
<scope>test</scope> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<!-- Spring Data --> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.data</groupId> |
||||||
|
<artifactId>spring-data-document-core</artifactId> |
||||||
|
<version>${project.version}</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.data</groupId> |
||||||
|
<artifactId>spring-data-commons-core</artifactId> |
||||||
|
<version>${data.commons.version}</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<!-- MongoDB --> |
||||||
|
<dependency> |
||||||
|
<groupId>org.mongodb</groupId> |
||||||
|
<artifactId>mongo-java-driver</artifactId> |
||||||
|
<version>${mongo.version}</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<!-- Logging --> |
||||||
|
<dependency> |
||||||
|
<groupId>org.slf4j</groupId> |
||||||
|
<artifactId>slf4j-api</artifactId> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.slf4j</groupId> |
||||||
|
<artifactId>jcl-over-slf4j</artifactId> |
||||||
|
<scope>compile</scope> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.slf4j</groupId> |
||||||
|
<artifactId>slf4j-log4j12</artifactId> |
||||||
|
<scope>runtime</scope> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>log4j</groupId> |
||||||
|
<artifactId>log4j</artifactId> |
||||||
|
<exclusions> |
||||||
|
<exclusion> |
||||||
|
<groupId>javax.mail</groupId> |
||||||
|
<artifactId>mail</artifactId> |
||||||
|
</exclusion> |
||||||
|
<exclusion> |
||||||
|
<groupId>javax.jms</groupId> |
||||||
|
<artifactId>jms</artifactId> |
||||||
|
</exclusion> |
||||||
|
<exclusion> |
||||||
|
<groupId>com.sun.jdmk</groupId> |
||||||
|
<artifactId>jmxtools</artifactId> |
||||||
|
</exclusion> |
||||||
|
<exclusion> |
||||||
|
<groupId>com.sun.jmx</groupId> |
||||||
|
<artifactId>jmxri</artifactId> |
||||||
|
</exclusion> |
||||||
|
</exclusions> |
||||||
|
<scope>compile</scope> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<!-- Test dependencies --> |
||||||
|
<dependency> |
||||||
|
<groupId>org.mockito</groupId> |
||||||
|
<artifactId>mockito-all</artifactId> |
||||||
|
<scope>test</scope> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>org.hamcrest</groupId> |
||||||
|
<artifactId>hamcrest-all</artifactId> |
||||||
|
<version>1.1</version> |
||||||
|
<scope>test</scope> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>junit</groupId> |
||||||
|
<artifactId>junit</artifactId> |
||||||
|
<scope>test</scope> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>joda-time</groupId> |
||||||
|
<artifactId>joda-time</artifactId> |
||||||
|
<version>1.6</version> |
||||||
|
<scope>test</scope> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
</dependencies> |
||||||
|
<build> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<groupId>com.springsource.bundlor</groupId> |
||||||
|
<artifactId>com.springsource.bundlor.maven</artifactId> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<groupId>com.mysema.maven</groupId> |
||||||
|
<artifactId>maven-apt-plugin</artifactId> |
||||||
|
<version>1.0</version> |
||||||
|
<executions> |
||||||
|
<execution> |
||||||
|
<phase>generate-test-sources</phase> |
||||||
|
<goals> |
||||||
|
<goal>test-process</goal> |
||||||
|
</goals> |
||||||
|
<configuration> |
||||||
|
<outputDirectory>target/generated-sources/test-annotations</outputDirectory> |
||||||
|
<processor>org.springframework.data.document.mongodb.repository.MongoAnnotationProcessor</processor> |
||||||
|
</configuration> |
||||||
|
</execution> |
||||||
|
</executions> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
|
||||||
|
<repositories> |
||||||
|
<repository> |
||||||
|
<id>querydsl</id> |
||||||
|
<name>Mysema QueryDsl</name> |
||||||
|
<url>http://source.mysema.com/maven2/releases</url> |
||||||
|
<snapshots> |
||||||
|
<enabled>false</enabled> |
||||||
|
</snapshots> |
||||||
|
</repository> |
||||||
|
</repositories> |
||||||
|
</project> |
||||||
@ -0,0 +1,193 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2011 by the original author(s). |
||||||
|
* |
||||||
|
* 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.document.mongodb.log4j; |
||||||
|
|
||||||
|
import java.net.UnknownHostException; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.Calendar; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import com.mongodb.BasicDBList; |
||||||
|
import com.mongodb.BasicDBObject; |
||||||
|
import com.mongodb.DB; |
||||||
|
import com.mongodb.Mongo; |
||||||
|
import com.mongodb.WriteConcern; |
||||||
|
import org.apache.log4j.AppenderSkeleton; |
||||||
|
import org.apache.log4j.Level; |
||||||
|
import org.apache.log4j.spi.LoggingEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Jon Brisbin <jbrisbin@vmware.com> |
||||||
|
*/ |
||||||
|
public class MongoLog4jAppender extends AppenderSkeleton { |
||||||
|
|
||||||
|
public static final String LEVEL = "level"; |
||||||
|
public static final String NAME = "name"; |
||||||
|
public static final String APP_ID = "applicationId"; |
||||||
|
public static final String TIMESTAMP = "timestamp"; |
||||||
|
public static final String PROPERTIES = "properties"; |
||||||
|
public static final String TRACEBACK = "traceback"; |
||||||
|
public static final String MESSAGE = "message"; |
||||||
|
|
||||||
|
protected String host = "localhost"; |
||||||
|
protected int port = 27017; |
||||||
|
protected String database = "logs"; |
||||||
|
protected String collection = null; |
||||||
|
protected String applicationId = System.getProperty("APPLICATION_ID", null); |
||||||
|
protected WriteConcern warnOrHigherWriteConcern = WriteConcern.SAFE; |
||||||
|
protected WriteConcern infoOrLowerWriteConcern = WriteConcern.NORMAL; |
||||||
|
protected Mongo mongo; |
||||||
|
protected DB db; |
||||||
|
|
||||||
|
public MongoLog4jAppender() { |
||||||
|
} |
||||||
|
|
||||||
|
public MongoLog4jAppender(boolean isActive) { |
||||||
|
super(isActive); |
||||||
|
} |
||||||
|
|
||||||
|
public String getHost() { |
||||||
|
return host; |
||||||
|
} |
||||||
|
|
||||||
|
public void setHost(String host) { |
||||||
|
this.host = host; |
||||||
|
} |
||||||
|
|
||||||
|
public int getPort() { |
||||||
|
return port; |
||||||
|
} |
||||||
|
|
||||||
|
public void setPort(int port) { |
||||||
|
this.port = port; |
||||||
|
} |
||||||
|
|
||||||
|
public String getDatabase() { |
||||||
|
return database; |
||||||
|
} |
||||||
|
|
||||||
|
public void setDatabase(String database) { |
||||||
|
this.database = database; |
||||||
|
} |
||||||
|
|
||||||
|
public String getCollection() { |
||||||
|
return collection; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCollection(String collection) { |
||||||
|
this.collection = collection; |
||||||
|
} |
||||||
|
|
||||||
|
public String getApplicationId() { |
||||||
|
return applicationId; |
||||||
|
} |
||||||
|
|
||||||
|
public void setApplicationId(String applicationId) { |
||||||
|
this.applicationId = applicationId; |
||||||
|
} |
||||||
|
|
||||||
|
public void setWarnOrHigherWriteConcern(String wc) { |
||||||
|
this.warnOrHigherWriteConcern = WriteConcern.valueOf(wc); |
||||||
|
} |
||||||
|
|
||||||
|
public String getWarnOrHigherWriteConcern() { |
||||||
|
return warnOrHigherWriteConcern.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public String getInfoOrLowerWriteConcern() { |
||||||
|
return infoOrLowerWriteConcern.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public void setInfoOrLowerWriteConcern(String wc) { |
||||||
|
this.infoOrLowerWriteConcern = WriteConcern.valueOf(wc); |
||||||
|
} |
||||||
|
|
||||||
|
protected void connectToMongo() throws UnknownHostException { |
||||||
|
this.mongo = new Mongo(host, port); |
||||||
|
this.db = mongo.getDB(database); |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked"}) |
||||||
|
@Override |
||||||
|
protected void append(final LoggingEvent event) { |
||||||
|
if (null == db) { |
||||||
|
try { |
||||||
|
connectToMongo(); |
||||||
|
} catch (UnknownHostException e) { |
||||||
|
throw new RuntimeException(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
BasicDBObject dbo = new BasicDBObject(); |
||||||
|
dbo.put(APP_ID, applicationId); |
||||||
|
dbo.put(NAME, event.getLogger().getName()); |
||||||
|
dbo.put(LEVEL, event.getLevel().toString()); |
||||||
|
dbo.put(TIMESTAMP, String.format("%s", event.getTimeStamp())); |
||||||
|
|
||||||
|
// Copy properties into document
|
||||||
|
Map<Object, Object> props = event.getProperties(); |
||||||
|
if (null != props && props.size() > 0) { |
||||||
|
BasicDBObject propsDbo = new BasicDBObject(); |
||||||
|
for (Map.Entry<Object, Object> entry : props.entrySet()) { |
||||||
|
propsDbo.put(entry.getKey().toString(), entry.getValue().toString()); |
||||||
|
} |
||||||
|
dbo.put(PROPERTIES, propsDbo); |
||||||
|
} |
||||||
|
|
||||||
|
// Copy traceback info (if there is any) into the document
|
||||||
|
String[] traceback = event.getThrowableStrRep(); |
||||||
|
if (null != traceback && traceback.length > 0) { |
||||||
|
BasicDBList tbDbo = new BasicDBList(); |
||||||
|
tbDbo.addAll(Arrays.asList(traceback)); |
||||||
|
dbo.put(TRACEBACK, tbDbo); |
||||||
|
} |
||||||
|
|
||||||
|
// Put the rendered message into the document
|
||||||
|
dbo.put(MESSAGE, event.getRenderedMessage()); |
||||||
|
|
||||||
|
// Insert the document
|
||||||
|
if (null == collection) { |
||||||
|
// Use the category name
|
||||||
|
collection = event.getLogger().getName(); |
||||||
|
} else { |
||||||
|
Calendar now = Calendar.getInstance(); |
||||||
|
collection = String.format(collection, |
||||||
|
now.get(Calendar.YEAR), |
||||||
|
now.get(Calendar.MONTH), |
||||||
|
now.get(Calendar.DAY_OF_MONTH), |
||||||
|
now.get(Calendar.HOUR_OF_DAY), |
||||||
|
event.getLevel().toString(), |
||||||
|
event.getLogger().getName()); |
||||||
|
} |
||||||
|
|
||||||
|
WriteConcern wc; |
||||||
|
if (event.getLevel().isGreaterOrEqual(Level.WARN)) { |
||||||
|
wc = warnOrHigherWriteConcern; |
||||||
|
} else { |
||||||
|
wc = infoOrLowerWriteConcern; |
||||||
|
} |
||||||
|
db.getCollection(collection).insert(dbo, wc); |
||||||
|
} |
||||||
|
|
||||||
|
public void close() { |
||||||
|
mongo.close(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean requiresLayout() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,71 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2011 by the original author(s). |
||||||
|
* |
||||||
|
* 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.document.mongodb.log4j; |
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*; |
||||||
|
import static org.junit.Assert.*; |
||||||
|
|
||||||
|
import java.net.UnknownHostException; |
||||||
|
|
||||||
|
import com.mongodb.DB; |
||||||
|
import com.mongodb.DBCursor; |
||||||
|
import com.mongodb.Mongo; |
||||||
|
import org.apache.log4j.Logger; |
||||||
|
import org.apache.log4j.MDC; |
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Jon Brisbin <jbrisbin@vmware.com> |
||||||
|
*/ |
||||||
|
public class AppenderTest { |
||||||
|
|
||||||
|
private static final String NAME = AppenderTest.class.getName(); |
||||||
|
private Logger log = Logger.getLogger(NAME); |
||||||
|
private Mongo mongo; |
||||||
|
private DB db; |
||||||
|
|
||||||
|
@Before |
||||||
|
public void setup() { |
||||||
|
try { |
||||||
|
mongo = new Mongo("localhost", 27017); |
||||||
|
db = mongo.getDB("logs"); |
||||||
|
db.getCollection(NAME).drop(); |
||||||
|
} catch (UnknownHostException e) { |
||||||
|
throw new RuntimeException(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testLogging() { |
||||||
|
log.debug("DEBUG message"); |
||||||
|
log.info("INFO message"); |
||||||
|
log.warn("WARN message"); |
||||||
|
log.error("ERROR message"); |
||||||
|
|
||||||
|
DBCursor msgs = db.getCollection(NAME).find(); |
||||||
|
assertThat(msgs.count(), is(4)); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testProperties() { |
||||||
|
MDC.put("property", "one"); |
||||||
|
log.debug("DEBUG message"); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
log4j.rootCategory=INFO, stdout |
||||||
|
|
||||||
|
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.applicationId = my.application |
||||||
|
log4j.appender.stdout.warnOrHigherWriteConcern = FSYNC_SAFE |
||||||
|
|
||||||
|
log4j.category.org.apache.activemq=ERROR |
||||||
|
log4j.category.org.springframework.batch=DEBUG |
||||||
|
log4j.category.org.springframework.data.document.mongodb=DEBUG |
||||||
|
log4j.category.org.springframework.transaction=INFO |
||||||
|
|
||||||
|
log4j.category.org.hibernate.SQL=DEBUG |
||||||
|
# for debugging datasource initialization |
||||||
|
# log4j.category.test.jdbc=DEBUG |
||||||
Loading…
Reference in new issue