mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-17 10:11:27 +00:00
Improved request logging
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
package org.cryptomator.webdav.filters;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LoggingHttpFilter implements HttpFilter {
|
||||
|
||||
private static final Set<String> METHODS_TO_LOG_DETAILED = methodsToLog();
|
||||
|
||||
private static final Set<String> methodsToLog() {
|
||||
String methodsToLog = System.getProperty("cryptomator.LoggingHttpFilter.methodsToLogDetailed");
|
||||
if (methodsToLog == null) {
|
||||
return Collections.emptySet();
|
||||
} else {
|
||||
return new HashSet<>(asList(methodsToLog.toUpperCase().split(",")));
|
||||
}
|
||||
}
|
||||
|
||||
private final Logger LOG = LoggerFactory.getLogger(LoggingHttpFilter.class);
|
||||
|
||||
@Override
|
||||
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
if (METHODS_TO_LOG_DETAILED.contains(request.getMethod().toUpperCase())) {
|
||||
logDetailed(request, response, chain);
|
||||
} else {
|
||||
logBasic(request, response, chain);
|
||||
}
|
||||
}
|
||||
|
||||
private void logBasic(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
Optional<Throwable> thrown = Optional.empty();
|
||||
try {
|
||||
chain.doFilter(request, response);
|
||||
} catch (IOException | ServletException e) {
|
||||
thrown = Optional.of(e);
|
||||
throw e;
|
||||
} catch (RuntimeException | Error e) {
|
||||
thrown = Optional.of(e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (thrown.isPresent()) {
|
||||
logError(request, thrown.get());
|
||||
} else {
|
||||
logSuccess(request, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void logDetailed(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
RecordingHttpServletRequest recordingRequest = new RecordingHttpServletRequest(request);
|
||||
RecordingHttpServletResponse recordingResponse = new RecordingHttpServletResponse(response);
|
||||
Optional<Throwable> thrown = Optional.empty();
|
||||
try {
|
||||
chain.doFilter(recordingRequest, recordingResponse);
|
||||
} catch (IOException | ServletException e) {
|
||||
thrown = Optional.of(e);
|
||||
throw e;
|
||||
} catch (RuntimeException | Error e) {
|
||||
thrown = Optional.of(e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (thrown.isPresent()) {
|
||||
logError(recordingRequest, thrown.get());
|
||||
} else {
|
||||
logSuccess(recordingRequest, recordingResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void logSuccess(HttpServletRequest request, HttpServletResponse response) {
|
||||
LOG.debug(format(
|
||||
"## Request ##\n" + //
|
||||
"%s %s %s\n" //
|
||||
+ "%s\n" //
|
||||
+ "## Response ##\n" //
|
||||
+ "%s %s\n" //
|
||||
+ "%s\n", //
|
||||
request.getMethod(), request.getRequestURI(), request.getProtocol(), //
|
||||
headers(request), //
|
||||
request.getProtocol(), response.getStatus(), //
|
||||
headers(response)));
|
||||
}
|
||||
|
||||
private void logError(HttpServletRequest request, Throwable throwable) {
|
||||
LOG.error(
|
||||
format("## Request ##\n" + //
|
||||
"%s %s %s\n" //
|
||||
+ "%s\n" //
|
||||
+ "%s\n\n", //
|
||||
request.getMethod(), request.getRequestURI(), request.getProtocol(), //
|
||||
headers(request)), //
|
||||
throwable);
|
||||
}
|
||||
|
||||
private void logSuccess(RecordingHttpServletRequest request, RecordingHttpServletResponse response) {
|
||||
LOG.debug(format(
|
||||
"## Request ##\n" + //
|
||||
"%s %s %s\n" //
|
||||
+ "%s\n" //
|
||||
+ "%s\n\n" //
|
||||
+ "## Response ##\n" //
|
||||
+ "%s %s\n" //
|
||||
+ "%s\n" //
|
||||
+ "%s", //
|
||||
request.getMethod(), request.getRequestURI(), request.getProtocol(), //
|
||||
headers(request), //
|
||||
new String(request.getRecording()), //
|
||||
request.getProtocol(), response.getStatus(), //
|
||||
headers(response), //
|
||||
new String(response.getRecording())));
|
||||
}
|
||||
|
||||
private void logError(RecordingHttpServletRequest request, Throwable throwable) {
|
||||
LOG.error(
|
||||
format("## Request ##\n" + //
|
||||
"%s %s %s\n" //
|
||||
+ "%s\n" //
|
||||
+ "%s\n\n", //
|
||||
request.getMethod(), request.getRequestURI(), request.getProtocol(), //
|
||||
headers(request), //
|
||||
new String(request.getRecording())), //
|
||||
throwable);
|
||||
}
|
||||
|
||||
private String headers(HttpServletResponse response) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (String headerName : response.getHeaderNames()) {
|
||||
for (String value : response.getHeaders(headerName)) {
|
||||
result.append(headerName).append(": ").append(value).append('\n');
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private String headers(HttpServletRequest request) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
Enumeration<String> headerNames = request.getHeaderNames();
|
||||
while (headerNames.hasMoreElements()) {
|
||||
String headerName = headerNames.nextElement();
|
||||
Enumeration<String> values = request.getHeaders(headerName);
|
||||
while (values.hasMoreElements()) {
|
||||
result.append(headerName).append(": ").append(values.nextElement()).append('\n');
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
// empty
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// empty
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.cryptomator.webdav.filters;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
|
||||
class RecordingHttpServletRequest extends HttpServletRequestWrapper {
|
||||
|
||||
private final RecordingServletInputStream recording;
|
||||
|
||||
public RecordingHttpServletRequest(HttpServletRequest request) throws IOException {
|
||||
super(request);
|
||||
recording = new RecordingServletInputStream(request.getInputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletInputStream getInputStream() throws IOException {
|
||||
return recording;
|
||||
}
|
||||
|
||||
public byte[] getRecording() {
|
||||
return recording.getRecording();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.cryptomator.webdav.filters;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletResponseWrapper;
|
||||
|
||||
class RecordingHttpServletResponse extends HttpServletResponseWrapper {
|
||||
|
||||
private final RecordingServletOutputStream recording;
|
||||
|
||||
public RecordingHttpServletResponse(HttpServletResponse response) throws IOException {
|
||||
super(response);
|
||||
recording = new RecordingServletOutputStream(response.getOutputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletOutputStream getOutputStream() throws IOException {
|
||||
return recording;
|
||||
}
|
||||
|
||||
public byte[] getRecording() {
|
||||
return recording.getRecording();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package org.cryptomator.webdav.filters;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ReadListener;
|
||||
import javax.servlet.ServletInputStream;
|
||||
|
||||
import org.apache.commons.io.input.TeeInputStream;
|
||||
|
||||
class RecordingServletInputStream extends ServletInputStream {
|
||||
|
||||
private final ServletInputStream delegate;
|
||||
private final TeeInputStream teeInputStream;
|
||||
private final ByteArrayOutputStream recording = new ByteArrayOutputStream(4096);
|
||||
|
||||
public RecordingServletInputStream(ServletInputStream delegate) {
|
||||
this.delegate = delegate;
|
||||
this.teeInputStream = new TeeInputStream(delegate, recording);
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
return teeInputStream.read();
|
||||
}
|
||||
|
||||
public int read(byte[] b) throws IOException {
|
||||
return teeInputStream.read(b);
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
return teeInputStream.read(b, off, len);
|
||||
}
|
||||
|
||||
public boolean isFinished() {
|
||||
return delegate.isFinished();
|
||||
}
|
||||
|
||||
public boolean isReady() {
|
||||
return delegate.isReady();
|
||||
}
|
||||
|
||||
public void setReadListener(ReadListener readListener) {
|
||||
delegate.setReadListener(readListener);
|
||||
}
|
||||
|
||||
public long skip(long n) throws IOException {
|
||||
return teeInputStream.skip(n);
|
||||
}
|
||||
|
||||
public int available() throws IOException {
|
||||
return teeInputStream.available();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
teeInputStream.close();
|
||||
}
|
||||
|
||||
public byte[] getRecording() {
|
||||
return recording.toByteArray();
|
||||
}
|
||||
|
||||
public void mark(int readlimit) {
|
||||
}
|
||||
|
||||
public void reset() throws IOException {
|
||||
throw new IOException("Mark not supported");
|
||||
}
|
||||
|
||||
public boolean markSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.cryptomator.webdav.filters;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.WriteListener;
|
||||
|
||||
import org.apache.commons.io.output.TeeOutputStream;
|
||||
|
||||
class RecordingServletOutputStream extends ServletOutputStream {
|
||||
|
||||
private final ServletOutputStream delegate;
|
||||
private final TeeOutputStream teeOutputStream;
|
||||
private final ByteArrayOutputStream recording = new ByteArrayOutputStream(4096);
|
||||
|
||||
public RecordingServletOutputStream(ServletOutputStream delegate) {
|
||||
this.delegate = delegate;
|
||||
this.teeOutputStream = new TeeOutputStream(delegate, recording);
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
teeOutputStream.write(b);
|
||||
}
|
||||
|
||||
public void write(byte[] b) throws IOException {
|
||||
teeOutputStream.write(b);
|
||||
}
|
||||
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
teeOutputStream.write(b, off, len);
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
teeOutputStream.flush();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
teeOutputStream.close();
|
||||
}
|
||||
|
||||
public boolean isReady() {
|
||||
return delegate.isReady();
|
||||
}
|
||||
|
||||
public void setWriteListener(WriteListener writeListener) {
|
||||
delegate.setWriteListener(writeListener);
|
||||
}
|
||||
|
||||
public byte[] getRecording() {
|
||||
return recording.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import javax.servlet.DispatcherType;
|
||||
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.webdav.filters.AcceptRangeFilter;
|
||||
import org.cryptomator.webdav.filters.LoggingHttpFilter;
|
||||
import org.cryptomator.webdav.filters.UriNormalizationFilter;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
@@ -54,6 +55,7 @@ class FileSystemBasedWebDavServer {
|
||||
servletContext.addServlet(servletHolder, "/*");
|
||||
servletContext.addFilter(AcceptRangeFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
|
||||
servletContext.addFilter(UriNormalizationFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
|
||||
servletContext.addFilter(LoggingHttpFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
|
||||
servletCollection.mapContexts();
|
||||
|
||||
server.setConnectors(new Connector[] { localConnector });
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
<Loggers>
|
||||
<!-- show our own debug messages: -->
|
||||
<Logger name="org.cryptomator" level="DEBUG" />
|
||||
<Logger name="org.eclipse.jetty.server.Server" level="DEBUG" />
|
||||
<!-- mute dependencies: -->
|
||||
<Root level="INFO">
|
||||
<AppenderRef ref="Console" />
|
||||
|
||||
Reference in New Issue
Block a user