Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit

Contains a working first draft of the appender.
  • Loading branch information...
commit a2f7b6cb4b82440a2606b60b0fc481138680eb9f 0 parents
@apetresc authored
1  .gitignore
@@ -0,0 +1 @@
+target/
42 pom.xml
@@ -0,0 +1,42 @@
+<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/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.twitsprout</groupId>
+ <artifactId>amazon-sns-log4j-appender</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>jar</packaging>
+
+ <name>amazon-sns-log4j-appender</name>
+ <url>http://github.com/apetresc/amazon-sns-log4j-appender</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <version>3.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>[1.2,)</version>
+ </dependency>
+ <dependency>
+ <groupId>com.amazonaws</groupId>
+ <artifactId>aws-java-sdk</artifactId>
+ <version>[1.2,)</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
111 src/main/java/com/twitsprout/appender/sns/SnsAsyncAppender.java
@@ -0,0 +1,111 @@
+package com.twitsprout.appender.sns;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.concurrent.Future;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.LoggingEvent;
+
+import com.amazonaws.AmazonClientException;
+import com.amazonaws.auth.PropertiesCredentials;
+import com.amazonaws.services.sns.AmazonSNSAsync;
+import com.amazonaws.services.sns.AmazonSNSAsyncClient;
+import com.amazonaws.services.sns.model.CreateTopicRequest;
+import com.amazonaws.services.sns.model.PublishRequest;
+import com.amazonaws.services.sns.model.PublishResult;
+
+public class SnsAsyncAppender extends AppenderSkeleton {
+ private final AmazonSNSAsync sns;
+ private String topicArn;
+
+ private boolean snsClosed;
+
+ public SnsAsyncAppender() {
+ try {
+ this.sns = new AmazonSNSAsyncClient(new PropertiesCredentials(
+ SnsAsyncAppender.class.getResourceAsStream("/AwsCredentials.properties")));
+ this.snsClosed = true;
+ } catch (IOException ioe) {
+ throw new RuntimeException("Could not instantiate SnsAsyncAppender", ioe);
+ }
+ }
+
+ public SnsAsyncAppender(String topicName) {
+ try {
+ this.sns = new AmazonSNSAsyncClient(new PropertiesCredentials(
+ SnsAsyncAppender.class.getResourceAsStream("/AwsCredentials.properties")));
+ this.topicArn = sns.createTopic(new CreateTopicRequest(topicName)).getTopicArn();
+ this.snsClosed = false;
+ } catch (IOException ioe) {
+ throw new RuntimeException("Could not instantiate SnsAsyncAppender", ioe);
+ } catch (AmazonClientException ace) {
+ throw new RuntimeException("Could not instantiate SnsAsyncAppender", ace);
+ }
+ }
+
+ /* For unit-testing purposes */
+ /* package */ SnsAsyncAppender(AmazonSNSAsync sns, String topicName) {
+ this.sns = sns;
+ this.topicArn = sns.createTopic(new CreateTopicRequest(topicName)).getTopicArn();
+ this.snsClosed = false;
+ }
+
+ public void setTopicName(String topicName) {
+ try {
+ topicArn = sns.createTopic(new CreateTopicRequest(topicName)).getTopicArn();
+ snsClosed = false;
+ } catch (Exception e) {
+ LogLog.error("Could not set topic name to: " + topicName);
+ snsClosed = true;
+ }
+ }
+
+ @Override
+ protected void append(LoggingEvent event) {
+ if (!checkEntryConditions()) {
+ return;
+ }
+
+ String logMessage;
+ if (layout != null) {
+ logMessage = layout.format(event);
+ } else {
+ logMessage = event.getRenderedMessage();
+ }
+ if (logMessage.getBytes().length > 64 * 1024) {
+ // SNS has a 64K limit on each published message.
+ logMessage = new String(Arrays.copyOf(logMessage.getBytes(), 64 * 1024));
+ }
+
+ try {
+ sns.publishAsync(new PublishRequest(
+ topicArn,
+ logMessage,
+ event.getLoggerName() + " log: " + event.getLevel().toString()
+ ));
+ } catch (AmazonClientException ase) {
+ LogLog.error("Could not log to SNS", ase);
+ }
+ }
+
+ protected boolean checkEntryConditions() {
+ return !snsClosed;
+ }
+
+ public String getTopicArn() {
+ return topicArn;
+ }
+
+ public void close() {
+ if (snsClosed) return;
+ snsClosed = true;
+ sns.shutdown();
+ }
+
+ public boolean requiresLayout() {
+ return false;
+ }
+
+}
20 src/test/java/com/twitsprout/appender/sns/SampleApp.java
@@ -0,0 +1,20 @@
+package com.twitsprout.appender.sns;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+
+public class SampleApp {
+
+ static Logger logger = Logger.getLogger(SampleApp.class);
+
+ public static void main(String[] args) throws IOException {
+ Properties log4jProperties = new Properties();
+ log4jProperties.load(SampleApp.class.getResourceAsStream("/log4j.properties.sample"));
+ PropertyConfigurator.configure(log4jProperties);
+
+ logger.fatal("This is a test");
+ }
+}
120 src/test/java/com/twitsprout/appender/sns/SnsAsyncAppenderTest.java
@@ -0,0 +1,120 @@
+package com.twitsprout.appender.sns;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+import org.easymock.EasyMock;
+import org.easymock.IArgumentMatcher;
+
+import com.amazonaws.services.sns.AmazonSNSAsync;
+import com.amazonaws.services.sns.model.CreateTopicRequest;
+import com.amazonaws.services.sns.model.CreateTopicResult;
+import com.amazonaws.services.sns.model.PublishRequest;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+public class SnsAsyncAppenderTest extends TestCase {
+ private static final String SAMPLE_TOPIC_NAME = "log_topic";
+ private static final String SAMPLE_TOPIC_ARN = "log_topic_arn";
+ private static final String SAMPLE_SHORT_LOG_MESSAGE = "This is a short log message";
+
+ private Logger logger;
+
+ public void setUp() {
+ logger = Logger.getLogger(SnsAsyncAppenderTest.class);
+ }
+
+ private static class CreateTopicRequestArgumentMatcher implements IArgumentMatcher {
+ private final CreateTopicRequest expected;
+
+ public CreateTopicRequestArgumentMatcher(CreateTopicRequest expected) {
+ this.expected = expected;
+ }
+
+ public static CreateTopicRequest eqCreateTopicRequest(CreateTopicRequest expected) {
+ EasyMock.reportMatcher(new CreateTopicRequestArgumentMatcher(expected));
+ return null;
+ }
+
+ public boolean matches(Object argument) {
+ if (argument == null) return false;
+ if (!(argument instanceof CreateTopicRequest)) return false;
+ return expected.getName().equals(((CreateTopicRequest) argument).getName());
+ }
+
+ public void appendTo(StringBuffer buffer) {
+ buffer
+ .append("eqCreateTopicRequest(")
+ .append(expected.getClass().getName())
+ .append(" with topicName \"")
+ .append(expected.getName())
+ .append("\")");
+ }
+ }
+
+ private static class PublishRequestArgumentMatcher implements IArgumentMatcher {
+ private final PublishRequest expected;
+
+ public PublishRequestArgumentMatcher(PublishRequest expected) {
+ this.expected = expected;
+ }
+
+ public static PublishRequest eqPublishRequest(PublishRequest expected) {
+ EasyMock.reportMatcher(new PublishRequestArgumentMatcher(expected));
+ return null;
+ }
+
+ public boolean matches(Object argument) {
+ if (argument == null) return false;
+ if (!(argument instanceof PublishRequest)) return false;
+ PublishRequest req = (PublishRequest) argument;
+ return expected.getTopicArn().equals(req.getTopicArn())
+ && expected.getMessage().equals(req.getMessage())
+ && expected.getSubject().equals(req.getSubject());
+ }
+
+ public void appendTo(StringBuffer buffer) {
+ buffer
+ .append("eqPublishRequest(")
+ .append(expected.getClass().getName())
+ .append(" with message \"")
+ .append(expected.getMessage())
+ .append("\")");
+ }
+
+ }
+
+ public void testSnsAsyncAppender_constructor() {
+ AmazonSNSAsync mockSns = EasyMock.createMock(AmazonSNSAsync.class);
+ EasyMock.expect(mockSns.createTopic(
+ CreateTopicRequestArgumentMatcher.eqCreateTopicRequest(new CreateTopicRequest(SAMPLE_TOPIC_NAME))))
+ .andReturn(new CreateTopicResult().withTopicArn(SAMPLE_TOPIC_ARN));
+ EasyMock.replay(mockSns);
+
+ SnsAsyncAppender testAppender = new SnsAsyncAppender(mockSns, SAMPLE_TOPIC_NAME);
+ Assert.assertEquals(SAMPLE_TOPIC_ARN, testAppender.getTopicArn());
+ }
+
+ public void testAppend() {
+ AmazonSNSAsync mockSns = EasyMock.createMock(AmazonSNSAsync.class);
+ EasyMock.expect(mockSns.createTopic(
+ CreateTopicRequestArgumentMatcher.eqCreateTopicRequest(new CreateTopicRequest(SAMPLE_TOPIC_NAME))))
+ .andReturn(new CreateTopicResult().withTopicArn(SAMPLE_TOPIC_ARN));
+ EasyMock.expect(mockSns.publishAsync(
+ PublishRequestArgumentMatcher.eqPublishRequest(new PublishRequest(
+ SAMPLE_TOPIC_ARN,
+ SAMPLE_SHORT_LOG_MESSAGE,
+ logger.getName() + " log: FATAL"))))
+ .andReturn(null);
+ EasyMock.replay(mockSns);
+
+ SnsAsyncAppender testAppender = new SnsAsyncAppender(mockSns, SAMPLE_TOPIC_NAME);
+ LoggingEvent loggingEvent = new LoggingEvent("", logger, Level.FATAL, SAMPLE_SHORT_LOG_MESSAGE, null);
+ testAppender.append(loggingEvent);
+ }
+
+ public void test_Logger() {
+
+ }
+}
14 src/test/resources/log4j.properties.sample
@@ -0,0 +1,14 @@
+log4j.rootLogger=debug, stdout, R
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+
+# Pattern to output the caller's file name and line number.
+log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
+
+log4j.appender.R=com.twitsprout.appender.sns.SnsAsyncAppender
+log4j.appender.R.TopicName=amazon-log-test
+log4j.appender.R.Threshold=ERROR
+
+log4j.appender.R.layout=org.apache.log4j.PatternLayout
+log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
Please sign in to comment.
Something went wrong with that request. Please try again.