Permalink
Browse files

Merge pull request #9 from griphiam/master

Added Queue to Appender
  • Loading branch information...
2 parents cf0a47f + 3adf691 commit 44fdbf8cba7b7d5820df84a88e52b94959f01296 @kencochrane kencochrane committed Apr 1, 2012
View
@@ -4,6 +4,8 @@ Raven-java is a Java client for Sentry. It is a basic log4j appender that will s
This is a very raw project at the moment it still needs some more TLC and testing before I would consider it production ready.
+The log4j appender is asyncronous by design so there is no need to put it in a AsyncAppender.
+
Installation
------------
@@ -28,7 +30,7 @@ you'll find in the target directory of the project.
<dependency>
<groupId>net.kencochrane</groupId>
<artifactId>raven-java</artifactId>
- <version>0.4-SNAPSHOT</version>
+ <version>0.5-SNAPSHOT</version>
</dependency>
**Option 2**: add the plain jar and the jar files of all dependencies to your classpath
@@ -60,6 +62,22 @@ If you need to use a proxy for HTTP transport, you can configure it as well::
log4j.appender.sentry.proxy=HTTP:proxyhost:proxyport
+Queue Size
+^^^^^^^^^^
+By default, the appender is configured with a queue of 1000 events. To tune this parameter:
+
+ log4j.appender.sentry.queue_size=100
+
+Blocking
+^^^^^^^^
+By default, the appender is non-blocking. If the queue is filled then log events will be written to Standard Error.
+If you want to make sure log events always reach sentry, you can turn blocking on:
+
+ log4j.appender.sentry.blocking=true
+
+WARNING: By setting blocking true, you will effectively lock up the thread doing the logging! Use with care.
+
+
SENTRY_DSN Environment Variable
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Raven-Java will first look to see if there is an environment variable called ``SENTRY_DSN`` set before it looks at the log4j config. If the environment variable is set, it will use that value instead of the one set in ``log4j.appender.sentry.sentry_dsn``. This is for a few reasons.
@@ -145,6 +163,8 @@ TODO
History
-------
+- 0.5
+ - Added async support
- 0.4
- Added the ability to get the SENTRY_DSN from the ENV
- Added RavenClient.captureMessage
@@ -165,4 +185,5 @@ Contributors
------------
- Ken Cochrane (@KenCochrane)
- Kevin Wetzels (@roambe)
-- David Cramer (@zeeg)
+- David Cramer (@zeeg)
+- Mark Philpot (@griphiam)
View
@@ -6,7 +6,7 @@
<groupId>net.kencochrane</groupId>
<artifactId>raven-java</artifactId>
- <version>0.4-SNAPSHOT</version>
+ <version>0.5-SNAPSHOT</version>
<packaging>jar</packaging>
<name>raven-java</name>
<description>Java Raven client and log4j appender.</description>
@@ -13,6 +13,14 @@
private String sentry_dsn;
private String proxy;
+ private int queue_size;
+ private boolean blocking;
+
+ public SentryAppender()
+ {
+ queue_size = 1000;
+ blocking = false;
+ }
public String getSentry_dsn() {
return sentry_dsn;
@@ -30,6 +38,22 @@ public void setProxy(String proxy) {
this.proxy = proxy;
}
+ public int getQueue_size() {
+ return queue_size;
+ }
+
+ public void setQueue_size(int queue_size) {
+ this.queue_size = queue_size;
+ }
+
+ public boolean getBlocking() {
+ return blocking;
+ }
+
+ public void setBlocking(boolean blocking) {
+ this.blocking = blocking;
+ }
+
/**
* Look for the ENV variable first, and if it isn't there, then look in the log4j properties
*
@@ -51,42 +75,24 @@ protected void append(LoggingEvent loggingEvent) {
//find the sentry DSN.
String sentryDSN = findSentryDSN();
- synchronized (this) {
- try {
- // get timestamp and timestamp in correct string format.
- long timestamp = loggingEvent.getTimeStamp();
-
- // get the log and info about the log.
- String logMessage = loggingEvent.getRenderedMessage();
- String loggingClass = loggingEvent.getLogger().getName();
- int logLevel = (loggingEvent.getLevel().toInt() / 1000); //Need to divide by 1000 to keep consistent with sentry
- String culprit = loggingEvent.getLoggerName();
-
- // create the client passing in the sentry DSN from the log4j properties file.
- RavenClient client = new RavenClient(sentryDSN, getProxy());
-
- // is it an exception?
- ThrowableInformation throwableInformation = loggingEvent.getThrowableInformation();
-
- // send the message to the sentry server
- if (throwableInformation == null){
- client.captureMessage(logMessage, timestamp, loggingClass, logLevel, culprit);
- }else{
- client.captureException(logMessage, timestamp, loggingClass, logLevel, culprit, throwableInformation.getThrowable());
- }
-
- } catch (Exception e) {
- System.err.println(e);
+ synchronized (this)
+ {
+ if(!SentryQueue.getInstance().isSetup())
+ {
+ SentryQueue.getInstance().setup(sentryDSN, getProxy(), queue_size, blocking);
}
}
+ SentryQueue.getInstance().addEvent(loggingEvent);
}
- public void close() {
- // clean up normally goes here. but there isn't any
+ public void close()
+ {
+ SentryQueue.getInstance().shutdown();
}
- public boolean requiresLayout() {
+ public boolean requiresLayout()
+ {
return false;
}
}
@@ -0,0 +1,75 @@
+package net.kencochrane.sentry;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * User: mphilpot
+ * Date: 3/29/12
+ */
+public class SentryQueue
+{
+ private static SentryQueue ourInstance = new SentryQueue();
+ private static BlockingQueue<LoggingEvent> queue;
+
+ private SentryWorker worker;
+ private boolean blocking;
+
+ public static SentryQueue getInstance()
+ {
+ return ourInstance;
+ }
+
+ private SentryQueue()
+ {
+ queue = null;
+
+ worker = null;
+
+ }
+
+ public void shutdown()
+ {
+ worker.shutdown();
+ worker.interrupt();
+ }
+
+ public synchronized boolean isSetup()
+ {
+ return (queue != null);
+ }
+
+ public synchronized void setup(String sentryDSN, String proxy, int queueSize, boolean blocking)
+ {
+ queue = new LinkedBlockingQueue<LoggingEvent>(queueSize);
+ this.blocking = blocking;
+
+ worker = new SentryWorker(queue, sentryDSN, proxy);
+ worker.start();
+ }
+
+ public void addEvent(LoggingEvent le)
+ {
+ try
+ {
+ if(blocking)
+ {
+ queue.put(le);
+ }
+ else
+ {
+ queue.add(le);
+ }
+ }
+ catch(IllegalStateException e)
+ {
+ System.err.println("Sentry Queue Full :: " + le);
+ }
+ catch(InterruptedException e)
+ {
+ System.err.println("Sentry Queue Interrupted :: "+ le);
+ }
+ }
+}
@@ -0,0 +1,83 @@
+package net.kencochrane.sentry;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.ThrowableInformation;
+
+import java.util.concurrent.BlockingQueue;
+
+/**
+ * User: mphilpot
+ * Date: 3/29/12
+ */
+public class SentryWorker extends Thread
+{
+ private boolean shouldShutdown;
+
+ private RavenClient client;
+
+ private BlockingQueue<LoggingEvent> queue;
+
+ public SentryWorker(BlockingQueue<LoggingEvent> queue, String sentryDSN, String proxy)
+ {
+ this.shouldShutdown = false;
+ this.queue = queue;
+ this.client = new RavenClient(sentryDSN, proxy);
+ }
+
+ @Override
+ public void run()
+ {
+ while(!shouldShutdown)
+ {
+ try
+ {
+ LoggingEvent le = queue.take();
+
+ sendToSentry(le);
+ }
+ catch (InterruptedException e)
+ {
+ // Thread interrupted... probably shutting down
+ }
+ }
+ }
+
+ public void shutdown()
+ {
+ shouldShutdown = true;
+ }
+
+ public void sendToSentry(LoggingEvent loggingEvent)
+ {
+ synchronized (this)
+ {
+ try
+ {
+ // get timestamp and timestamp in correct string format.
+ long timestamp = loggingEvent.getTimeStamp();
+
+ // get the log and info about the log.
+ String logMessage = loggingEvent.getRenderedMessage();
+ String loggingClass = loggingEvent.getLogger().getName();
+ int logLevel = (loggingEvent.getLevel().toInt() / 1000); //Need to divide by 1000 to keep consistent with sentry
+ String culprit = loggingEvent.getLoggerName();
+
+ // is it an exception?
+ ThrowableInformation throwableInformation = loggingEvent.getThrowableInformation();
+
+ // send the message to the sentry server
+ if (throwableInformation == null){
+ client.captureMessage(logMessage, timestamp, loggingClass, logLevel, culprit);
+ }else{
+ client.captureException(logMessage, timestamp, loggingClass, logLevel, culprit, throwableInformation.getThrowable());
+ }
+
+ } catch (Exception e)
+ {
+ // Can we tell if there is another logger to send the event to?
+ System.err.println(e);
+ }
+ }
+ }
+
+}
@@ -0,0 +1,45 @@
+package net.kencochrane.sentry;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+import org.junit.Test;
+
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * User: mphilpot
+ * Date: 3/29/12
+ */
+public class PerformanceTest
+{
+ static final Logger log = Logger.getLogger(PerformanceTest.class);
+
+ @Test
+ public void testPerformance() throws InterruptedException
+ {
+ PropertyConfigurator.configure(getClass().getResource("/log4j_configuration.txt"));
+
+ Date start = new Date();
+ for(int i = 0; i < 100; i++)
+ {
+ log.info("Simple log message w/ no exception");
+ }
+ Date end = new Date();
+
+ System.out.println(String.format("Simple test :: %d ms", end.getTime() - start.getTime()));
+
+ Date startE = new Date();
+ Exception e = new Exception("Test Exception");
+ for(int i = 0; i < 100; i++ )
+ {
+ log.warn("Log message w/ exception", e);
+ }
+ Date endE = new Date();
+
+ System.out.println(String.format("Exception test :: %d ms", endE.getTime() - startE.getTime()));
+
+ // To see the messages get sent to the server
+ //TimeUnit.SECONDS.sleep(30);
+ }
+}

0 comments on commit 44fdbf8

Please sign in to comment.