diff --git a/README.md b/README.md index 5e7ce9a..5aba319 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,9 @@ Also, check [our FAQ entry](docs/faq.md#what-kind-of-setup-is-mxisd-really-desig See the [dedicated document](docs/getting-started.md) # Support +## Troubleshooting +A basic troubleshooting guide is available [here](docs/troubleshooting.md). + ## Community Over Matrix: [#mxisd:kamax.io](https://matrix.to/#/#mxisd:kamax.io) ([Preview](https://view.matrix.org/room/!NPRUEisLjcaMtHIzDr:kamax.io/)) diff --git a/docs/getting-started.md b/docs/getting-started.md index b951ccb..89809b4 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -144,7 +144,8 @@ by the relevant hostname which you configured in your reverse proxy. **NOTE:** You might not see a suggestion for the e-mail address, which is normal. Still proceed with the invite. If it worked, it means you are up and running and can enjoy mxisd in its basic mode! Congratulations! -If it did not work, [get in touch](../README.md#support) and we'll do our best to get you started. +If it did not work, read the basic [troubleshooting guide](troubleshooting.md), [get in touch](../README.md#support) and +we'll do our best to get you started. ## Next steps Once your mxisd server is up and running, there are several ways you can enhance and integrate further with your diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..abbb323 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,53 @@ +# Troubleshooting +- [Purpose](#purpose) +- [Logs](#logs) + - [Locations](#locations) + - [Reading Them](#reading-them) + - [Common issues](#common-issues) +- [Submit an issue](#submit-an-issue) + +## Purpose +This document describes basic troubleshooting steps for mxisd. + +## Logs +### Locations +mxisd logs to `STDOUT` (Standard Output) and `STDERR` (Standard Error) only, which gets redirected +to log file(s) depending on your system. + +If you use the [Debian package](install/debian.md), this goes to `syslog`. +If you use the [Docker image](install/docker.md), this goes to the container logs. + +For any other platform, please refer to your package maintainer. + +### Reading them +Before reporting an issue, it is important to produce clean and complete logs so they can be understood. + +It is usually useless to try to troubleshoot an issue based on a single log line. Any action or API request +in mxisd would trigger more than one log lines, and those would be considered necessary context to +understand what happened. + +You may also find things called *stacktraces*. Those are important to pin-point bugs and the likes and should +always be included in any report. They also tend to be very specific about the issue at hand. + +Example of a stacktrace: +``` +Exception in thread "main" java.lang.NullPointerException + at com.example.myproject.Book.getTitle(Book.java:16) + at com.example.myproject.Author.getBookTitles(Author.java:25) + at com.example.myproject.Bootstrap.main(Bootstrap.java:14) +``` + +### Common issues +#### Internal Server Error +`Contact your administrator with reference Transaction #123456789` + +This is a generic message produced in case of an unknown error. The transaction reference allows to easily find +the location in the logs to look for an error. + +**IMPORTANT:** That line alone does not tell you anything about the error. You'll need the log lines before and after, +usually including a stacktrace, to know what happened. Please take the time to read the surround output to get +context about the issue at hand. + +## Submit an issue +In case the logs do not allow you to understand the issue at hand, please submit clean and complete logs +as explained [here](#reading-them) in a new issue on the repository, or [get in touch](../README.md#contact). diff --git a/src/main/java/io/kamax/mxisd/http/undertow/handler/BasicHttpHandler.java b/src/main/java/io/kamax/mxisd/http/undertow/handler/BasicHttpHandler.java index 072dcdb..0b21a19 100644 --- a/src/main/java/io/kamax/mxisd/http/undertow/handler/BasicHttpHandler.java +++ b/src/main/java/io/kamax/mxisd/http/undertow/handler/BasicHttpHandler.java @@ -101,6 +101,10 @@ public abstract class BasicHttpHandler implements HttpHandler { return GsonUtil.parseObj(getBodyUtf8(exchange)); } + protected void putHeader(HttpServerExchange ex, String name, String value) { + ex.getResponseHeaders().put(HttpString.tryFromString(name), value); + } + protected void respond(HttpServerExchange ex, int statusCode, JsonElement bodyJson) { respondJson(ex, statusCode, GsonUtil.get().toJson(bodyJson)); } diff --git a/src/main/java/io/kamax/mxisd/http/undertow/handler/SaneHandler.java b/src/main/java/io/kamax/mxisd/http/undertow/handler/SaneHandler.java index e4cc8a1..5854ce2 100644 --- a/src/main/java/io/kamax/mxisd/http/undertow/handler/SaneHandler.java +++ b/src/main/java/io/kamax/mxisd/http/undertow/handler/SaneHandler.java @@ -27,8 +27,7 @@ import io.kamax.matrix.json.InvalidJsonException; import io.kamax.mxisd.exception.*; import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; -import io.undertow.util.HttpString; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,15 +36,22 @@ import java.time.Instant; public class SaneHandler extends BasicHttpHandler { + private static final Logger log = LoggerFactory.getLogger(SaneHandler.class); + + private static final String CorsOriginName = "Access-Control-Allow-Origin"; + private static final String CorsOriginValue = "*"; + private static final String CorsMethodsName = "Access-Control-Allow-Methods"; + private static final String CorsMethodsValue = "GET, POST, PUT, DELETE, OPTIONS"; + private static final String CorsHeadersName = "Access-Control-Allow-Headers"; + private static final String CorsHeadersValue = "Origin, X-Requested-With, Content-Type, Accept, Authorization"; + public static SaneHandler around(HttpHandler h) { return new SaneHandler(h); } - private transient final Logger log = LoggerFactory.getLogger(SaneHandler.class); + private final HttpHandler child; - private HttpHandler child; - - public SaneHandler(HttpHandler child) { + private SaneHandler(HttpHandler child) { this.child = child; } @@ -58,9 +64,9 @@ public class SaneHandler extends BasicHttpHandler { } else { try { // CORS headers as per spec - exchange.getResponseHeaders().put(HttpString.tryFromString("Access-Control-Allow-Origin"), "*"); - exchange.getResponseHeaders().put(HttpString.tryFromString("Access-Control-Allow-Methods"), "GET, POST, PUT, DELETE, OPTIONS"); - exchange.getResponseHeaders().put(HttpString.tryFromString("Access-Control-Allow-Headers"), "Origin, X-Requested-With, Content-Type, Accept, Authorization"); + putHeader(exchange, CorsOriginName, CorsOriginValue); + putHeader(exchange, CorsMethodsName, CorsMethodsValue); + putHeader(exchange, CorsHeadersName, CorsHeadersValue); child.handleRequest(exchange); } catch (IllegalArgumentException e) { @@ -89,9 +95,9 @@ public class SaneHandler extends BasicHttpHandler { handleException(exchange, e); } catch (InternalServerError e) { if (StringUtils.isNotBlank(e.getInternalReason())) { - log.error("Reference #{} - {}", e.getReference(), e.getInternalReason()); + log.error("Transaction #{} - {}", e.getReference(), e.getInternalReason()); } else { - log.error("Reference #{}", e); + log.error("Transaction #{}", e); } handleException(exchange, e); @@ -105,14 +111,11 @@ public class SaneHandler extends BasicHttpHandler { respond(exchange, e.getStatus(), buildErrorBody(exchange, e.getErrorCode(), e.getError())); } catch (RuntimeException e) { log.error("Unknown error when handling {}", exchange.getRequestURL(), e); - respond(exchange, HttpStatus.SC_INTERNAL_SERVER_ERROR, buildErrorBody(exchange, - "M_UNKNOWN", - StringUtils.defaultIfBlank( - e.getMessage(), - "An internal server error occurred. If this error persists, please contact support with reference #" + - Instant.now().toEpochMilli() - ) - )); + String message = e.getMessage(); + if (StringUtils.isBlank(message)) { + message = "An internal server error occurred. Contact your administrator with reference Transaction #" + Instant.now().toEpochMilli(); + } + respond(exchange, HttpStatus.SC_INTERNAL_SERVER_ERROR, buildErrorBody(exchange, "M_UNKNOWN", message)); } finally { exchange.endExchange(); }