[CXF-7242] Update new LoggingInInterceptor to actually log payloads Also update new logging to take a PrintStream to make it more compatible with old interceptors
Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/f56aa148 Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/f56aa148 Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/f56aa148 Branch: refs/heads/3.1.x-fixes Commit: f56aa148e9e54d6f9a2642d7c2160cff13e0231e Parents: 4c76926 Author: Daniel Kulp <[email protected]> Authored: Fri Feb 3 14:47:09 2017 -0500 Committer: Daniel Kulp <[email protected]> Committed: Tue Feb 14 09:53:17 2017 -0500 ---------------------------------------------------------------------- .../cxf/ext/logging/LoggingInInterceptor.java | 171 ++++++++++++++----- .../cxf/ext/logging/LoggingOutInterceptor.java | 6 +- .../logging/event/DefaultLogEventMapper.java | 4 +- .../logging/event/PrintWriterEventSender.java | 77 +++++++++ 4 files changed, 213 insertions(+), 45 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/f56aa148/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java ---------------------------------------------------------------------- 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 940603f..6c19109 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 @@ -18,84 +18,171 @@ */ package org.apache.cxf.ext.logging; -import java.io.IOException; -import java.nio.charset.StandardCharsets; +import java.io.InputStream; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.SequenceInputStream; +import java.util.Collection; +import java.util.Collections; import org.apache.cxf.common.injection.NoJSR250Annotations; import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.ext.logging.event.DefaultLogEventMapper; +import org.apache.cxf.ext.logging.event.EventType; import org.apache.cxf.ext.logging.event.LogEvent; import org.apache.cxf.ext.logging.event.LogEventSender; +import org.apache.cxf.ext.logging.event.PrintWriterEventSender; import org.apache.cxf.ext.logging.slf4j.Slf4jEventSender; +import org.apache.cxf.helpers.IOUtils; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.io.CachedOutputStream; import org.apache.cxf.io.CachedWriter; +import org.apache.cxf.io.DelegatingInputStream; import org.apache.cxf.message.Message; +import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; +import org.apache.cxf.phase.PhaseInterceptor; /** * */ @NoJSR250Annotations public class LoggingInInterceptor extends AbstractLoggingInterceptor { - + class SendLogEventInterceptor extends AbstractPhaseInterceptor<Message> { + SendLogEventInterceptor() { + super(Phase.PRE_INVOKE); + } + @Override + public void handleMessage(Message message) throws Fault { + LogEvent event = message.get(LogEvent.class); + if (event != null) { + DefaultLogEventMapper mapper = new DefaultLogEventMapper(); + mapper.setEpInfo(message, event); + event.setType(mapper.getEventType(message)); + message.remove(LogEvent.class); + sender.send(event); + } + } + } + public LoggingInInterceptor() { this(new Slf4jEventSender()); } - + public LoggingInInterceptor(PrintWriter writer) { + this(new PrintWriterEventSender(writer)); + } public LoggingInInterceptor(LogEventSender sender) { - super(Phase.PRE_INVOKE, sender); + super(Phase.RECEIVE, sender); + } + public Collection<PhaseInterceptor<? extends Message>> getAdditionalInterceptors() { + return Collections.singleton(new SendLogEventInterceptor()); + } + + + public void handleFault(Message message) { + LogEvent event = message.get(LogEvent.class); + if (event != null) { + DefaultLogEventMapper mapper = new DefaultLogEventMapper(); + mapper.setEpInfo(message, event); + event.setType(EventType.FAULT_IN); + message.remove(LogEvent.class); + sender.send(event); + } } - 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); + LogEvent event = message.get(LogEvent.class); + if (event == null) { + createExchangeId(message); + event = new DefaultLogEventMapper().map(message); + if (shouldLogContent(event)) { + addContent(message, event); + } else { + event.setPayload(AbstractLoggingInterceptor.CONTENT_SUPPRESSED); + } + // at this point, we have the payload. However, we may not have the endpoint yet. Delay sending + // the event till a little bit later + message.put(LogEvent.class, event); } - sender.send(event); } private void addContent(Message message, final LogEvent event) { + InputStream is = message.getContent(InputStream.class); + if (is != null) { + logInputStream(message, is, event); + } else { + Reader reader = message.getContent(Reader.class); + if (reader != null) { + logReader(message, reader, event); + } + } + } + + protected void logInputStream(Message message, InputStream is, LogEvent event) { + CachedOutputStream bos = new CachedOutputStream(); + if (threshold > 0) { + bos.setThreshold(threshold); + } + String encoding = event.getEncoding(); try { - CachedOutputStream cos = message.getContent(CachedOutputStream.class); - if (cos != null) { - handleOutputStream(event, message, cos); + // use the appropriate input stream and restore it later + InputStream bis = is instanceof DelegatingInputStream + ? ((DelegatingInputStream)is).getInputStream() : is; + + + //only copy up to the limit since that's all we need to log + //we can stream the rest + IOUtils.copyAtLeast(bis, bos, limit == -1 ? Integer.MAX_VALUE : limit); + bos.flush(); + bis = new SequenceInputStream(bos.getInputStream(), bis); + + // restore the delegating input stream or the input stream + if (is instanceof DelegatingInputStream) { + ((DelegatingInputStream)is).setInputStream(bis); } else { - CachedWriter writer = message.getContent(CachedWriter.class); - if (writer != null) { - handleWriter(event, writer); - } + message.setContent(InputStream.class, bis); } - } catch (IOException e) { + + if (bos.getTempFile() != null) { + //large thing on disk... + event.setFullContentFile(bos.getTempFile()); + } + if (bos.size() > limit && limit != -1) { + event.setTruncated(true); + } + + StringBuilder builder = new StringBuilder(limit); + if (StringUtils.isEmpty(encoding)) { + bos.writeCacheTo(builder, limit); + } else { + bos.writeCacheTo(builder, encoding, limit); + } + bos.close(); + event.setPayload(builder.toString()); + } catch (Exception e) { throw new Fault(e); } } - private void handleOutputStream(final LogEvent event, Message message, CachedOutputStream cos) throws IOException { - String encoding = (String)message.get(Message.ENCODING); - if (StringUtils.isEmpty(encoding)) { - encoding = StandardCharsets.UTF_8.name(); + protected void logReader(Message message, Reader reader, LogEvent event) { + try { + CachedWriter writer = new CachedWriter(); + IOUtils.copyAndCloseInput(reader, writer); + message.setContent(Reader.class, writer.getReader()); + + if (writer.getTempFile() != null) { + //large thing on disk... + event.setFullContentFile(writer.getTempFile()); + } + if (writer.size() > limit && limit != -1) { + event.setTruncated(true); + } + int max = writer.size() > limit ? (int)limit : (int)writer.size(); + StringBuilder b = new StringBuilder(max); + writer.writeCacheTo(b); + event.setPayload(b.toString()); + } catch (Exception e) { + throw new Fault(e); } - StringBuilder payload = new StringBuilder(); - cos.writeCacheTo(payload, encoding, limit); - cos.close(); - event.setPayload(payload.toString()); - boolean isTruncated = cos.size() > limit && limit != -1; - event.setTruncated(isTruncated); - event.setFullContentFile(cos.getTempFile()); - } - - private void handleWriter(final LogEvent event, CachedWriter writer) throws IOException { - boolean isTruncated = writer.size() > limit && limit != -1; - StringBuilder payload = new StringBuilder(); - writer.writeCacheTo(payload, limit); - event.setPayload(payload.toString()); - event.setTruncated(isTruncated); - event.setFullContentFile(writer.getTempFile()); } - } http://git-wip-us.apache.org/repos/asf/cxf/blob/f56aa148/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingOutInterceptor.java ---------------------------------------------------------------------- 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 8620021..2885f6e 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 @@ -21,6 +21,7 @@ package org.apache.cxf.ext.logging; import java.io.FilterWriter; import java.io.IOException; import java.io.OutputStream; +import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; @@ -29,6 +30,7 @@ import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.ext.logging.event.DefaultLogEventMapper; import org.apache.cxf.ext.logging.event.LogEvent; import org.apache.cxf.ext.logging.event.LogEventSender; +import org.apache.cxf.ext.logging.event.PrintWriterEventSender; import org.apache.cxf.ext.logging.slf4j.Slf4jEventSender; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.interceptor.StaxOutInterceptor; @@ -47,7 +49,9 @@ public class LoggingOutInterceptor extends AbstractLoggingInterceptor { public LoggingOutInterceptor() { this(new Slf4jEventSender()); } - + public LoggingOutInterceptor(PrintWriter w) { + this(new PrintWriterEventSender(w)); + } public LoggingOutInterceptor(LogEventSender sender) { super(Phase.PRE_STREAM, sender); addBefore(StaxOutInterceptor.class.getName()); http://git-wip-us.apache.org/repos/asf/cxf/blob/f56aa148/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/DefaultLogEventMapper.java ---------------------------------------------------------------------- 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 0123a1b..f48629f 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 @@ -281,7 +281,7 @@ public class DefaultLogEventMapper implements LogEventMapper { * @param message the message * @return the event type */ - private EventType getEventType(Message message) { + public EventType getEventType(Message message) { boolean isRequestor = MessageUtils.isRequestor(message); boolean isFault = MessageUtils.isFault(message); if (!isFault) { @@ -320,7 +320,7 @@ public class DefaultLogEventMapper implements LogEventMapper { } } - private void setEpInfo(Message message, final LogEvent event) { + public void setEpInfo(Message message, final LogEvent event) { EndpointInfo endpoint = getEPInfo(message); event.setPortName(endpoint.getName()); event.setPortTypeName(endpoint.getName()); http://git-wip-us.apache.org/repos/asf/cxf/blob/f56aa148/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/PrintWriterEventSender.java ---------------------------------------------------------------------- diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/PrintWriterEventSender.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/PrintWriterEventSender.java new file mode 100644 index 0000000..b34133d --- /dev/null +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/PrintWriterEventSender.java @@ -0,0 +1,77 @@ +/** + * 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.event; + +import java.io.PrintWriter; +import java.time.Instant; + +import javax.xml.namespace.QName; + +/** + * + */ +public class PrintWriterEventSender implements LogEventSender { + PrintWriter writer; + + public PrintWriterEventSender(PrintWriter writer) { + this.writer = writer; + } + + void setPrintWriter(PrintWriter w) { + writer = w; + } + + + /** {@inheritDoc}*/ + @Override + public void send(LogEvent event) { + StringBuilder b = new StringBuilder(); + + b.append(Instant.now().toString()).append(" - PrintWriterEventSender\n"); + put(b, "type", event.getType().toString()); + put(b, "address", event.getAddress()); + put(b, "content-type", event.getContentType()); + put(b, "encoding", event.getEncoding()); + put(b, "exchangeId", event.getExchangeId()); + put(b, "httpMethod", event.getHttpMethod()); + put(b, "messageId", event.getMessageId()); + put(b, "responseCode", event.getResponseCode()); + put(b, "serviceName", localPart(event.getServiceName())); + put(b, "portName", localPart(event.getPortName())); + put(b, "portTypeName", localPart(event.getPortTypeName())); + if (event.getFullContentFile() != null) { + put(b, "fullContentFile", event.getFullContentFile().getAbsolutePath()); + } + put(b, "headers", event.getHeaders().toString()); + synchronized (writer) { + writer.print(b.toString()); + writer.println(event.getPayload()); + } + } + protected String localPart(QName name) { + return name == null ? null : name.getLocalPart(); + } + + protected void put(StringBuilder b, String key, String value) { + if (value != null) { + b.append(" ").append(key).append(": ").append(value).append("\n"); + } + } +}
