From c41c15e61a80c8e45e8336f16f002ff2a18a97a5 Mon Sep 17 00:00:00 2001
From: Daniel Gerhardt <code@dgerhardt.net>
Date: Wed, 26 Apr 2017 13:53:44 +0200
Subject: [PATCH] Log unhandled exceptions

---
 .../AbstractControllerExceptionHandler.java   | 30 +++++++++++++++-
 .../ControllerExceptionHandler.java           | 35 ++++++++++---------
 .../DefaultControllerExceptionHandler.java    |  3 +-
 src/main/resources/log4j-dev.properties       |  4 +--
 src/main/resources/log4j.properties           |  4 +--
 5 files changed, 53 insertions(+), 23 deletions(-)

diff --git a/src/main/java/de/thm/arsnova/controller/AbstractControllerExceptionHandler.java b/src/main/java/de/thm/arsnova/controller/AbstractControllerExceptionHandler.java
index a393dc248..58e3f907e 100644
--- a/src/main/java/de/thm/arsnova/controller/AbstractControllerExceptionHandler.java
+++ b/src/main/java/de/thm/arsnova/controller/AbstractControllerExceptionHandler.java
@@ -1,15 +1,23 @@
 package de.thm.arsnova.controller;
 
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.event.Level;
 import org.springframework.beans.factory.annotation.Value;
 
 import java.util.HashMap;
 import java.util.Map;
 
 public class AbstractControllerExceptionHandler {
+	private static final Logger logger = LoggerFactory.getLogger(AbstractControllerExceptionHandler.class);
+
 	/* Since exception messages might contain sensitive data, they are not exposed by default. */
 	@Value("${api.expose-error-messages:false}") private boolean exposeMessages;
 
-	protected Map<String, Object> handleException(Throwable e) {
+	protected Map<String, Object> handleException(@NonNull Throwable e, @NonNull Level level) {
+		final String message = e.getMessage() != null ? e.getMessage() : "";
+		log(level, message, e);
 		final Map<String, Object> result = new HashMap<>();
 		result.put("errorType", e.getClass().getSimpleName());
 		if (exposeMessages) {
@@ -18,4 +26,24 @@ public class AbstractControllerExceptionHandler {
 
 		return result;
 	}
+
+	private void log(Level level, String message, Throwable e) {
+		switch (level) {
+			case ERROR:
+				logger.error(message, e);
+				break;
+			case WARN:
+				logger.warn(message, e);
+				break;
+			case INFO:
+				logger.info(message, e);
+				break;
+			case DEBUG:
+				logger.debug(message, e);
+				break;
+			case TRACE:
+				logger.trace(message, e);
+				break;
+		}
+	}
 }
diff --git a/src/main/java/de/thm/arsnova/controller/ControllerExceptionHandler.java b/src/main/java/de/thm/arsnova/controller/ControllerExceptionHandler.java
index 433f259aa..9e91b10a1 100644
--- a/src/main/java/de/thm/arsnova/controller/ControllerExceptionHandler.java
+++ b/src/main/java/de/thm/arsnova/controller/ControllerExceptionHandler.java
@@ -25,6 +25,7 @@ import de.thm.arsnova.exceptions.NotImplementedException;
 import de.thm.arsnova.exceptions.PayloadTooLargeException;
 import de.thm.arsnova.exceptions.PreconditionFailedException;
 import de.thm.arsnova.exceptions.UnauthorizedException;
+import org.slf4j.event.Level;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.converter.HttpMessageNotReadableException;
 import org.springframework.security.access.AccessDeniedException;
@@ -46,25 +47,32 @@ import java.util.Map;
  */
 @ControllerAdvice
 public class ControllerExceptionHandler extends AbstractControllerExceptionHandler {
+	@ExceptionHandler(NoContentException.class)
+	@ResponseBody
+	@ResponseStatus(HttpStatus.NO_CONTENT)
+	public Map<String, Object> handleNoContentException(final Exception e, final HttpServletRequest request) {
+		return handleException(e, Level.TRACE);
+	}
+
 	@ExceptionHandler(NotFoundException.class)
 	@ResponseBody
 	@ResponseStatus(HttpStatus.NOT_FOUND)
 	public Map<String, Object> handleNotFoundException(final Exception e, final HttpServletRequest request) {
-		return handleException(e);
+		return handleException(e, Level.TRACE);
 	}
 
 	@ExceptionHandler(UnauthorizedException.class)
 	@ResponseBody
 	@ResponseStatus(HttpStatus.UNAUTHORIZED)
 	public Map<String, Object> handleUnauthorizedException(final Exception e, final HttpServletRequest request) {
-		return handleException(e);
+		return handleException(e, Level.TRACE);
 	}
 
 	@ExceptionHandler(AuthenticationCredentialsNotFoundException.class)
 	@ResponseStatus(HttpStatus.UNAUTHORIZED)
 	@ResponseBody
 	public Map<String, Object> handleAuthenticationCredentialsNotFoundException(final Exception e, final HttpServletRequest request) {
-		return handleException(e);
+		return handleException(e, Level.DEBUG);
 	}
 
 	@ExceptionHandler(AccessDeniedException.class)
@@ -83,55 +91,48 @@ public class ControllerExceptionHandler extends AbstractControllerExceptionHandl
 			response.setStatus(HttpStatus.FORBIDDEN.value());
 		}
 
-		return handleException(e);
+		return handleException(e, Level.DEBUG);
 	}
 
 	@ExceptionHandler(ForbiddenException.class)
 	@ResponseBody
 	@ResponseStatus(HttpStatus.FORBIDDEN)
 	public Map<String, Object> handleForbiddenException(final Exception e, final HttpServletRequest request) {
-		return handleException(e);
-	}
-
-	@ExceptionHandler(NoContentException.class)
-	@ResponseBody
-	@ResponseStatus(HttpStatus.NO_CONTENT)
-	public Map<String, Object> handleNoContentException(final Exception e, final HttpServletRequest request) {
-		return handleException(e);
+		return handleException(e, Level.DEBUG);
 	}
 
 	@ExceptionHandler(BadRequestException.class)
 	@ResponseBody
 	@ResponseStatus(HttpStatus.BAD_REQUEST)
 	public Map<String, Object> handleBadRequestException(final Exception e, final HttpServletRequest request) {
-		return handleException(e);
+		return handleException(e, Level.DEBUG);
 	}
 
 	@ExceptionHandler(PreconditionFailedException.class)
 	@ResponseBody
 	@ResponseStatus(HttpStatus.PRECONDITION_FAILED)
 	public Map<String, Object> handlePreconditionFailedException(final Exception e, final HttpServletRequest request) {
-		return handleException(e);
+		return handleException(e, Level.DEBUG);
 	}
 
 	@ExceptionHandler(NotImplementedException.class)
 	@ResponseBody
 	@ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
 	public Map<String, Object> handleNotImplementedException(final Exception e, final HttpServletRequest request) {
-		return handleException(e);
+		return handleException(e, Level.DEBUG);
 	}
 
 	@ExceptionHandler(PayloadTooLargeException.class)
 	@ResponseBody
 	@ResponseStatus(HttpStatus.PAYLOAD_TOO_LARGE)
 	public Map<String, Object> handlePayloadTooLargeException(final Exception e, final HttpServletRequest request) {
-		return handleException(e);
+		return handleException(e, Level.DEBUG);
 	}
 
 	@ExceptionHandler(HttpMessageNotReadableException.class)
 	@ResponseBody
 	@ResponseStatus(HttpStatus.BAD_REQUEST)
 	public Map<String, Object> handleHttpMessageNotReadableException(final Exception e, final HttpServletRequest request) {
-		return handleException(e);
+		return handleException(e, Level.DEBUG);
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/controller/DefaultControllerExceptionHandler.java b/src/main/java/de/thm/arsnova/controller/DefaultControllerExceptionHandler.java
index fe8546c82..3d1249280 100644
--- a/src/main/java/de/thm/arsnova/controller/DefaultControllerExceptionHandler.java
+++ b/src/main/java/de/thm/arsnova/controller/DefaultControllerExceptionHandler.java
@@ -1,5 +1,6 @@
 package de.thm.arsnova.controller;
 
+import org.slf4j.event.Level;
 import org.springframework.core.annotation.AnnotationUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.ControllerAdvice;
@@ -26,6 +27,6 @@ public class DefaultControllerExceptionHandler extends AbstractControllerExcepti
 			throw e;
 		}
 
-		return handleException(e);
+		return handleException(e, Level.ERROR);
 	}
 }
diff --git a/src/main/resources/log4j-dev.properties b/src/main/resources/log4j-dev.properties
index bae1f9dff..021ba3029 100644
--- a/src/main/resources/log4j-dev.properties
+++ b/src/main/resources/log4j-dev.properties
@@ -1,8 +1,8 @@
 log4j.rootCategory=INFO, stdout
 
 log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n
+log4j.appender.stdout.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n%throwable
 
 log4j.category.de.thm.arsnova=DEBUG
 log4j.category.com.corundumstudio.socketio=INFO
diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties
index fb38b279b..b0ce03ef6 100644
--- a/src/main/resources/log4j.properties
+++ b/src/main/resources/log4j.properties
@@ -1,8 +1,8 @@
 log4j.rootCategory=INFO, stdout
 
 log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n
+log4j.appender.stdout.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n%throwable{2}
 
 log4j.category.de.thm.arsnova=INFO
 log4j.category.com.corundumstudio.socketio=INFO
-- 
GitLab