Skip to content

Commit

Permalink
Merge pull request #57 from ibi-group/bugsnag
Browse files Browse the repository at this point in the history
Add ability to report errors to Bugsnag
  • Loading branch information
evansiroky authored Jul 10, 2021
2 parents 3b44958 + 2e19ad4 commit 0ba999c
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 13 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -765,5 +765,11 @@
<artifactId>bsf</artifactId>
<version>2.4.0</version>
</dependency>
<!-- Error reporting -->
<dependency>
<groupId>com.bugsnag</groupId>
<version>3.6.2</version>
<artifactId>bugsnag</artifactId>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
package org.opentripplanner.api.common;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.opentripplanner.standalone.ErrorUtils;

import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Provider
public class OTPExceptionMapper implements ExceptionMapper<Exception> {
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);
ErrorUtils.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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@

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.ErrorUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/** 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<Class<? extends Exception>, Message> messages;
static {
messages = new HashMap<Class<? extends Exception>, Message> ();
Expand All @@ -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;
ErrorUtils.reportErrorToBugsnag("Unhandled exception while planning trip", req, e);
}
this.setMsg(message);
if (e instanceof VertexNotFoundException)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 Bugsnag release stage to use when reporting errors."
)
public String bugsnagReleaseStage;

@Parameter(names = {"--bugsnagAppType"},
description = "A Bugsnag 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);
Expand Down Expand Up @@ -269,4 +283,3 @@ public void validate(String name, String value) throws ParameterException {
}
}
}

77 changes: 77 additions & 0 deletions src/main/java/org/opentripplanner/standalone/ErrorUtils.java
Original file line number Diff line number Diff line change
@@ -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;

/**
* 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 ErrorUtils {
private static Bugsnag bugsnag;
private static final Logger LOG = LoggerFactory.getLogger(ErrorUtils.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 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,
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);
}
}
2 changes: 2 additions & 0 deletions src/main/java/org/opentripplanner/standalone/OTPMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ public static void main(String[] args) {
System.exit(-1);
}

ErrorUtils.initializeBugsnagErrorReporting(params);

OTPMain main = new OTPMain(params);
if (!main.run()) {
System.exit(-1);
Expand Down

0 comments on commit 0ba999c

Please sign in to comment.