From 5d0088f23d5c854510f9e5679f33b1eec8d8adaa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Drahom=C3=ADr=20Karch=C5=88=C3=A1k?=
 <drahomir.karchnak@nic.cz>
Date: Thu, 28 Jul 2016 16:54:48 +0200
Subject: [PATCH] #421 WIP First use of Sentry/Raven for crash reporting...

---
 build.gradle                                |   1 +
 core/build.gradle                           |   1 +
 core/src/cz/nic/tablexia/Tablexia.java      |  12 ++
 core/src/cz/nic/tablexia/TablexiaRaven.java | 121 ++++++++++++++++++++
 4 files changed, 135 insertions(+)
 create mode 100644 core/src/cz/nic/tablexia/TablexiaRaven.java

diff --git a/build.gradle b/build.gradle
index e7ed8bb50..7b7048c0e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -400,6 +400,7 @@ project(":core") {
 	    compile "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
         compile "com.google.guava:guava:$guavaVersion"
 
+        compile 'com.getsentry.raven:raven:7.5.0'
 
         testCompile "junit:junit:4.11"
 		testCompile "com.badlogicgames.gdx:gdx-backend-headless:$gdxVersion"               
diff --git a/core/build.gradle b/core/build.gradle
index 6894b0f57..5a9c469ff 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -43,6 +43,7 @@ task writeTablexiaBuildConfig {
                 "    public final static Integer TABLEXIA_SERVER_PORT = ${project.hasProperty('TABLEXIA_SERVER_PORT') ? "$TABLEXIA_SERVER_PORT" : "null"};\n" +
                 "    public final static String  TABLEXIA_SERVER_SECRET = ${project.hasProperty('TABLEXIA_SERVER_SECRET') ? "\"$TABLEXIA_SERVER_SECRET\"" : "null"};\n" +
                 "    public final static String  FLURRY_KEY = ${project.hasProperty('TABLEXIA_FLURRY_KEY') ? "\"$TABLEXIA_FLURRY_KEY\"" : "null"};\n" +
+                "    public final static String  SENTRY_DSN_KEY = ${project.hasProperty('TABLEXIA_SENTRY_DSN_KEY') ? "\"$TABLEXIA_SENTRY_DSN_KEY\"" : "null"};\n" +
                 "\n" +
                 "}", BUILD_CONFIG_FILE_ENCODING)
     }
diff --git a/core/src/cz/nic/tablexia/Tablexia.java b/core/src/cz/nic/tablexia/Tablexia.java
index 8b809e02e..af66524ce 100644
--- a/core/src/cz/nic/tablexia/Tablexia.java
+++ b/core/src/cz/nic/tablexia/Tablexia.java
@@ -5,6 +5,8 @@ import com.badlogic.gdx.graphics.GL30;
 import com.badlogic.gdx.scenes.scene2d.InputEvent;
 import com.badlogic.gdx.scenes.scene2d.actions.Actions;
 import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
+import com.getsentry.raven.connection.EventSendFailureCallback;
+import com.getsentry.raven.event.Event;
 
 import net.engio.mbassy.bus.error.IPublicationErrorHandler;
 import net.engio.mbassy.bus.error.PublicationError;
@@ -68,6 +70,7 @@ public class Tablexia extends TablexiaApplication {
     private static IConnectionManager connectionManager;
 
     private final SQLConnectionType     sqlConnectionType;
+//    private final Raven                 raven;
     private MenuController              menuController;
     private TablexiaButton              backButton;
     private boolean                     backButtonVisibility;
@@ -111,6 +114,15 @@ public class Tablexia extends TablexiaApplication {
 				Log.err(ApplicationBus.class, error.getMessage(), error.getCause());
 			}
 		});
+
+        TablexiaRaven.start();
+        TablexiaRaven.enableCrashReporting();
+        TablexiaRaven.addEventSendFailureCallback(new EventSendFailureCallback() {
+            @Override
+            public void onFailure(Event event, Exception exception) {
+                Log.info(getClass(), "Event failure!");
+            }
+        });
     }
 
     private void loadingComplete() {
diff --git a/core/src/cz/nic/tablexia/TablexiaRaven.java b/core/src/cz/nic/tablexia/TablexiaRaven.java
new file mode 100644
index 000000000..edee5019b
--- /dev/null
+++ b/core/src/cz/nic/tablexia/TablexiaRaven.java
@@ -0,0 +1,121 @@
+package cz.nic.tablexia;
+
+import com.badlogic.gdx.Gdx;
+import com.getsentry.raven.DefaultRavenFactory;
+import com.getsentry.raven.Raven;
+import com.getsentry.raven.connection.Connection;
+import com.getsentry.raven.connection.EventSendFailureCallback;
+import com.getsentry.raven.dsn.Dsn;
+import com.getsentry.raven.event.Event;
+import com.getsentry.raven.event.EventBuilder;
+import com.getsentry.raven.event.interfaces.ExceptionInterface;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Created by drahomir on 7/28/16.
+ */
+public class TablexiaRaven {
+    // TABLEXIA RAVEN FACTORY
+    private class TablexiaRavenFactory extends DefaultRavenFactory {
+        private EventSendFailureCallback eventSendFailureCallback;
+
+        public Raven ravenInstance(String DSN, EventSendFailureCallback callback) {
+            eventSendFailureCallback = callback;
+            return createRavenInstance(new Dsn(DSN));
+        }
+
+        @Override
+        protected Connection createConnection(Dsn dsn) {
+            Connection connection = super.createConnection(dsn);
+
+            if(eventSendFailureCallback != null) connection.addEventSendFailureCallback(eventSendFailureCallback);
+
+            return connection;
+        }
+    }
+
+    //TABLEXIA RAVEN //TODO Refactor
+    private static TablexiaRaven instance;
+
+    private final EventSendFailureCallback DEFAULT_SEND_EVENT_FAILURE_CALLBACK = new EventSendFailureCallback() {
+        @Override
+        public void onFailure(Event event, Exception exception) {
+            //Calls all registered callbacks
+            for(EventSendFailureCallback eventSendFailureCallback : sendFailureCallbacks) eventSendFailureCallback.onFailure(event, exception);
+        }
+    };
+
+    private Set<EventSendFailureCallback> sendFailureCallbacks;
+    private Raven raven;
+
+    private TablexiaRaven(String DSN) {
+        this.raven = new TablexiaRavenFactory().ravenInstance(DSN, DEFAULT_SEND_EVENT_FAILURE_CALLBACK);
+        this.sendFailureCallbacks = new HashSet<EventSendFailureCallback>();
+
+        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+            @Override
+            public void uncaughtException(Thread t, Throwable e) {
+                sendExceptionEvent(t, e);
+            }
+        });
+    }
+
+    public static void start() {
+        if(TablexiaBuildConfig.SENTRY_DSN_KEY == null) {
+            return;
+        }
+
+        if(instance != null) {
+            return;
+        }
+
+        instance = new TablexiaRaven(TablexiaBuildConfig.SENTRY_DSN_KEY);
+    }
+
+    private static boolean isStarted() {
+        return instance != null;
+    }
+
+    //TODO - Must be called after start method which is not good
+    public static void enableCrashReporting() {
+        if(!isStarted()) {
+            return;
+        }
+
+        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+            @Override
+            public void uncaughtException(Thread t, Throwable e) {
+                instance.sendExceptionEvent(t, e);
+            }
+        });
+    }
+
+    //TODO - Use this for saving storing events in DB and sending 'em later
+    //TODO - Must be called after start method which is not good
+    public static void addEventSendFailureCallback(EventSendFailureCallback eventSendFailureCallback) {
+        if(!isStarted()) {
+            return;
+        }
+
+        if(eventSendFailureCallback != null) {
+            if(!instance.sendFailureCallbacks.contains(eventSendFailureCallback)) instance.sendFailureCallbacks.add(eventSendFailureCallback);
+        }
+    }
+
+    private void sendExceptionEvent(Thread t, Throwable e) {
+        EventBuilder eventBuilder = new EventBuilder()
+                .withMessage(e.getMessage())
+                .withSentryInterface(new ExceptionInterface(e))
+                .withTag("Platform", Gdx.app.getType().toString())
+                .withTag("Version", TablexiaBuildConfig.VERSION_NAME)
+                .withExtra("Language", TablexiaSettings.getInstance().getLocale().toString())
+                .withExtra("UserInfo", TablexiaSettings.getInstance().getSelectedUser())
+                .withExtra("UserUUID", TablexiaSettings.getInstance().getSelectedUser().getUuid())
+                .withLevel(Event.Level.ERROR);
+
+        raven.runBuilderHelpers(eventBuilder);
+        raven.sendEvent(eventBuilder.build());
+    }
+}
\ No newline at end of file
-- 
GitLab