From 2d0b948bf3c5291c3d3e5d7e6b7aa4508c31a51f Mon Sep 17 00:00:00 2001 From: Evan Siroky Date: Wed, 23 Jun 2021 15:14:49 -0700 Subject: [PATCH 1/5] Add ability to report errors to Bugsnag --- pom.xml | 6 ++ .../api/common/OTPExceptionMapper.java | 7 +- .../api/model/error/PlannerError.java | 10 +-- .../api/resource/PlannerResource.java | 2 +- .../standalone/BugsnagReporter.java | 77 +++++++++++++++++++ .../standalone/CommandLineParameters.java | 14 ++++ .../opentripplanner/standalone/OTPMain.java | 2 + 7 files changed, 107 insertions(+), 11 deletions(-) create mode 100644 src/main/java/org/opentripplanner/standalone/BugsnagReporter.java diff --git a/pom.xml b/pom.xml index 6d064584143..a7fb8891f69 100644 --- a/pom.xml +++ b/pom.xml @@ -765,5 +765,11 @@ bsf 2.4.0 + + + com.bugsnag + 3.6.2 + bugsnag + diff --git a/src/main/java/org/opentripplanner/api/common/OTPExceptionMapper.java b/src/main/java/org/opentripplanner/api/common/OTPExceptionMapper.java index 117db541336..f4240924286 100644 --- a/src/main/java/org/opentripplanner/api/common/OTPExceptionMapper.java +++ b/src/main/java/org/opentripplanner/api/common/OTPExceptionMapper.java @@ -1,7 +1,6 @@ package org.opentripplanner.api.common; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.opentripplanner.standalone.BugsnagReporter; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; @@ -9,11 +8,11 @@ @Provider public class OTPExceptionMapper implements ExceptionMapper { - private static final Logger LOG = LoggerFactory.getLogger(OTPExceptionMapper.class); public Response toResponse(Exception ex) { // Show the exception in the server log - LOG.error("Unhandled exception", ex); + BugsnagReporter.reportErrorToBugsnag("Unhandled server exception", ex); + // Return the short form message to the client return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity(ex.toString() + " " + ex.getMessage()) diff --git a/src/main/java/org/opentripplanner/api/model/error/PlannerError.java b/src/main/java/org/opentripplanner/api/model/error/PlannerError.java index c7f353be192..74ae8384169 100644 --- a/src/main/java/org/opentripplanner/api/model/error/PlannerError.java +++ b/src/main/java/org/opentripplanner/api/model/error/PlannerError.java @@ -2,9 +2,9 @@ import org.opentripplanner.api.common.LocationNotAccessible; import org.opentripplanner.api.common.Message; +import org.opentripplanner.routing.core.RoutingRequest; import org.opentripplanner.routing.error.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.opentripplanner.standalone.BugsnagReporter; import java.util.HashMap; import java.util.List; @@ -12,8 +12,6 @@ /** This API response element represents an error in trip planning. */ public class PlannerError { - - private static final Logger LOG = LoggerFactory.getLogger(PlannerError.class); private static Map, Message> messages; static { messages = new HashMap, Message> (); @@ -38,12 +36,12 @@ public PlannerError() { noPath = true; } - public PlannerError(Exception e) { + public PlannerError(RoutingRequest req, Exception e) { this(); message = messages.get(e.getClass()); if (message == null) { - LOG.error("exception planning trip: ", e); message = Message.SYSTEM_ERROR; + BugsnagReporter.reportErrorToBugsnag("Unhandled exception while planning trip", req, e); } this.setMsg(message); if (e instanceof VertexNotFoundException) diff --git a/src/main/java/org/opentripplanner/api/resource/PlannerResource.java b/src/main/java/org/opentripplanner/api/resource/PlannerResource.java index bbfcf2a8c26..c306e79be9b 100644 --- a/src/main/java/org/opentripplanner/api/resource/PlannerResource.java +++ b/src/main/java/org/opentripplanner/api/resource/PlannerResource.java @@ -82,7 +82,7 @@ public Response plan(@Context UriInfo uriInfo, @Context Request grizzlyRequest) else response.setPlan(plan); } catch (Exception e) { - PlannerError error = new PlannerError(e); + PlannerError error = new PlannerError(request, e); if(!PlannerError.isPlanningError(e.getClass())) LOG.warn("Error while planning path: ", e); response.setError(error); diff --git a/src/main/java/org/opentripplanner/standalone/BugsnagReporter.java b/src/main/java/org/opentripplanner/standalone/BugsnagReporter.java new file mode 100644 index 00000000000..7cd41d3699c --- /dev/null +++ b/src/main/java/org/opentripplanner/standalone/BugsnagReporter.java @@ -0,0 +1,77 @@ +package org.opentripplanner.standalone; + +import com.bugsnag.Bugsnag; +import com.bugsnag.Report; +import com.bugsnag.Severity; +import org.opentripplanner.common.MavenVersion; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Bugsnag util for reporting errors to the project defined by the Bugsnag project notifier API key. + * + * A Bugsnag project identifier key is unique to a Bugsnag project and allows errors to be saved against it. This key + * can be obtained by logging into Bugsnag (https://app.bugsnag.com), clicking on Projects (left side menu) and + * selecting the required project. Once selected, the notifier API key is presented. + */ +public class BugsnagReporter { + private static Bugsnag bugsnag; + private static final Logger LOG = LoggerFactory.getLogger(BugsnagReporter.class); + + /** + * Initialize Bugsnag using the project notifier API key when the application is first loaded. + * @param params The parsed OTP command line parameters + */ + public static void initializeBugsnagErrorReporting(CommandLineParameters params) { + if (params.bugsnagKey != null) { + bugsnag = new Bugsnag(params.bugsnagKey); + bugsnag.setAppVersion(MavenVersion.VERSION.getShortVersionString()); + if (params.bugsnagReleaseStage != null) { + bugsnag.setReleaseStage(params.bugsnagReleaseStage); + } + if (params.bugsnagAppType != null) { + bugsnag.setAppType(params.bugsnagAppType); + } + LOG.info("Bugsnag reporting enabled."); + } else { + LOG.warn("Bugsnag project notifier API key not available. Bugsnag error reporting disabled."); + } + } + + /** + * If Bugsnag has been configured, report error based on provided information. + */ + public static boolean reportErrorToBugsnag(String message, Throwable throwable) { + return reportErrorToBugsnag(message, null, throwable); + } + + /** + * If Bugsnag has been configured, report error based on provided information. Note: throwable must be non-null. + */ + public static boolean reportErrorToBugsnag(String message, Object badEntity, Throwable throwable) { + // Log error to log output. + LOG.error(message, throwable); + + // If bugsnag is disabled, make sure to report full error to otp-middleware logs. + if (bugsnag == null) { + LOG.warn("Bugsnag error reporting is disabled. Unable to report to Bugsnag this message: {} for this bad entity: {}", + message, + badEntity, + throwable); + return false; + } + + // If no throwable provided, create a new UnknownError exception so that Bugsnag will accept the error report. + if (throwable == null) { + LOG.warn("No exception provided for this error report (message: {}). New UnknownError used instead.", message); + throwable = new UnknownError("Exception type is unknown! Please add exception where report method is called."); + } + + // Finally, construct report and send to bugsnag. + Report report = bugsnag.buildReport(throwable); + report.setContext(message); + report.addToTab("debugging", "bad entity", badEntity != null ? badEntity.toString() : "N/A"); + report.setSeverity(Severity.ERROR); + return bugsnag.notify(report); + } +} diff --git a/src/main/java/org/opentripplanner/standalone/CommandLineParameters.java b/src/main/java/org/opentripplanner/standalone/CommandLineParameters.java index 1cfe9b7ac00..d98ac40c2c0 100644 --- a/src/main/java/org/opentripplanner/standalone/CommandLineParameters.java +++ b/src/main/java/org/opentripplanner/standalone/CommandLineParameters.java @@ -138,6 +138,20 @@ public class CommandLineParameters implements Cloneable { @Parameter(names = { "--enableScriptingWebService" }, description = "enable scripting through a web-service (Warning! Very unsafe for public facing servers)") boolean enableScriptingWebService = false; + @Parameter(names = {"--bugsnagKey"}, + description = "A Bugsnag project notifier key that will be used to report unhandled exceptions and trip planning errors.") + public String bugsnagKey = null; + + @Parameter(names = {"--bugsnagReleaseStage"}, + description = "A Bugsang release stage to use when reporting errors." + ) + public String bugsnagReleaseStage; + + @Parameter(names = {"--bugsnagAppType"}, + description = "A Bugsang app type to use when reporting errors." + ) + public String bugsnagAppType; + /** Set some convenience parameters based on other parameters' values. */ public void infer() { server |= (inMemory || preFlight || port != null); diff --git a/src/main/java/org/opentripplanner/standalone/OTPMain.java b/src/main/java/org/opentripplanner/standalone/OTPMain.java index 50d768c3ac9..cbddafd495a 100644 --- a/src/main/java/org/opentripplanner/standalone/OTPMain.java +++ b/src/main/java/org/opentripplanner/standalone/OTPMain.java @@ -68,6 +68,8 @@ public static void main(String[] args) { System.exit(-1); } + BugsnagReporter.initializeBugsnagErrorReporting(params); + OTPMain main = new OTPMain(params); if (!main.run()) { System.exit(-1); From 2e31ad4536f07b4b2e25b879c02b04f930688482 Mon Sep 17 00:00:00 2001 From: Evan Siroky Date: Wed, 23 Jun 2021 15:22:24 -0700 Subject: [PATCH 2/5] Remove unneeded comment --- .../java/org/opentripplanner/api/common/OTPExceptionMapper.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/api/common/OTPExceptionMapper.java b/src/main/java/org/opentripplanner/api/common/OTPExceptionMapper.java index f4240924286..75120d096ec 100644 --- a/src/main/java/org/opentripplanner/api/common/OTPExceptionMapper.java +++ b/src/main/java/org/opentripplanner/api/common/OTPExceptionMapper.java @@ -10,7 +10,6 @@ public class OTPExceptionMapper implements ExceptionMapper { public Response toResponse(Exception ex) { - // Show the exception in the server log BugsnagReporter.reportErrorToBugsnag("Unhandled server exception", ex); // Return the short form message to the client From a7b83eefa7ad1ea5e8430aa2f38b3a7a688d18a2 Mon Sep 17 00:00:00 2001 From: Evan Siroky Date: Fri, 9 Jul 2021 16:49:00 -0700 Subject: [PATCH 3/5] Update src/main/java/org/opentripplanner/standalone/CommandLineParameters.java Co-authored-by: Landon Reed --- .../org/opentripplanner/standalone/CommandLineParameters.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/opentripplanner/standalone/CommandLineParameters.java b/src/main/java/org/opentripplanner/standalone/CommandLineParameters.java index d98ac40c2c0..623c94caaaf 100644 --- a/src/main/java/org/opentripplanner/standalone/CommandLineParameters.java +++ b/src/main/java/org/opentripplanner/standalone/CommandLineParameters.java @@ -143,7 +143,7 @@ public class CommandLineParameters implements Cloneable { public String bugsnagKey = null; @Parameter(names = {"--bugsnagReleaseStage"}, - description = "A Bugsang release stage to use when reporting errors." + description = "A Bugsnag release stage to use when reporting errors." ) public String bugsnagReleaseStage; @@ -283,4 +283,3 @@ public void validate(String name, String value) throws ParameterException { } } } - From 1cc86aee19d9029aad261c0eed9e9000c209bcae Mon Sep 17 00:00:00 2001 From: Evan Siroky Date: Fri, 9 Jul 2021 16:49:09 -0700 Subject: [PATCH 4/5] Update src/main/java/org/opentripplanner/standalone/CommandLineParameters.java Co-authored-by: Landon Reed --- .../org/opentripplanner/standalone/CommandLineParameters.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/opentripplanner/standalone/CommandLineParameters.java b/src/main/java/org/opentripplanner/standalone/CommandLineParameters.java index 623c94caaaf..c733ac38475 100644 --- a/src/main/java/org/opentripplanner/standalone/CommandLineParameters.java +++ b/src/main/java/org/opentripplanner/standalone/CommandLineParameters.java @@ -148,7 +148,7 @@ public class CommandLineParameters implements Cloneable { public String bugsnagReleaseStage; @Parameter(names = {"--bugsnagAppType"}, - description = "A Bugsang app type to use when reporting errors." + description = "A Bugsnag app type to use when reporting errors." ) public String bugsnagAppType; From 2e19ad4ff455fd4a2475d8f235d77b9b0ac3acd5 Mon Sep 17 00:00:00 2001 From: Evan Siroky Date: Fri, 9 Jul 2021 16:56:48 -0700 Subject: [PATCH 5/5] Rename BugsnagReporter class to ErrorUtils and update some comments --- .../opentripplanner/api/common/OTPExceptionMapper.java | 4 ++-- .../org/opentripplanner/api/model/error/PlannerError.java | 4 ++-- .../standalone/{BugsnagReporter.java => ErrorUtils.java} | 8 ++++---- src/main/java/org/opentripplanner/standalone/OTPMain.java | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) rename src/main/java/org/opentripplanner/standalone/{BugsnagReporter.java => ErrorUtils.java} (92%) diff --git a/src/main/java/org/opentripplanner/api/common/OTPExceptionMapper.java b/src/main/java/org/opentripplanner/api/common/OTPExceptionMapper.java index 75120d096ec..533e0ac299d 100644 --- a/src/main/java/org/opentripplanner/api/common/OTPExceptionMapper.java +++ b/src/main/java/org/opentripplanner/api/common/OTPExceptionMapper.java @@ -1,6 +1,6 @@ package org.opentripplanner.api.common; -import org.opentripplanner.standalone.BugsnagReporter; +import org.opentripplanner.standalone.ErrorUtils; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; @@ -10,7 +10,7 @@ public class OTPExceptionMapper implements ExceptionMapper { public Response toResponse(Exception ex) { - BugsnagReporter.reportErrorToBugsnag("Unhandled server exception", ex); + ErrorUtils.reportErrorToBugsnag("Unhandled server exception", ex); // Return the short form message to the client return Response.status(Response.Status.INTERNAL_SERVER_ERROR) diff --git a/src/main/java/org/opentripplanner/api/model/error/PlannerError.java b/src/main/java/org/opentripplanner/api/model/error/PlannerError.java index 74ae8384169..0ae77406d6a 100644 --- a/src/main/java/org/opentripplanner/api/model/error/PlannerError.java +++ b/src/main/java/org/opentripplanner/api/model/error/PlannerError.java @@ -4,7 +4,7 @@ import org.opentripplanner.api.common.Message; import org.opentripplanner.routing.core.RoutingRequest; import org.opentripplanner.routing.error.*; -import org.opentripplanner.standalone.BugsnagReporter; +import org.opentripplanner.standalone.ErrorUtils; import java.util.HashMap; import java.util.List; @@ -41,7 +41,7 @@ public PlannerError(RoutingRequest req, Exception e) { message = messages.get(e.getClass()); if (message == null) { message = Message.SYSTEM_ERROR; - BugsnagReporter.reportErrorToBugsnag("Unhandled exception while planning trip", req, e); + ErrorUtils.reportErrorToBugsnag("Unhandled exception while planning trip", req, e); } this.setMsg(message); if (e instanceof VertexNotFoundException) diff --git a/src/main/java/org/opentripplanner/standalone/BugsnagReporter.java b/src/main/java/org/opentripplanner/standalone/ErrorUtils.java similarity index 92% rename from src/main/java/org/opentripplanner/standalone/BugsnagReporter.java rename to src/main/java/org/opentripplanner/standalone/ErrorUtils.java index 7cd41d3699c..71b93d3078e 100644 --- a/src/main/java/org/opentripplanner/standalone/BugsnagReporter.java +++ b/src/main/java/org/opentripplanner/standalone/ErrorUtils.java @@ -8,15 +8,15 @@ import org.slf4j.LoggerFactory; /** - * Bugsnag util for reporting errors to the project defined by the Bugsnag project notifier API key. + * A util class for reporting errors to the project defined by the Bugsnag project notifier API key. * * A Bugsnag project identifier key is unique to a Bugsnag project and allows errors to be saved against it. This key * can be obtained by logging into Bugsnag (https://app.bugsnag.com), clicking on Projects (left side menu) and * selecting the required project. Once selected, the notifier API key is presented. */ -public class BugsnagReporter { +public class ErrorUtils { private static Bugsnag bugsnag; - private static final Logger LOG = LoggerFactory.getLogger(BugsnagReporter.class); + private static final Logger LOG = LoggerFactory.getLogger(ErrorUtils.class); /** * Initialize Bugsnag using the project notifier API key when the application is first loaded. @@ -52,7 +52,7 @@ public static boolean reportErrorToBugsnag(String message, Object badEntity, Thr // Log error to log output. LOG.error(message, throwable); - // If bugsnag is disabled, make sure to report full error to otp-middleware logs. + // If bugsnag is disabled, make sure to report log full error. if (bugsnag == null) { LOG.warn("Bugsnag error reporting is disabled. Unable to report to Bugsnag this message: {} for this bad entity: {}", message, diff --git a/src/main/java/org/opentripplanner/standalone/OTPMain.java b/src/main/java/org/opentripplanner/standalone/OTPMain.java index cbddafd495a..751d4c11266 100644 --- a/src/main/java/org/opentripplanner/standalone/OTPMain.java +++ b/src/main/java/org/opentripplanner/standalone/OTPMain.java @@ -68,7 +68,7 @@ public static void main(String[] args) { System.exit(-1); } - BugsnagReporter.initializeBugsnagErrorReporting(params); + ErrorUtils.initializeBugsnagErrorReporting(params); OTPMain main = new OTPMain(params); if (!main.run()) {