From b0e2450a0e35d5640766305b1602bee653be9aed Mon Sep 17 00:00:00 2001 From: "Jamin W. Collins" Date: Thu, 30 Aug 2018 18:02:50 -0600 Subject: [PATCH] initial Sentry integration Signed-off-by: Jamin W. Collins --- forge-gui-desktop/pom.xml | 4 + forge-gui-desktop/sentry.properties | 14 +++ .../java/forge/error/BugReportDialog.java | 40 +++++---- .../home/settings/CSubmenuPreferences.java | 1 + .../home/settings/VSubmenuPreferences.java | 9 ++ .../src/main/java/forge/view/Main.java | 5 ++ forge-gui/pom.xml | 10 +++ .../main/java/forge/error/BugReporter.java | 90 +++++++------------ .../forge/properties/ForgePreferences.java | 2 + 9 files changed, 104 insertions(+), 71 deletions(-) create mode 100644 forge-gui-desktop/sentry.properties diff --git a/forge-gui-desktop/pom.xml b/forge-gui-desktop/pom.xml index a99c3f76dee..e60ec6c9091 100644 --- a/forge-gui-desktop/pom.xml +++ b/forge-gui-desktop/pom.xml @@ -319,6 +319,7 @@ + @@ -464,6 +465,7 @@ + @@ -578,6 +580,7 @@ + @@ -688,6 +691,7 @@ + diff --git a/forge-gui-desktop/sentry.properties b/forge-gui-desktop/sentry.properties new file mode 100644 index 00000000000..2d575965e3b --- /dev/null +++ b/forge-gui-desktop/sentry.properties @@ -0,0 +1,14 @@ +# ideally this should be using HTTPS, but this is fine for now +dsn=http://87bc8d329e49441895502737c069067b@sentry.cardforge.org:9000/3 +stacktrace.app.packages=forge + +# where to store events if offline or can't reach the above server +buffer.dir=sentry-events +buffer.size=100 + +# allow ample time for graceful shutdown +buffer.shutdowntimeout=5000 +async.shutdowntimeout=5000 + +# allow longer messages +maxmessagelength=1500 \ No newline at end of file diff --git a/forge-gui-desktop/src/main/java/forge/error/BugReportDialog.java b/forge-gui-desktop/src/main/java/forge/error/BugReportDialog.java index 3127252e22b..3b9eaa6d65d 100644 --- a/forge-gui-desktop/src/main/java/forge/error/BugReportDialog.java +++ b/forge-gui-desktop/src/main/java/forge/error/BugReportDialog.java @@ -28,17 +28,19 @@ import java.util.List; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JButton; +import javax.swing.JCheckBox; import javax.swing.JDialog; +import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.KeyStroke; +import forge.model.FModel; +import forge.properties.ForgePreferences; import net.miginfocom.swing.MigLayout; import forge.gui.WrapLayout; -import forge.toolbox.FHyperlink; -import forge.toolbox.FLabel; /** * The class BugReportDialog. Enables showing and saving error messages that @@ -59,22 +61,19 @@ public class BugReportDialog { area.setWrapStyleWord(true); JPanel helpPanel = new JPanel(new WrapLayout(FlowLayout.LEFT, 4, 2)); - for (String word : BugReporter.HELP_URL_LABEL.split(" ")) { - helpPanel.add(new FLabel.Builder().text("" + word + "").useSkinColors(false).build()); - } - helpPanel.add(new FHyperlink.Builder().url(BugReporter.HELP_URL).text("this post").useSkinColors(false).build()); - JPanel p = new JPanel(new MigLayout("wrap")); - p.add(new FLabel.Builder().text(BugReporter.HELP_TEXT).useSkinColors(false).build(), "gap 5"); p.add(helpPanel, "w 600"); p.add(new JScrollPane(area), "w 100%, h 100%, gaptop 5"); // Button is not modified, String gets the automatic listener to hide // the dialog List options = new ArrayList(); - options.add(new JButton(new _CopyAndGo(area))); + options.add(new JButton(new _Report())); + // option to enable automatic Sentry submission + options.add(new JCheckBox(new _ActivateSentry())); + options.add(new JLabel(BugReporter.SENTRY)); options.add(new JButton(new _SaveAction(area))); - options.add(BugReporter.CONTINUE); + options.add(BugReporter.DISCARD); if (showExitAppBtn) { options.add(new JButton(new _ExitAction())); } @@ -91,18 +90,29 @@ public class BugReportDialog { } @SuppressWarnings("serial") - private static class _CopyAndGo extends AbstractAction { - private final JTextArea text; + private static class _ActivateSentry extends AbstractAction { - public _CopyAndGo(JTextArea text) { + @Override + public void actionPerformed(final ActionEvent actionEvent) { + JCheckBox checkBox = (JCheckBox)actionEvent.getSource(); + // enable Sentry use in the future through preference setting + FModel.getPreferences().setPref(ForgePreferences.FPref.USE_SENTRY, checkBox.isSelected()); + FModel.getPreferences().save(); + } + } + + @SuppressWarnings("serial") + private static class _Report extends AbstractAction { + + public _Report() { super(BugReporter.REPORT); this.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_R, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); - this.text = text; } @Override public void actionPerformed(final ActionEvent e) { - BugReporter.copyAndGoToForums(text.getText()); + BugReporter.sendSentry(); + JOptionPane.getRootFrame().dispose(); } } diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java index d7efbea3719..3e32bdc8303 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java @@ -116,6 +116,7 @@ public enum CSubmenuPreferences implements ICDoc { lstControls.add(Pair.of(view.getCbUiForTouchScreen(), FPref.UI_FOR_TOUCHSCREN)); lstControls.add(Pair.of(view.getCbTimedTargOverlay(), FPref.UI_TIMED_TARGETING_OVERLAY_UPDATES)); lstControls.add(Pair.of(view.getCbCompactMainMenu(), FPref.UI_COMPACT_MAIN_MENU)); + lstControls.add(Pair.of(view.getCbUseSentry(), FPref.USE_SENTRY)); lstControls.add(Pair.of(view.getCbPromptFreeBlocks(), FPref.MATCHPREF_PROMPT_FREE_BLOCKS)); lstControls.add(Pair.of(view.getCbPauseWhileMinimized(), FPref.UI_PAUSE_WHILE_MINIMIZED)); lstControls.add(Pair.of(view.getCbWorkshopSyntax(), FPref.DEV_WORKSHOP_SYNTAX)); diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java index 6486d73aac3..40a96099df4 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java @@ -98,6 +98,7 @@ public enum VSubmenuPreferences implements IVSubmenu { private final JCheckBox cbFilterLandsByColorId = new OptionsCheckBox("Filter Lands by Color in Activated Abilities"); private final JCheckBox cbShowStormCount = new OptionsCheckBox("Show Storm Count in Prompt Pane"); private final JCheckBox cbRemindOnPriority = new OptionsCheckBox("Visually Alert on Receipt of Priority"); + private final JCheckBox cbUseSentry = new OptionsCheckBox("Automatically submit bug reports."); private final Map shortcutFields = new HashMap<>(); @@ -147,6 +148,9 @@ public enum VSubmenuPreferences implements IVSubmenu { pnlPrefs.add(cbCompactMainMenu, titleConstraints); pnlPrefs.add(new NoteLabel("Enable for a space efficient sidebar that displays only one menu group at a time (RESTART REQUIRED)."), descriptionConstraints); + pnlPrefs.add(cbUseSentry, titleConstraints); + pnlPrefs.add(new NoteLabel("When enabled, automatically submits bug reports to developers."), descriptionConstraints); + pnlPrefs.add(btnResetJavaFutureCompatibilityWarnings, "w 300px!, h 30px!, gap 27px 0 0 20px, span 2 1"); // Gameplay Options @@ -503,6 +507,11 @@ public enum VSubmenuPreferences implements IVSubmenu { return cbCompactMainMenu; } + /** @return {@link javax.swing.JCheckBox} */ + public final JCheckBox getCbUseSentry() { + return cbUseSentry; + } + /** @return {@link javax.swing.JCheckBox} */ public final JCheckBox getCbRemoveSmall() { return cbRemoveSmall; diff --git a/forge-gui-desktop/src/main/java/forge/view/Main.java b/forge-gui-desktop/src/main/java/forge/view/Main.java index a51f3fe6461..05f72970c58 100644 --- a/forge-gui-desktop/src/main/java/forge/view/Main.java +++ b/forge-gui-desktop/src/main/java/forge/view/Main.java @@ -22,6 +22,8 @@ import forge.GuiDesktop; import forge.Singletons; import forge.card.CardReaderExperiments; import forge.error.ExceptionHandler; +import forge.util.BuildInfo; +import io.sentry.Sentry; /** * Main class for Forge's swing application view. @@ -31,6 +33,9 @@ public final class Main { * Main entry point for Forge */ public static void main(final String[] args) { + Sentry.init(); + Sentry.getStoredClient().setRelease(BuildInfo.getVersionString()); + // HACK - temporary solution to "Comparison method violates it's general contract!" crash System.setProperty("java.util.Arrays.useLegacyMergeSort", "true"); diff --git a/forge-gui/pom.xml b/forge-gui/pom.xml index e8427390009..eb23abcb821 100644 --- a/forge-gui/pom.xml +++ b/forge-gui/pom.xml @@ -67,5 +67,15 @@ cling-support 2.0.1 + + io.sentry + sentry-log4j + 1.7.5 + + + org.slf4j + slf4j-simple + 1.7.22 + diff --git a/forge-gui/src/main/java/forge/error/BugReporter.java b/forge-gui/src/main/java/forge/error/BugReporter.java index d44510b7343..c58fe050ac8 100644 --- a/forge-gui/src/main/java/forge/error/BugReporter.java +++ b/forge-gui/src/main/java/forge/error/BugReporter.java @@ -26,8 +26,12 @@ import java.io.StringWriter; import forge.FThreads; import forge.GuiBase; -import forge.util.BuildInfo; +import forge.model.FModel; +import forge.properties.ForgePreferences; import forge.util.gui.SOptionPane; +import io.sentry.Sentry; +import io.sentry.event.Breadcrumb; +import io.sentry.event.BreadcrumbBuilder; /** * The class ErrorViewer. Enables showing and saving error messages that @@ -41,27 +45,13 @@ public class BugReporter { public static final String REPORT = "Report"; public static final String SAVE = "Save"; - public static final String CONTINUE = "Continue"; + public static final String DISCARD = "Discard"; public static final String EXIT = "Exit"; + public static final String SENTRY = "Submit bug reports automatically"; - public static final String HELP_TEXT = String.format( - "A template for a post in the bug reports forum topic is shown below. Just select '%s' " - + "and the template will be copied to your system clipboard and the forum page will open in your browser. " - + "Then all you have to do is paste the text into a forum post and edit the description line.", REPORT); - public static final String HELP_URL_LABEL = - "Reporting bugs in Forge is very important. We sincerely thank you for your time." - + " For help writing a solid bug report, please see:"; - public static final String HELP_URL = - "http://www.slightlymagic.net/forum/viewtopic.php?f=26&p=109925#p109925"; - private static final String FORUM_URL; + private static Throwable exception; + private static String message; - static { - if (BuildInfo.isDevelopmentVersion()) { - FORUM_URL = "http://www.slightlymagic.net/forum/viewtopic.php?f=52&t=6333&start=54564487645#bottom"; - } else { - FORUM_URL = "http://www.slightlymagic.net/forum/viewforum.php?f=26"; - } - } /** * Shows exception information in a format ready to post to the forum as a @@ -72,6 +62,7 @@ public class BugReporter { if (ex == null) { return; } + exception = ex; if (message != null) { System.err.printf("%s > %s%n", FThreads.debugGetCurrThreadId(), message); } @@ -79,10 +70,10 @@ public class BugReporter { ex.printStackTrace(); final StringBuilder sb = new StringBuilder(); - sb.append("Description: [describe what you were doing when the crash occurred]\n\n"); - buildSpoilerHeader(sb, ex.getClass().getSimpleName()); - sb.append("\n\n"); if (null != message && !message.isEmpty()) { + Sentry.getContext().recordBreadcrumb( + new BreadcrumbBuilder().setMessage(message).build() + ); sb.append(FThreads.debugGetCurrThreadId()).append(" > ").append(message).append("\n"); } @@ -100,12 +91,16 @@ public class BugReporter { else { sb.append(swStr); } - - buildSpoilerFooter(sb); - - GuiBase.getInterface().showBugReportDialog("Report a crash", sb.toString(), true); + if (isSentryEnabled()) { + sendSentry(); + } else { + GuiBase.getInterface().showBugReportDialog("Report a crash", sb.toString(), true); + } } + private static boolean isSentryEnabled() { + return FModel.getPreferences().getPrefBoolean(ForgePreferences.FPref.USE_SENTRY); + } /** * Alias for reportException(ex, null). */ @@ -125,41 +120,16 @@ public class BugReporter { */ public static void reportBug(final String details) { final StringBuilder sb = new StringBuilder(); - sb.append("Description: [describe the problem]\n\n"); - buildSpoilerHeader(sb, "General bug report"); if (null != details && !details.isEmpty()) { sb.append("\n\n"); sb.append(details); } - buildSpoilerFooter(sb); + message = sb.toString(); - GuiBase.getInterface().showBugReportDialog("Report a bug", sb.toString(), false); - } - - private static StringBuilder buildSpoilerHeader(final StringBuilder sb, final String reportTitle) { - sb.append("[spoiler=").append(reportTitle).append("][code]"); - sb.append("\nForge Version: ").append(GuiBase.getInterface().getCurrentVersion()); - sb.append("\nOperating System: ").append(System.getProperty("os.name")) - .append(" ").append(System.getProperty("os.version")) - .append(" ").append(System.getProperty("os.arch")); - sb.append("\nJava Version: ").append(System.getProperty("java.version")) - .append(" ").append(System.getProperty("java.vendor")); - return sb; - } - - private static StringBuilder buildSpoilerFooter(final StringBuilder sb) { - sb.append("[/code][/spoiler]"); - return sb; - } - - public static void copyAndGoToForums(final String text) { - try { - // copy text to clipboard - GuiBase.getInterface().copyToClipboard(text); - GuiBase.getInterface().browseToUrl(FORUM_URL); - } catch (final Exception ex) { - SOptionPane.showMessageDialog("Sorry, a problem occurred while opening the forum in your default browser.", - "A problem occurred", SOptionPane.ERROR_ICON); + if (isSentryEnabled()) { + sendSentry(); + } else { + GuiBase.getInterface().showBugReportDialog("Report a bug", message, false); } } @@ -187,6 +157,14 @@ public class BugReporter { } } + public static void sendSentry() { + if (exception != null) { + Sentry.capture(exception); + } else if (message !=null) { + Sentry.capture(message); + } + } + /** * Private constructor to prevent instantiation. */ diff --git a/forge-gui/src/main/java/forge/properties/ForgePreferences.java b/forge-gui/src/main/java/forge/properties/ForgePreferences.java index 7e2c7096384..22d13d973d7 100644 --- a/forge-gui/src/main/java/forge/properties/ForgePreferences.java +++ b/forge-gui/src/main/java/forge/properties/ForgePreferences.java @@ -136,6 +136,8 @@ public class ForgePreferences extends PreferencesStore { //TODO This should be removed after the update that requires Java 8. DISABLE_DISPLAY_JAVA_8_UPDATE_WARNING("false"), + USE_SENTRY("false"), // this controls whether automated bug reporting is done or not + MATCH_HOT_SEAT_MODE("false"), //this only applies to mobile game MATCHPREF_PROMPT_FREE_BLOCKS("false"),