diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/AbstractLoggingInterceptor.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/AbstractLoggingInterceptor.java index a59b211414a..ae74648d0a2 100644 --- a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/AbstractLoggingInterceptor.java +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/AbstractLoggingInterceptor.java @@ -29,9 +29,12 @@ public abstract class AbstractLoggingInterceptor extends AbstractPhaseInterceptor { public static final int DEFAULT_LIMIT = 48 * 1024; + protected static final String CONTENT_SUPPRESSED = "--- Content suppressed ---"; protected int limit = DEFAULT_LIMIT; protected long threshold = -1; + protected boolean logBinary; + protected boolean logMultipart = true; protected LogEventSender sender; @@ -62,6 +65,20 @@ public void setPrettyLogging(boolean prettyLogging) { } } + protected boolean shouldLogContent(LogEvent event) { + return event.isBinaryContent() && logBinary + || event.isMultipartContent() && logMultipart + || !event.isBinaryContent() && !event.isMultipartContent(); + } + + public void setLogBinary(boolean logBinary) { + this.logBinary = logBinary; + } + + public void setLogMultipart(boolean logMultipart) { + this.logMultipart = logMultipart; + } + public void createExchangeId(Message message) { Exchange exchange = message.getExchange(); String exchangeId = (String)exchange.get(LogEvent.KEY_EXCHANGE_ID); diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingFeature.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingFeature.java index 78e76a736a3..1e34887bd64 100644 --- a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingFeature.java +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingFeature.java @@ -90,4 +90,22 @@ public void setSender(LogEventSender sender) { public void setPrettyLogging(boolean prettyLogging) { this.prettyFilter.setPrettyLogging(prettyLogging); } + + /** + * Log binary content? + * @param logBinary defaults to false + */ + public void setLogBinary(boolean logBinary) { + in.setLogBinary(logBinary); + out.setLogBinary(logBinary); + } + + /** + * Log multipart content? + * @param logMultipart defaults to true + */ + public void setLogMultipart(boolean logMultipart) { + in.setLogMultipart(logMultipart); + out.setLogMultipart(logMultipart); + } } diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java index 4c5760ec9e2..940603f9b51 100644 --- a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java @@ -50,6 +50,15 @@ public LoggingInInterceptor(LogEventSender sender) { public void handleMessage(Message message) throws Fault { createExchangeId(message); final LogEvent event = new DefaultLogEventMapper().map(message); + if (shouldLogContent(event)) { + addContent(message, event); + } else { + event.setPayload(AbstractLoggingInterceptor.CONTENT_SUPPRESSED); + } + sender.send(event); + } + + private void addContent(Message message, final LogEvent event) { try { CachedOutputStream cos = message.getContent(CachedOutputStream.class); if (cos != null) { @@ -63,8 +72,6 @@ public void handleMessage(Message message) throws Fault { } catch (IOException e) { throw new Fault(e); } - - sender.send(event); } private void handleOutputStream(final LogEvent event, Message message, CachedOutputStream cos) throws IOException { diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingOutInterceptor.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingOutInterceptor.java index ff4ff2a4138..8620021faf6 100644 --- a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingOutInterceptor.java +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingOutInterceptor.java @@ -79,7 +79,7 @@ private OutputStream createCachingOut(Message message, final OutputStream os, Ca return newOut; } - private static class LogEventSendingWriter extends FilterWriter { + private class LogEventSendingWriter extends FilterWriter { StringWriter out2; int count; Message message; @@ -126,6 +126,15 @@ public void close() throws IOException { if (w2 == null) { w2 = (StringWriter)out; } + + String payload = shouldLogContent(event) ? getPayload(event, w2) : CONTENT_SUPPRESSED; + event.setPayload(payload); + sender.send(event); + message.setContent(Writer.class, out); + super.close(); + } + + private String getPayload(final LogEvent event, StringWriter w2) { String ct = (String)message.get(Message.CONTENT_TYPE); StringBuilder payload = new StringBuilder(); try { @@ -133,10 +142,7 @@ public void close() throws IOException { } catch (Exception ex) { // ignore } - event.setPayload(payload.toString()); - sender.send(event); - message.setContent(Writer.class, out); - super.close(); + return payload.toString(); } protected void writePayload(StringBuilder builder, StringWriter stringWriter, String contentType) @@ -150,7 +156,7 @@ protected void writePayload(StringBuilder builder, StringWriter stringWriter, St } } - public static class LoggingCallback implements CachedOutputStreamCallback { + public class LoggingCallback implements CachedOutputStreamCallback { private final Message message; private final OutputStream origStream; @@ -170,13 +176,10 @@ public void onFlush(CachedOutputStream cos) { public void onClose(CachedOutputStream cos) { final LogEvent event = new DefaultLogEventMapper().map(message); - try { - String encoding = (String)message.get(Message.ENCODING); - StringBuilder payload = new StringBuilder(); - writePayload(payload, cos, encoding, event.getContentType()); - event.setPayload(payload.toString()); - } catch (Exception ex) { - // ignore + if (shouldLogContent(event)) { + copyPayload(cos, event); + } else { + event.setPayload(CONTENT_SUPPRESSED); } sender.send(event); @@ -189,6 +192,17 @@ public void onClose(CachedOutputStream cos) { } message.setContent(OutputStream.class, origStream); } + + private void copyPayload(CachedOutputStream cos, final LogEvent event) { + try { + String encoding = (String)message.get(Message.ENCODING); + StringBuilder payload = new StringBuilder(); + writePayload(payload, cos, encoding, event.getContentType()); + event.setPayload(payload.toString()); + } catch (Exception ex) { + // ignore + } + } protected void writePayload(StringBuilder builder, CachedOutputStream cos, String encoding, String contentType) throws Exception { diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/WireTapIn.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/WireTapIn.java index 527b80eae3b..e6b8508bda5 100644 --- a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/WireTapIn.java +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/WireTapIn.java @@ -61,7 +61,6 @@ public void handleMessage(final Message message) throws Fault { } catch (Exception e) { throw new Fault(e); } - } private void handleReader(Message message, Reader reader) throws IOException { @@ -103,4 +102,5 @@ public void setLimit(int limit) { public void setThreshold(long threshold) { this.threshold = threshold; } + } diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/DefaultLogEventMapper.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/DefaultLogEventMapper.java index 17a07bfcc8c..0123a1bc9fa 100644 --- a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/DefaultLogEventMapper.java +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/DefaultLogEventMapper.java @@ -55,6 +55,7 @@ public class DefaultLogEventMapper implements LogEventMapper { BINARY_CONTENT_MEDIA_TYPES.add("image/jpeg"); BINARY_CONTENT_MEDIA_TYPES.add("image/gif"); } + private static final String MULTIPART_CONTENT_MEDIA_TYPE = "multipart"; public LogEvent map(Message message) { final LogEvent event = new LogEvent(); @@ -83,6 +84,7 @@ public LogEvent map(Message message) { event.setPrincipal(getPrincipal(message)); event.setBinaryContent(isBinaryContent(message)); + event.setMultipartContent(isMultipartContent(message)); setEpInfo(message, event); return event; } @@ -170,6 +172,11 @@ private boolean isBinaryContent(Message message) { String contentType = safeGet(message, Message.CONTENT_TYPE); return contentType != null && BINARY_CONTENT_MEDIA_TYPES.contains(contentType); } + + private boolean isMultipartContent(Message message) { + String contentType = safeGet(message, Message.CONTENT_TYPE); + return contentType != null && contentType.startsWith(MULTIPART_CONTENT_MEDIA_TYPE); + } /** * check if a Message is a Rest Message @@ -260,7 +267,7 @@ private String getRestOperationName(Message curMessage) { return new StringBuffer().append(httpMethod).append('[').append(path).append(']').toString(); } - private String safeGet(Message message, String key) { + private static String safeGet(Message message, String key) { if (!message.containsKey(key)) { return null; } diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/LogEvent.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/LogEvent.java index 92a4a698861..2ce1b83303f 100644 --- a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/LogEvent.java +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/LogEvent.java @@ -40,6 +40,7 @@ public final class LogEvent { private String operationName; private Map headers; private boolean binaryContent; + private boolean multipartContent; private String payload; private boolean truncated; private File fullContentFile; @@ -167,6 +168,14 @@ public boolean isBinaryContent() { public void setBinaryContent(boolean binaryContent) { this.binaryContent = binaryContent; } + + public boolean isMultipartContent() { + return multipartContent; + } + + public void setMultipartContent(boolean multipartContent) { + this.multipartContent = multipartContent; + } public String getPayload() { return payload; diff --git a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/RESTLoggingTest.java b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/RESTLoggingTest.java index 20dc3bb51be..088daa9128b 100644 --- a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/RESTLoggingTest.java +++ b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/RESTLoggingTest.java @@ -19,6 +19,7 @@ package org.apache.cxf.ext.logging; import java.io.IOException; +import java.io.InputStream; import java.net.MalformedURLException; import java.util.Collections; import java.util.List; @@ -43,8 +44,40 @@ public void testSlf4j() throws IOException { server.start(); WebClient client = createClient(loggingFeature); String result = client.get(String.class); + server.destroy(); Assert.assertEquals("test1", result); + } + + @Test + public void testBinary() throws IOException { + LoggingFeature loggingFeature = new LoggingFeature(); + TestEventSender sender = new TestEventSender(); + loggingFeature.setSender(sender); + Server server = createServiceBinary(loggingFeature); + server.start(); + WebClient client = createClient(loggingFeature); + client.get(InputStream.class).close(); + loggingFeature.setLogBinary(true); + client.get(InputStream.class).close(); server.destroy(); + + assertLogged(sender.getEvents().get(0)); + assertLogged(sender.getEvents().get(1)); + assertNotLogged(sender.getEvents().get(2)); + assertNotLogged(sender.getEvents().get(3)); + + assertLogged(sender.getEvents().get(4)); + assertLogged(sender.getEvents().get(5)); + assertLogged(sender.getEvents().get(6)); + assertLogged(sender.getEvents().get(7)); + } + + private void assertLogged(LogEvent event) { + Assert.assertNotEquals(AbstractLoggingInterceptor.CONTENT_SUPPRESSED, event.getPayload()); + } + + private void assertNotLogged(LogEvent event) { + Assert.assertEquals(AbstractLoggingInterceptor.CONTENT_SUPPRESSED, event.getPayload()); } private WebClient createClient(LoggingFeature loggingFeature) { @@ -62,6 +95,14 @@ private Server createService(LoggingFeature loggingFeature) { return factory.create(); } + private Server createServiceBinary(LoggingFeature loggingFeature) { + JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean(); + factory.setAddress(SERVICE_URI); + factory.setFeatures(Collections.singletonList(loggingFeature)); + factory.setServiceBean(new TestServiceRestBinary()); + return factory.create(); + } + @Test public void testEvents() throws MalformedURLException { LoggingFeature loggingFeature = new LoggingFeature(); diff --git a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestServiceRest.java b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestServiceRest.java index f56639d656a..9ba323f4fa9 100644 --- a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestServiceRest.java +++ b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestServiceRest.java @@ -28,4 +28,5 @@ public class TestServiceRest { public String echo(@PathParam("msg") String msg) { return msg; } + } diff --git a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestServiceRestBinary.java b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestServiceRestBinary.java new file mode 100644 index 00000000000..d7ac812fe68 --- /dev/null +++ b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestServiceRestBinary.java @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.ext.logging; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +public class TestServiceRestBinary { + @GET + @Path("test1") + @Produces(MediaType.APPLICATION_OCTET_STREAM) + public byte[] getBinary() { + return new byte[] {1, 2, 3, 4, 5, 6, 8, 9}; + } + +}