1
18
19 package io.undertow.servlet.api;
20
21 import io.undertow.UndertowLogger;
22 import io.undertow.server.HttpServerExchange;
23 import io.undertow.servlet.ExceptionLog;
24 import org.jboss.logging.BasicLogger;
25 import org.jboss.logging.Logger;
26
27 import java.io.IOException;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.Map;
31 import javax.servlet.ServletRequest;
32 import javax.servlet.ServletResponse;
33
34
40 public class LoggingExceptionHandler implements ExceptionHandler {
41
42 public static final LoggingExceptionHandler DEFAULT = new LoggingExceptionHandler(Collections.<Class<? extends Throwable>, ExceptionDetails>emptyMap());
43
44 private final Map<Class<? extends Throwable>, ExceptionDetails> exceptionDetails;
45
46 public LoggingExceptionHandler(Map<Class<? extends Throwable>, ExceptionDetails> exceptionDetails) {
47 this.exceptionDetails = exceptionDetails;
48 }
49
50 @Override
51 public boolean handleThrowable(HttpServerExchange exchange, ServletRequest request, ServletResponse response, Throwable t) {
52 ExceptionDetails details = null;
53 if (!exceptionDetails.isEmpty()) {
54 Class c = t.getClass();
55 while (c != null && c != Object.class) {
56 details = exceptionDetails.get(c);
57 if (details != null) {
58 break;
59 }
60 c = c.getSuperclass();
61 }
62 }
63
64 ExceptionLog log = t.getClass().getAnnotation(ExceptionLog.class);
65 if (details != null) {
66 Logger.Level level = details.level;
67 Logger.Level stackTraceLevel = details.stackTraceLevel;
68 String category = details.category;
69 handleCustomLog(exchange, t, level, stackTraceLevel, category);
70 } else if (log != null) {
71 Logger.Level level = log.value();
72 Logger.Level stackTraceLevel = log.stackTraceLevel();
73 String category = log.category();
74 handleCustomLog(exchange, t, level, stackTraceLevel, category);
75 } else if (t instanceof IOException) {
76
77
78 UndertowLogger.REQUEST_IO_LOGGER.debugf(t, "Exception handling request to %s", exchange.getRequestURI());
79 } else {
80 UndertowLogger.REQUEST_LOGGER.exceptionHandlingRequest(t, exchange.getRequestURI());
81 }
82 return false;
83 }
84
85 private void handleCustomLog(HttpServerExchange exchange, Throwable t, Logger.Level level, Logger.Level stackTraceLevel, String category) {
86 BasicLogger logger = UndertowLogger.REQUEST_LOGGER;
87 if (!category.isEmpty()) {
88 logger = Logger.getLogger(category);
89 }
90 boolean stackTrace = true;
91 if (stackTraceLevel.ordinal() > level.ordinal()) {
92 if (!logger.isEnabled(stackTraceLevel)) {
93 stackTrace = false;
94 }
95 }
96 if (stackTrace) {
97 logger.logf(level, t, "Exception handling request to %s", exchange.getRequestURI());
98 } else {
99 logger.logf(level, "Exception handling request to %s: %s", exchange.getRequestURI(), t.getMessage());
100 }
101 }
102
103
104 private static class ExceptionDetails {
105
106 final Logger.Level level;
107
108 final Logger.Level stackTraceLevel;
109
110 final String category;
111
112 private ExceptionDetails(Logger.Level level, Logger.Level stackTraceLevel, String category) {
113 this.level = level;
114 this.stackTraceLevel = stackTraceLevel;
115 this.category = category;
116 }
117 }
118
119 public static Builder builder() {
120 return new Builder();
121 }
122
123 public static final class Builder {
124 private final Map<Class<? extends Throwable>, ExceptionDetails> exceptionDetails = new HashMap<>();
125
126 Builder() {}
127
128 public Builder add(Class<? extends Throwable> exception, String category, Logger.Level level) {
129 exceptionDetails.put(exception, new ExceptionDetails(level, Logger.Level.FATAL, category));
130 return this;
131 }
132 public Builder add(Class<? extends Throwable> exception, String category) {
133 exceptionDetails.put(exception, new ExceptionDetails(Logger.Level.ERROR, Logger.Level.FATAL, category));
134 return this;
135 }
136 public Builder add(Class<? extends Throwable> exception, String category, Logger.Level level, Logger.Level stackTraceLevel) {
137 exceptionDetails.put(exception, new ExceptionDetails(level, stackTraceLevel, category));
138 return this;
139 }
140
141 public LoggingExceptionHandler build() {
142 return new LoggingExceptionHandler(exceptionDetails);
143 }
144 }
145 }
146