diff --git a/.gitignore b/.gitignore
index 9dbdbbfd78..bfe9da4a1d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
*.pyc
*.DS_Store
-out/*
\ No newline at end of file
+out/*
+target/*
+.idea/*
\ No newline at end of file
diff --git a/.idea/libraries/common.xml b/.idea/libraries/common.xml
deleted file mode 100644
index 80734af5ac..0000000000
--- a/.idea/libraries/common.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/README.rst b/README.rst
index 0521d50636..35bfacfc7f 100644
--- a/README.rst
+++ b/README.rst
@@ -6,11 +6,29 @@ This is a very raw project at the moment it still needs some more TLC and testin
Installation
------------
-Copy the raven-java-0.2.jar file to your java classpath and then configure log4j to use the SentryAppender.
+You'll need Maven 2 to build the project::
-The raven-java-0.2.jar is a self contained jar file, all dependencies are included, so this jar should be all you need.
+ $ cd raven-java
+ $ mvn package -Dmaven.test.skip
-There is an example project checked into github.com where you can see an example log4j config file.
+The last step will build the standalone raven-java jar file but also a jar file containing raven-java and all dependencies, which
+you'll find in the target directory of the project.
+
+**Option 1**: add raven-java as a dependency when you're using Maven::
+
+
+ net.kencochrane
+ raven-java
+ 1.0-SNAPSHOT
+
+
+**Option 2**: add the plain jar and the jar files of all dependencies to your classpath
+
+**Option 3**: add the self contained jar file to your classpath
+
+Log4J configuration
+-------------------
+Check out src/test/java/resources/log4j_configuration.txt where you can see an example log4j config file.
You will need to add the SentryAppender and the sentry_dsn properties.
@@ -24,15 +42,23 @@ Log4j Config example::
log4j.appender.sentry=net.kencochrane.sentry.SentryAppender
log4j.appender.sentry.sentry_dsn=http://b4935bdd78624092ac2bc70fdcdb6f5a:7a37d9ad4765428180316bfec91a27ef@localhost:8000/1
+Proxy
+~~~~~
+If you need to use a proxy for HTTP transport, you can configure it as well::
+
+ log4j.appender.sentry.proxy=HTTP:proxyhost:proxyport
Sentry Versions Supported
-------------------------
This client has been tested with Sentry 2.7 and 2.8, and only very briefly.
+Other
+-----
+If you want to generate the javadocs for this project, simply run ``mvn javadoc:javadoc`` and you'll be able to browse the
+docs from the target directory of the project.
+
TODO
----
-- Add a ant task to build the jar files (I made this first one from intellij (10.5 community edition) File-> Project structure -> artifacts.
-- Make this maven friendly. Not familiar with Maven, so maybe someone else can help with this.
- Create better documentation
- Add unit tests
- Add more examples
diff --git a/example/run.sh b/example/run.sh
deleted file mode 100755
index 59502a869d..0000000000
--- a/example/run.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env bash
-
-java -classpath /Users/kcochrane/github/raven-java/out/production/raven-java:../lib/log4j-1.2.16.jar:../lib/httpmime-4.1.2.jar:../lib/commons-codec-1.4.jar:../lib/httpcore-4.1.2.jar:../lib/httpclient-4.1.2.jar:../lib/commons-logging-1.1.1.jar:../lib/json_simple-1.1.jar net.kencochrane.sentry.SentryExample log4j_configuration.txt
diff --git a/example/run2.sh b/example/run2.sh
deleted file mode 100755
index 44a5d7d7bd..0000000000
--- a/example/run2.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env bash
-
-java -classpath ../raven-java-0.2.jar net.kencochrane.sentry.SentryExample log4j_configuration.txt
diff --git a/example/src/net/kencochrane/sentry/SentryExample.java b/example/src/net/kencochrane/sentry/SentryExample.java
deleted file mode 100644
index 16daedf280..0000000000
--- a/example/src/net/kencochrane/sentry/SentryExample.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package net.kencochrane.sentry;
-
-/**
- * User: ken cochrane
- * Date: 2/6/12
- * Time: 11:35 AM
- */
-
- // Import log4j classes.
- import org.apache.log4j.Logger;
- import org.apache.log4j.PropertyConfigurator;
-
-/**
- * Simple example used to test out the sentry logger.
- */
- public class SentryExample {
-
- // Define a static logger variable so that it references the
- // Logger instance named "MyApp".
- static final Logger logger = Logger.getLogger(SentryExample.class);
-
- public static void main(String[] args) {
-
- // PropertyConfigurator.
- PropertyConfigurator.configure(args[0]);
-
- logger.debug("Debug example");
- logger.error("Error example");
- logger.trace("Trace Example");
- logger.fatal("Fatal Example");
- logger.info("info Example");
- logger.warn("Warn Example");
- }
- }
diff --git a/lib/commons-codec-1.4.jar b/lib/commons-codec-1.4.jar
deleted file mode 100644
index 458d432da8..0000000000
Binary files a/lib/commons-codec-1.4.jar and /dev/null differ
diff --git a/lib/commons-logging-1.1.1.jar b/lib/commons-logging-1.1.1.jar
deleted file mode 100644
index 1deef144cb..0000000000
Binary files a/lib/commons-logging-1.1.1.jar and /dev/null differ
diff --git a/lib/httpclient-4.1.2.jar b/lib/httpclient-4.1.2.jar
deleted file mode 100644
index b3cdb4cdc8..0000000000
Binary files a/lib/httpclient-4.1.2.jar and /dev/null differ
diff --git a/lib/httpcore-4.1.2.jar b/lib/httpcore-4.1.2.jar
deleted file mode 100644
index 66ae90b0d1..0000000000
Binary files a/lib/httpcore-4.1.2.jar and /dev/null differ
diff --git a/lib/httpmime-4.1.2.jar b/lib/httpmime-4.1.2.jar
deleted file mode 100644
index eea3b3ff17..0000000000
Binary files a/lib/httpmime-4.1.2.jar and /dev/null differ
diff --git a/lib/json_simple-1.1.jar b/lib/json_simple-1.1.jar
deleted file mode 100644
index f395f41471..0000000000
Binary files a/lib/json_simple-1.1.jar and /dev/null differ
diff --git a/lib/log4j-1.2.16.jar b/lib/log4j-1.2.16.jar
deleted file mode 100644
index 3f9d847618..0000000000
Binary files a/lib/log4j-1.2.16.jar and /dev/null differ
diff --git a/pom.xml b/pom.xml
index d2236b46b3..d968a4b83b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,9 +23,9 @@
1.6
- org.json
- json
- 20090211
+ com.googlecode.json-simple
+ json-simple
+ 1.1
log4j
@@ -58,6 +58,24 @@
UTF-8
+
+ maven-assembly-plugin
+ 2.3
+
+
+ jar-with-dependencies
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF
deleted file mode 100644
index 87a3423ac9..0000000000
--- a/src/META-INF/MANIFEST.MF
+++ /dev/null
@@ -1,3 +0,0 @@
-Manifest-Version: 1.0
-Main-Class: net.kencochrane.sentry.SentryAppender
-
diff --git a/src/net/kencochrane/sentry/RavenClient.java b/src/main/java/net/kencochrane/sentry/RavenClient.java
similarity index 69%
rename from src/net/kencochrane/sentry/RavenClient.java
rename to src/main/java/net/kencochrane/sentry/RavenClient.java
index 0819ced448..7f746802ce 100644
--- a/src/net/kencochrane/sentry/RavenClient.java
+++ b/src/main/java/net/kencochrane/sentry/RavenClient.java
@@ -1,5 +1,6 @@
package net.kencochrane.sentry;
+import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.io.IOException;
@@ -58,11 +59,17 @@ public void setSentryDSN(String sentryDSN) {
* @param culprit Who we think caused the problem.
* @return JSON String of message body
*/
- private String buildJSON(String message, String timestamp, String loggerClass, int logLevel, String culprit) {
+ private String buildJSON(String message, String timestamp, String loggerClass, int logLevel, String culprit, Throwable exception) {
JSONObject obj = new JSONObject();
obj.put("event_id", RavenUtils.getRandomUUID()); //Hexadecimal string representing a uuid4 value.
obj.put("checksum", RavenUtils.calculateChecksum(message));
- obj.put("culprit", culprit);
+ if (exception == null) {
+ obj.put("culprit", culprit);
+ } else {
+ obj.put("culprit", determineCulprit(exception));
+ obj.put("sentry.interfaces.Exception", buildException(exception));
+ obj.put("sentry.interfaces.Stacktrace", buildStacktrace(exception));
+ }
obj.put("timestamp", timestamp);
obj.put("message", message);
obj.put("project", getConfig().getProjectId());
@@ -72,6 +79,63 @@ private String buildJSON(String message, String timestamp, String loggerClass, i
return obj.toJSONString();
}
+ /**
+ * Determines the class and method name where the root cause exception occurred.
+ *
+ * @param exception exception
+ * @return the culprit
+ */
+ private String determineCulprit(Throwable exception) {
+ Throwable cause = exception;
+ String culprit = null;
+ while (cause != null) {
+ StackTraceElement[] elements = cause.getStackTrace();
+ if (elements.length > 0) {
+ StackTraceElement trace = elements[0];
+ culprit = trace.getClassName() + "." + trace.getMethodName();
+ }
+ cause = cause.getCause();
+ }
+ return culprit;
+ }
+
+ private JSONObject buildException(Throwable exception) {
+ JSONObject json = new JSONObject();
+ json.put("type", exception.getClass().getSimpleName());
+ json.put("value", exception.getMessage());
+ json.put("module", exception.getClass().getPackage().getName());
+ return json;
+ }
+
+ private JSONObject buildStacktrace(Throwable exception) {
+ JSONArray array = new JSONArray();
+ Throwable cause = exception;
+ while (cause != null) {
+ StackTraceElement[] elements = cause.getStackTrace();
+ for (int index = 0; index < elements.length; ++index) {
+ if (index == 0) {
+ JSONObject causedByFrame = new JSONObject();
+ String msg = "Caused by: " + cause.getClass().getName();
+ if (cause.getMessage() != null) {
+ msg += " (\"" + cause.getMessage() + "\")";
+ }
+ causedByFrame.put("filename", msg);
+ causedByFrame.put("lineno", -1);
+ array.add(causedByFrame);
+ }
+ StackTraceElement element = elements[index];
+ JSONObject frame = new JSONObject();
+ frame.put("filename", element.getClassName());
+ frame.put("function", element.getMethodName());
+ frame.put("lineno", element.getLineNumber());
+ array.add(frame);
+ }
+ cause = cause.getCause();
+ }
+ JSONObject stacktrace = new JSONObject();
+ stacktrace.put("frames", array);
+ return stacktrace;
+ }
/**
* Take the raw message body and get it ready for sending. Encode and compress it.
@@ -98,11 +162,12 @@ private String buildMessageBody(String jsonMessage) {
* @param loggerClass The class associated with the log message
* @param logLevel int value for Log level for message (DEBUG, ERROR, INFO, etc.)
* @param culprit Who we think caused the problem.
+ * @param exception exception causing the problem
* @return Encode and compressed version of the JSON Message body
*/
- private String buildMessage(String message, String timestamp, String loggerClass, int logLevel, String culprit) {
+ private String buildMessage(String message, String timestamp, String loggerClass, int logLevel, String culprit, Throwable exception) {
// get the json version of the body
- String jsonMessage = buildJSON(message, timestamp, loggerClass, logLevel, culprit);
+ String jsonMessage = buildJSON(message, timestamp, loggerClass, logLevel, culprit, exception);
// compress and encode the json message.
return buildMessageBody(jsonMessage);
@@ -183,11 +248,13 @@ private void sendMessage(String messageBody, long timestamp) {
* @param loggerClass The class associated with the log message
* @param logLevel int value for Log level for message (DEBUG, ERROR, INFO, etc.)
* @param culprit Who we think caused the problem.
+ * @param exception exception that occurred
*/
- public void logMessage(String theLogMessage, long timestamp, String loggerClass, int logLevel, String culprit) {
+ public void logMessage(String theLogMessage, long timestamp, String loggerClass, int logLevel, String culprit, Throwable exception) {
String timestampDate = RavenUtils.getTimestampString(timestamp);
- String message = buildMessage(theLogMessage, timestampDate, loggerClass, logLevel, culprit);
+ String message = buildMessage(theLogMessage, timestampDate, loggerClass, logLevel, culprit, exception);
sendMessage(message, timestamp);
}
+
}
\ No newline at end of file
diff --git a/src/net/kencochrane/sentry/RavenConfig.java b/src/main/java/net/kencochrane/sentry/RavenConfig.java
similarity index 100%
rename from src/net/kencochrane/sentry/RavenConfig.java
rename to src/main/java/net/kencochrane/sentry/RavenConfig.java
diff --git a/src/net/kencochrane/sentry/RavenUtils.java b/src/main/java/net/kencochrane/sentry/RavenUtils.java
similarity index 94%
rename from src/net/kencochrane/sentry/RavenUtils.java
rename to src/main/java/net/kencochrane/sentry/RavenUtils.java
index dc06b48515..049fdb5a97 100644
--- a/src/net/kencochrane/sentry/RavenUtils.java
+++ b/src/main/java/net/kencochrane/sentry/RavenUtils.java
@@ -1,5 +1,7 @@
package net.kencochrane.sentry;
+import org.apache.commons.lang.time.DateFormatUtils;
+
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
@@ -7,7 +9,6 @@
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.SignatureException;
-import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
import java.util.zip.CRC32;
@@ -25,7 +26,6 @@
public class RavenUtils {
private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
- private static final SimpleDateFormat ISO8601FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
/**
* Computes RFC 2104-compliant HMAC signature.
@@ -83,10 +83,7 @@ public static String getHostname() {
* @return ISO8601 formatted date as a String
*/
public static String getDateAsISO8601String(Date date) {
- String result = ISO8601FORMAT.format(date);
- result = result.substring(0, result.length() - 2)
- + ":" + result.substring(result.length() - 2);
- return result;
+ return DateFormatUtils.ISO_DATETIME_FORMAT.format(date);
}
/**
@@ -95,8 +92,7 @@ public static String getDateAsISO8601String(Date date) {
* @return timestamp for now as long
*/
public static long getTimestampLong() {
- java.util.Date date = new java.util.Date();
- return date.getTime();
+ return System.currentTimeMillis();
}
/**
diff --git a/src/net/kencochrane/sentry/SentryAppender.java b/src/main/java/net/kencochrane/sentry/SentryAppender.java
similarity index 86%
rename from src/net/kencochrane/sentry/SentryAppender.java
rename to src/main/java/net/kencochrane/sentry/SentryAppender.java
index 10dd3efc4a..a6c9184a44 100644
--- a/src/net/kencochrane/sentry/SentryAppender.java
+++ b/src/main/java/net/kencochrane/sentry/SentryAppender.java
@@ -2,6 +2,7 @@
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.ThrowableInformation;
/**
* User: ken cochrane
@@ -52,7 +53,9 @@ protected void append(LoggingEvent loggingEvent) {
RavenClient client = new RavenClient(getSentry_dsn(), getProxy());
// send the message to the sentry server
- client.logMessage(logMessage, timestamp, loggingClass, logLevel, culprit);
+ ThrowableInformation throwableInformation = loggingEvent.getThrowableInformation();
+ Throwable throwable = (throwableInformation == null ? null : throwableInformation.getThrowable());
+ client.logMessage(logMessage, timestamp, loggingClass, logLevel, culprit, throwable);
} catch (Exception e) {
System.err.println(e);
diff --git a/src/test/java/net/kencochrane/sentry/SentryExample.java b/src/test/java/net/kencochrane/sentry/SentryExample.java
new file mode 100644
index 0000000000..52839ad56b
--- /dev/null
+++ b/src/test/java/net/kencochrane/sentry/SentryExample.java
@@ -0,0 +1,55 @@
+package net.kencochrane.sentry;
+
+/**
+ * User: ken cochrane
+ * Date: 2/6/12
+ * Time: 11:35 AM
+ */
+
+// Import log4j classes.
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+import org.junit.Test;
+
+/**
+ * Simple example used to test out the sentry logger.
+ */
+public class SentryExample {
+
+ // Define a static logger variable so that it references the
+ // Logger instance named "MyApp".
+ static final Logger logger = Logger.getLogger(SentryExample.class);
+
+ public void triggerRuntimeException() {
+ try {
+ triggerNullPointer();
+ } catch (Exception e) {
+ throw new RuntimeException("Error triggering null pointer", e);
+ }
+ }
+
+ public String triggerNullPointer() {
+ String c = null;
+ return c.toLowerCase();
+ }
+
+ @Test
+ public void test_simple() {
+
+ // PropertyConfigurator.
+ PropertyConfigurator.configure(getClass().getResource("/log4j_configuration.txt"));
+
+ logger.debug("Debug example");
+ logger.error("Error example");
+ logger.trace("Trace Example");
+ logger.fatal("Fatal Example");
+ logger.info("info Example");
+ logger.warn("Warn Example");
+ try {
+ triggerRuntimeException();
+ } catch (RuntimeException e) {
+ logger.error("Error example with stacktrace", e);
+ }
+ }
+}
diff --git a/example/log4j_configuration.txt b/src/test/resources/log4j_configuration.txt
similarity index 99%
rename from example/log4j_configuration.txt
rename to src/test/resources/log4j_configuration.txt
index ab760c2587..7bba8f0e6c 100644
--- a/example/log4j_configuration.txt
+++ b/src/test/resources/log4j_configuration.txt
@@ -20,3 +20,4 @@ log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
log4j.appender.sentry=net.kencochrane.sentry.SentryAppender
log4j.appender.sentry.sentry_dsn=http://b4935bdd78624092ac2bc70fdcdb6f5a:7a37d9ad4765428180316bfec91a27ef@localhost:8000/1
+