Return-Path: X-Original-To: apmail-cxf-commits-archive@www.apache.org Delivered-To: apmail-cxf-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 042631766C for ; Wed, 11 Mar 2015 15:18:36 +0000 (UTC) Received: (qmail 80044 invoked by uid 500); 11 Mar 2015 15:18:29 -0000 Delivered-To: apmail-cxf-commits-archive@cxf.apache.org Received: (qmail 79974 invoked by uid 500); 11 Mar 2015 15:18:29 -0000 Mailing-List: contact commits-help@cxf.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cxf.apache.org Delivered-To: mailing list commits@cxf.apache.org Received: (qmail 79965 invoked by uid 99); 11 Mar 2015 15:18:29 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 11 Mar 2015 15:18:29 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 278C2E0C0F; Wed, 11 Mar 2015 15:18:29 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: cschneider@apache.org To: commits@cxf.apache.org Message-Id: <02c988ffad044a25a43f46678c6c3859@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: cxf git commit: [CXF-6286] New logging feature as separate module Date: Wed, 11 Mar 2015 15:18:29 +0000 (UTC) Repository: cxf Updated Branches: refs/heads/master e805911d4 -> 28e8f04af [CXF-6286] New logging feature as separate module Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/28e8f04a Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/28e8f04a Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/28e8f04a Branch: refs/heads/master Commit: 28e8f04af96d3e353f0da5018a4531d45592c663 Parents: e805911 Author: Christian Schneider Authored: Wed Mar 11 16:16:55 2015 +0100 Committer: Christian Schneider Committed: Wed Mar 11 16:18:04 2015 +0100 ---------------------------------------------------------------------- rt/features/logging/pom.xml | 72 ++++ .../ext/logging/AbstractLoggingInterceptor.java | 66 ++++ .../apache/cxf/ext/logging/LoggingFeature.java | 82 +++++ .../cxf/ext/logging/LoggingInInterceptor.java | 87 +++++ .../cxf/ext/logging/LoggingOutInterceptor.java | 198 +++++++++++ .../org/apache/cxf/ext/logging/WireTapIn.java | 100 ++++++ .../logging/event/DefaultLogEventMapper.java | 336 +++++++++++++++++++ .../apache/cxf/ext/logging/event/EventType.java | 28 ++ .../apache/cxf/ext/logging/event/LogEvent.java | 196 +++++++++++ .../cxf/ext/logging/event/LogEventMapper.java | 25 ++ .../cxf/ext/logging/event/LogEventSender.java | 27 ++ .../cxf/ext/logging/slf4j/Slf4jEventSender.java | 79 +++++ .../apache/cxf/ext/logging/RESTLoggingTest.java | 135 ++++++++ .../apache/cxf/ext/logging/SOAPLoggingTest.java | 144 ++++++++ .../apache/cxf/ext/logging/TestEventSender.java | 38 +++ .../org/apache/cxf/ext/logging/TestService.java | 26 ++ .../apache/cxf/ext/logging/TestServiceRest.java | 31 ++ .../logging/src/test/resources/log4j.properties | 5 + rt/pom.xml | 1 + 19 files changed, 1676 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/rt/features/logging/pom.xml ---------------------------------------------------------------------- diff --git a/rt/features/logging/pom.xml b/rt/features/logging/pom.xml new file mode 100644 index 0000000..4a51e1f --- /dev/null +++ b/rt/features/logging/pom.xml @@ -0,0 +1,72 @@ + + 4.0.0 + + org.apache.cxf + cxf-parent + 3.1.0-SNAPSHOT + ../../../parent/pom.xml + + logging + logging + org.apache.cxf.ext + + + + org.apache.cxf + cxf-core + ${project.version} + + + org.slf4j + slf4j-api + + + org.apache.cxf + cxf-rt-bindings-soap + ${project.version} + + + + org.slf4j + slf4j-log4j12 + 1.7.7 + + + junit + junit + test + + + org.easymock + easymock + test + + + org.apache.cxf + cxf-rt-frontend-jaxws + ${project.version} + test + + + org.apache.cxf + cxf-rt-frontend-jaxrs + ${project.version} + test + + + org.apache.cxf + cxf-rt-rs-client + ${project.version} + test + + + org.apache.cxf + cxf-rt-transports-http-jetty + ${project.version} + test + + + \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/AbstractLoggingInterceptor.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..03590fb --- /dev/null +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/AbstractLoggingInterceptor.java @@ -0,0 +1,66 @@ +/** + * 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 java.util.UUID; + +import org.apache.cxf.ext.logging.event.LogEvent; +import org.apache.cxf.ext.logging.event.LogEventSender; +import org.apache.cxf.message.Exchange; +import org.apache.cxf.message.Message; +import org.apache.cxf.phase.AbstractPhaseInterceptor; + +abstract class AbstractLoggingInterceptor extends AbstractPhaseInterceptor { + public static final int DEFAULT_LIMIT = 48 * 1024; + + protected int limit = DEFAULT_LIMIT; + protected long threshold = -1; + + protected LogEventSender sender; + + public AbstractLoggingInterceptor(String phase, LogEventSender sender) { + super(phase); + this.sender = sender; + } + + public void setLimit(int lim) { + limit = lim; + } + + public int getLimit() { + return limit; + } + + public void setInMemThreshold(long t) { + threshold = t; + } + + public long getInMemThreshold() { + return threshold; + } + + public void createExchangeId(Message message) { + Exchange exchange = message.getExchange(); + String exchangeId = (String)exchange.get(LogEvent.KEY_EXCHANGE_ID); + if (exchangeId == null) { + exchangeId = UUID.randomUUID().toString(); + exchange.put(LogEvent.KEY_EXCHANGE_ID, exchangeId); + } + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingFeature.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..8906f0b --- /dev/null +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingFeature.java @@ -0,0 +1,82 @@ +/** + * 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 org.apache.cxf.Bus; +import org.apache.cxf.common.injection.NoJSR250Annotations; +import org.apache.cxf.ext.logging.event.LogEventSender; +import org.apache.cxf.ext.logging.slf4j.Slf4jEventSender; +import org.apache.cxf.feature.AbstractFeature; +import org.apache.cxf.interceptor.InterceptorProvider; + +/** + * This class is used to control message-on-the-wire logging. + * By attaching this feature to an endpoint, you + * can specify logging. If this feature is present, an endpoint will log input + * and output of ordinary and log messages. + *
+ * 
+      
+       
+      
+    
+  ]]>
+  
+ */ +@NoJSR250Annotations +public class LoggingFeature extends AbstractFeature { + private int limit = AbstractLoggingInterceptor.DEFAULT_LIMIT; + private long inMemThreshold; + private LogEventSender sender; + + public LoggingFeature() { + this.sender = new Slf4jEventSender(); + } + + @Override + protected void initializeProvider(InterceptorProvider provider, Bus bus) { + LoggingInInterceptor in = new LoggingInInterceptor(sender); + in.setLimit(limit); + in.setInMemThreshold(inMemThreshold); + LoggingOutInterceptor out = new LoggingOutInterceptor(sender); + out.setLimit(limit); + out.setInMemThreshold(inMemThreshold); + + WireTapIn wireTapIn = new WireTapIn(inMemThreshold, limit); + provider.getInInterceptors().add(wireTapIn); + provider.getInInterceptors().add(in); + provider.getInFaultInterceptors().add(in); + + provider.getOutInterceptors().add(out); + provider.getOutFaultInterceptors().add(out); + } + + public void setLimit(int lim) { + limit = lim; + } + + public void setInMemThreshold(long inMemThreshold) { + this.inMemThreshold = inMemThreshold; + } + + public void setSender(LogEventSender sender) { + this.sender = sender; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/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 new file mode 100644 index 0000000..7388eef --- /dev/null +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingInInterceptor.java @@ -0,0 +1,87 @@ +/** + * 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 java.io.IOException; + +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.LogEvent; +import org.apache.cxf.ext.logging.event.LogEventSender; +import org.apache.cxf.interceptor.Fault; +import org.apache.cxf.io.CachedOutputStream; +import org.apache.cxf.io.CachedWriter; +import org.apache.cxf.message.Message; +import org.apache.cxf.phase.Phase; + +/** + * + */ +@NoJSR250Annotations +public class LoggingInInterceptor extends AbstractLoggingInterceptor { + + public LoggingInInterceptor(LogEventSender sender) { + super(Phase.PRE_INVOKE, sender); + } + + public void handleMessage(Message message) throws Fault { + createExchangeId(message); + final LogEvent event = new DefaultLogEventMapper().map(message); + try { + CachedOutputStream cos = message.getContent(CachedOutputStream.class); + if (cos != null) { + handleOutputStream(event, message, cos); + } else { + CachedWriter writer = message.getContent(CachedWriter.class); + if (writer != null) { + handleWriter(event, writer); + } + } + } catch (IOException e) { + throw new Fault(e); + } + + sender.send(event); + } + + private void handleOutputStream(final LogEvent event, Message message, CachedOutputStream cos) throws IOException { + String encoding = (String)message.get(Message.ENCODING); + if (StringUtils.isEmpty(encoding)) { + encoding = "UTF-8"; + } + 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/28e8f04a/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 new file mode 100644 index 0000000..e17ef43 --- /dev/null +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/LoggingOutInterceptor.java @@ -0,0 +1,198 @@ +/** + * 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 java.io.FilterWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringWriter; +import java.io.Writer; + +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.LogEvent; +import org.apache.cxf.ext.logging.event.LogEventSender; +import org.apache.cxf.interceptor.Fault; +import org.apache.cxf.interceptor.StaxOutInterceptor; +import org.apache.cxf.io.CacheAndWriteOutputStream; +import org.apache.cxf.io.CachedOutputStream; +import org.apache.cxf.io.CachedOutputStreamCallback; +import org.apache.cxf.message.Message; +import org.apache.cxf.phase.Phase; + +/** + * + */ +@NoJSR250Annotations +public class LoggingOutInterceptor extends AbstractLoggingInterceptor { + + public LoggingOutInterceptor(LogEventSender sender) { + super(Phase.PRE_STREAM, sender); + addBefore(StaxOutInterceptor.class.getName()); + } + + public void handleMessage(Message message) throws Fault { + createExchangeId(message); + final OutputStream os = message.getContent(OutputStream.class); + if (os != null) { + LoggingCallback callback = new LoggingCallback(sender, message, os, limit); + message.setContent(OutputStream.class, createCachingOut(message, os, callback)); + } else { + final Writer iowriter = message.getContent(Writer.class); + if (iowriter != null) { + message.setContent(Writer.class, new LogEventSendingWriter(sender, message, iowriter, limit)); + } + } + } + + private OutputStream createCachingOut(Message message, final OutputStream os, CachedOutputStreamCallback callback) { + final CacheAndWriteOutputStream newOut = new CacheAndWriteOutputStream(os); + if (threshold > 0) { + newOut.setThreshold(threshold); + } + if (limit > 0) { + newOut.setCacheLimit(limit); + } + newOut.registerCallback(callback); + return newOut; + } + + private static class LogEventSendingWriter extends FilterWriter { + StringWriter out2; + int count; + Message message; + final int lim; + private LogEventSender sender; + + public LogEventSendingWriter(LogEventSender sender, Message message, Writer writer, int limit) { + super(writer); + this.sender = sender; + this.message = message; + if (!(writer instanceof StringWriter)) { + out2 = new StringWriter(); + } + lim = limit == -1 ? Integer.MAX_VALUE : limit; + } + + public void write(int c) throws IOException { + super.write(c); + if (out2 != null && count < lim) { + out2.write(c); + } + count++; + } + + public void write(char[] cbuf, int off, int len) throws IOException { + super.write(cbuf, off, len); + if (out2 != null && count < lim) { + out2.write(cbuf, off, len); + } + count += len; + } + + public void write(String str, int off, int len) throws IOException { + super.write(str, off, len); + if (out2 != null && count < lim) { + out2.write(str, off, len); + } + count += len; + } + + public void close() throws IOException { + final LogEvent event = new DefaultLogEventMapper().map(message); + StringWriter w2 = out2; + if (w2 == null) { + w2 = (StringWriter)out; + } + String ct = (String)message.get(Message.CONTENT_TYPE); + StringBuilder payload = new StringBuilder(); + try { + writePayload(payload, w2, ct); + } catch (Exception ex) { + // ignore + } + event.setPayload(payload.toString()); + sender.send(event); + message.setContent(Writer.class, out); + super.close(); + } + + protected void writePayload(StringBuilder builder, StringWriter stringWriter, String contentType) + throws Exception { + StringBuffer buffer = stringWriter.getBuffer(); + if (buffer.length() > lim) { + builder.append(buffer.subSequence(0, lim)); + } else { + builder.append(buffer); + } + } + } + + public static class LoggingCallback implements CachedOutputStreamCallback { + + private final Message message; + private final OutputStream origStream; + private final int lim; + private LogEventSender sender; + + public LoggingCallback(final LogEventSender sender, final Message msg, final OutputStream os, int limit) { + this.sender = sender; + this.message = msg; + this.origStream = os; + this.lim = limit == -1 ? Integer.MAX_VALUE : limit; + } + + 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 + } + + sender.send(event); + try { + // empty out the cache + cos.lockOutputStream(); + cos.resetOut(null, false); + } catch (Exception ex) { + // ignore + } + message.setContent(OutputStream.class, origStream); + } + + protected void writePayload(StringBuilder builder, CachedOutputStream cos, String encoding, + String contentType) throws Exception { + if (StringUtils.isEmpty(encoding)) { + cos.writeCacheTo(builder, lim); + } else { + cos.writeCacheTo(builder, encoding, lim); + } + } + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/WireTapIn.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..ae7ee66 --- /dev/null +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/WireTapIn.java @@ -0,0 +1,100 @@ +/** + * 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 java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.SequenceInputStream; + +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; + +public class WireTapIn extends AbstractPhaseInterceptor { + private long threshold; + private int limit; + + /** + * Instantiates a new WireTapIn + * @param limit + * + * @param logMessageContent the log message content + */ + public WireTapIn(long threshold, int limit) { + super(Phase.RECEIVE); + this.threshold = threshold; + this.limit = limit; + } + + @Override + public void handleMessage(final Message message) throws Fault { + try { + InputStream is = message.getContent(InputStream.class); + if (is != null) { + handleInputStream(message, is); + } else { + Reader reader = message.getContent(Reader.class); + if (reader != null) { + handleReader(message, reader); + } + } + } catch (Exception e) { + throw new Fault(e); + } + + } + + private void handleReader(Message message, Reader reader) throws IOException { + CachedWriter writer = new CachedWriter(); + IOUtils.copyAndCloseInput(reader, writer); + message.setContent(Reader.class, writer.getReader()); + message.setContent(CachedWriter.class, writer); + } + + private void handleInputStream(Message message, InputStream is) throws IOException { + CachedOutputStream bos = new CachedOutputStream(); + if (threshold > 0) { + bos.setThreshold(threshold); + } + // 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 { + message.setContent(InputStream.class, bis); + } + message.setContent(CachedOutputStream.class, bos); + + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/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 new file mode 100644 index 0000000..5a3da5f --- /dev/null +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/DefaultLogEventMapper.java @@ -0,0 +1,336 @@ +/** + * 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.security.AccessController; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import javax.security.auth.Subject; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamReader; + +import org.apache.cxf.binding.soap.SoapBinding; +import org.apache.cxf.configuration.security.AuthorizationPolicy; +import org.apache.cxf.endpoint.Endpoint; +import org.apache.cxf.helpers.CastUtils; +import org.apache.cxf.message.Message; +import org.apache.cxf.message.MessageUtils; +import org.apache.cxf.security.SecurityContext; +import org.apache.cxf.service.model.BindingOperationInfo; +import org.apache.cxf.service.model.EndpointInfo; +import org.apache.cxf.service.model.InterfaceInfo; +import org.apache.cxf.service.model.ServiceInfo; +import org.apache.cxf.service.model.ServiceModelUtil; +import org.apache.cxf.ws.addressing.AddressingProperties; +import org.apache.cxf.ws.addressing.ContextUtils; + +public class DefaultLogEventMapper implements LogEventMapper { + private static final Set BINARY_CONTENT_MEDIA_TYPES; + static { + BINARY_CONTENT_MEDIA_TYPES = new HashSet(); + BINARY_CONTENT_MEDIA_TYPES.add("application/octet-stream"); + BINARY_CONTENT_MEDIA_TYPES.add("image/png"); + BINARY_CONTENT_MEDIA_TYPES.add("image/jpeg"); + BINARY_CONTENT_MEDIA_TYPES.add("image/gif"); + } + + public LogEvent map(Message message) { + final LogEvent event = new LogEvent(); + event.setMessageId(getMessageId(message)); + event.setExchangeId((String)message.getExchange().get(LogEvent.KEY_EXCHANGE_ID)); + event.setType(getEventType(message)); + if (!Boolean.TRUE.equals(message.get(Message.DECOUPLED_CHANNEL_MESSAGE))) { + // avoid logging the default responseCode 200 for the decoupled responses + Integer responseCode = (Integer)message.get(Message.RESPONSE_CODE); + if (responseCode != null) { + event.setResponseCode(responseCode.toString()); + } + } + + String encoding = (String)message.get(Message.ENCODING); + + if (encoding != null) { + event.setEncoding(encoding); + } + String httpMethod = (String)message.get(Message.HTTP_REQUEST_METHOD); + if (httpMethod != null) { + event.setHttpMethod(httpMethod); + } + String ct = (String)message.get(Message.CONTENT_TYPE); + if (ct != null) { + event.setContentType(ct); + } + Map headerMap = getHeaders(message); + event.setHeaders(headerMap); + + String uri = getUri(message); + if (uri != null) { + event.setAddress(uri); + } + + event.setPrincipal(getPrincipal(message)); + event.setBinaryContent(isBinaryContent(message)); + setEpInfo(message, event); + return event; + } + + private String getPrincipal(Message message) { + String principal = getJAASPrincipal(); + if (principal != null) { + return principal; + } + SecurityContext sc = message.get(SecurityContext.class); + if (sc != null && sc.getUserPrincipal() != null) { + return sc.getUserPrincipal().getName(); + } + + AuthorizationPolicy authPolicy = message.get(AuthorizationPolicy.class); + if (authPolicy != null) { + return authPolicy.getUserName(); + } + return null; + } + + private String getJAASPrincipal() { + StringBuilder principals = new StringBuilder(); + Iterator principalIt = getJAASPrincipals(); + while (principalIt.hasNext()) { + principals.append(principalIt.next()); + if (principalIt.hasNext()) { + principals.append(","); + } + } + return principals.toString(); + } + + private Iterator getJAASPrincipals() { + Subject subject = Subject.getSubject(AccessController.getContext()); + return subject != null && subject.getPrincipals() != null + ? subject.getPrincipals().iterator() : Collections.emptyIterator(); + } + + private Map getHeaders(Message message) { + Map> headers = CastUtils.cast((Map)message.get(Message.PROTOCOL_HEADERS)); + Map result = new HashMap<>(); + for (String key : headers.keySet()) { + List value = headers.get(key); + if (value.size() == 1) { + result.put(key, value.iterator().next()); + } else { + String[] valueAr = value.toArray(new String[] {}); + result.put(key, valueAr.toString()); + } + } + return result; + } + + private String getUri(Message message) { + String uri = (String)message.get(Message.REQUEST_URL); + if (uri == null) { + String address = (String)message.get(Message.ENDPOINT_ADDRESS); + uri = (String)message.get(Message.REQUEST_URI); + if (uri != null && uri.startsWith("/")) { + if (address != null && !address.startsWith(uri)) { + if (address.endsWith("/") && address.length() > 1) { + address = address.substring(0, address.length()); + } + uri = address + uri; + } + } else { + uri = address; + } + } + String query = (String)message.get(Message.QUERY_STRING); + if (query != null) { + return uri + "?" + query; + } else { + return uri; + } + } + + private boolean isBinaryContent(Message message) { + String contentType = (String)message.get(Message.CONTENT_TYPE); + return contentType != null && BINARY_CONTENT_MEDIA_TYPES.contains(contentType); + } + + /** + * check if a Message is a Rest Message + * + * @param message + * @return + */ + private boolean isSOAPMessage(Message message) { + return message.getExchange().getBinding() instanceof SoapBinding; + } + + /** + * Get MessageId from WS Addressing properties + * + * @param message + * @return message id + */ + private String getMessageId(Message message) { + AddressingProperties addrProp = ContextUtils.retrieveMAPs(message, false, + MessageUtils.isOutbound(message), false); + return (addrProp != null) ? addrProp.getMessageID().getValue() : UUID.randomUUID().toString(); + } + + private String getOperationName(Message message) { + String operationName = null; + BindingOperationInfo boi = null; + + boi = message.getExchange().getBindingOperationInfo(); + if (null == boi) { + boi = getOperationFromContent(message); + } + + if (null == boi) { + Message inMsg = message.getExchange().getInMessage(); + if (null != inMsg) { + Message reqMsg = inMsg.getExchange().getInMessage(); + if (null != reqMsg) { + boi = getOperationFromContent(reqMsg); + } + } + } + + if (null != boi) { + operationName = boi.getName().toString(); + } + + return operationName; + } + + private BindingOperationInfo getOperationFromContent(Message message) { + BindingOperationInfo boi = null; + XMLStreamReader xmlReader = message.getContent(XMLStreamReader.class); + if (null != xmlReader) { + QName qName = xmlReader.getName(); + boi = ServiceModelUtil.getOperation(message.getExchange(), qName); + } + return boi; + } + + private Message getEffectiveMessage(Message message) { + boolean isRequestor = MessageUtils.isRequestor(message); + boolean isOutbound = MessageUtils.isOutbound(message); + if (isRequestor) { + return isOutbound ? message : message.getExchange().getOutMessage(); + } else { + return isOutbound ? message.getExchange().getInMessage() : message; + } + } + + private String getRestOperationName(Message curMessage) { + Message message = getEffectiveMessage(curMessage); + if (message.containsKey(Message.HTTP_REQUEST_METHOD)) { + String httpMethod = message.get(Message.HTTP_REQUEST_METHOD).toString(); + + String path = ""; + if (message.containsKey(Message.REQUEST_URI)) { + String requestUri = message.get(Message.REQUEST_URI).toString(); + int baseUriLength = (message.containsKey(Message.BASE_PATH)) ? message.get(Message.BASE_PATH) + .toString().length() : 0; + path = requestUri.substring(baseUriLength); + if (path.isEmpty()) { + path = "/"; + } + } + + return new StringBuffer().append(httpMethod).append('[').append(path).append(']').toString(); + } + return ""; + } + + /** + * Gets the event type from message. + * + * @param message the message + * @return the event type + */ + private EventType getEventType(Message message) { + boolean isRequestor = MessageUtils.isRequestor(message); + boolean isFault = MessageUtils.isFault(message); + if (!isFault) { + isFault = !isSOAPMessage(message) && isRESTFault(message); + } + boolean isOutbound = MessageUtils.isOutbound(message); + if (isOutbound) { + if (isFault) { + return EventType.FAULT_OUT; + } else { + return isRequestor ? EventType.REQ_OUT : EventType.RESP_OUT; + } + } else { + if (isFault) { + return EventType.FAULT_IN; + } else { + return isRequestor ? EventType.RESP_IN : EventType.REQ_IN; + } + } + } + + /** + * For REST we also consider a response to be a fault if the operation is not found or the response code + * is an error + * + * @param message + * @return + */ + private boolean isRESTFault(Message message) { + Object opName = message.getExchange().get("org.apache.cxf.resource.operation.name"); + if (opName == null) { + return true; + } else { + Integer responseCode = (Integer)message.get(Message.RESPONSE_CODE); + return (responseCode != null) && (responseCode >= 400); + } + } + + private void setEpInfo(Message message, final LogEvent event) { + EndpointInfo endpoint = getEPInfo(message); + event.setPortName(endpoint.getName()); + event.setPortTypeName(endpoint.getName()); + event.setOperationName(getRestOperationName(message)); + String opName = isSOAPMessage(message) ? getOperationName(message) : getRestOperationName(message); + event.setOperationName(opName); + if (endpoint.getService() != null) { + setServiceInfo(endpoint.getService(), event); + } + } + + private void setServiceInfo(ServiceInfo service, LogEvent event) { + event.setServiceName(service.getName()); + InterfaceInfo iface = service.getInterface(); + event.setPortTypeName(iface.getName()); + } + + private EndpointInfo getEPInfo(Message message) { + Endpoint ep = message.getExchange().getEndpoint(); + return (ep == null) ? null : ep.getEndpointInfo(); + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/EventType.java ---------------------------------------------------------------------- diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/EventType.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/EventType.java new file mode 100644 index 0000000..56e2609 --- /dev/null +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/EventType.java @@ -0,0 +1,28 @@ +/** + * 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; + +public enum EventType { + REQ_IN, + REQ_OUT, + RESP_IN, + RESP_OUT, + FAULT_IN, + FAULT_OUT +} http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/LogEvent.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..92a4a69 --- /dev/null +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/LogEvent.java @@ -0,0 +1,196 @@ +/** + * 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.File; +import java.util.Map; + +import javax.xml.namespace.QName; + +public final class LogEvent { + public static final String KEY_EXCHANGE_ID = "exchangeId"; + private String messageId; + private String exchangeId; + private EventType type; + private String address; + private String contentType; + private String encoding; + private String httpMethod; + private String responseCode; + private String principal; + private QName serviceName; // Only for SOAP + private QName portName; + private QName portTypeName; + private String operationName; + private Map headers; + private boolean binaryContent; + private String payload; + private boolean truncated; + private File fullContentFile; + + + public LogEvent() { + } + + public String getMessageId() { + return messageId; + } + + public void setMessageId(String id) { + this.messageId = id; + } + + public String getExchangeId() { + return exchangeId; + } + + public void setExchangeId(String exchangeId) { + this.exchangeId = exchangeId; + } + + public EventType getType() { + return type; + } + + public void setType(EventType type) { + this.type = type; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public String getEncoding() { + return encoding; + } + + public void setEncoding(String encoding) { + this.encoding = encoding; + } + + public String getHttpMethod() { + return httpMethod; + } + + public void setHttpMethod(String httpMethod) { + this.httpMethod = httpMethod; + } + + public String getResponseCode() { + return responseCode; + } + + public void setResponseCode(String responseCode) { + this.responseCode = responseCode; + } + + public String getPrincipal() { + return principal; + } + + public void setPrincipal(String principal) { + this.principal = principal; + } + + public QName getServiceName() { + return serviceName; + } + + public void setServiceName(QName serviceName) { + this.serviceName = serviceName; + } + + public QName getPortName() { + return portName; + } + + public void setPortName(QName portName) { + this.portName = portName; + } + + public QName getPortTypeName() { + return portTypeName; + } + + public void setPortTypeName(QName portTypeName) { + this.portTypeName = portTypeName; + } + + public String getOperationName() { + return operationName; + } + + public void setOperationName(String operationName) { + this.operationName = operationName; + } + + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public boolean isBinaryContent() { + return binaryContent; + } + + public void setBinaryContent(boolean binaryContent) { + this.binaryContent = binaryContent; + } + + public String getPayload() { + return payload; + } + + public void setPayload(String payload) { + this.payload = payload; + } + + public boolean isTruncated() { + return truncated; + } + + public void setTruncated(boolean truncated) { + this.truncated = truncated; + } + + public File getFullContentFile() { + return fullContentFile; + } + + public void setFullContentFile(File fullContentFile) { + this.fullContentFile = fullContentFile; + } + + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/LogEventMapper.java ---------------------------------------------------------------------- diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/LogEventMapper.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/LogEventMapper.java new file mode 100644 index 0000000..c187e3c --- /dev/null +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/LogEventMapper.java @@ -0,0 +1,25 @@ +/** + * 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 org.apache.cxf.message.Message; + +public interface LogEventMapper { + LogEvent map(Message message); +} http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/LogEventSender.java ---------------------------------------------------------------------- diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/LogEventSender.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/LogEventSender.java new file mode 100644 index 0000000..5152c8c --- /dev/null +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/event/LogEventSender.java @@ -0,0 +1,27 @@ +/** + * 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; + +/** + * Is called by the Logging interceptor to send the fully + * populated message + */ +public interface LogEventSender { + void send(LogEvent event); +} http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/slf4j/Slf4jEventSender.java ---------------------------------------------------------------------- diff --git a/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/slf4j/Slf4jEventSender.java b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/slf4j/Slf4jEventSender.java new file mode 100644 index 0000000..3a6e751 --- /dev/null +++ b/rt/features/logging/src/main/java/org/apache/cxf/ext/logging/slf4j/Slf4jEventSender.java @@ -0,0 +1,79 @@ +/** + * 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.slf4j; + +import java.util.HashSet; +import java.util.Set; + +import javax.xml.namespace.QName; + +import org.apache.cxf.ext.logging.event.LogEvent; +import org.apache.cxf.ext.logging.event.LogEventSender; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +public class Slf4jEventSender implements LogEventSender { + + @Override + public void send(LogEvent event) { + String cat = "org.apache.cxf.services." + event.getPortTypeName() + "." + event.getType(); + Logger log = LoggerFactory.getLogger(cat); + Set keys = new HashSet(); + try { + put(keys, "type", event.getType().toString()); + put(keys, "address", event.getAddress()); + put(keys, "content-type", event.getContentType()); + put(keys, "encoding", event.getEncoding()); + put(keys, "exchangeId", event.getExchangeId()); + put(keys, "httpMethod", event.getHttpMethod()); + put(keys, "messageId", event.getMessageId()); + put(keys, "responseCode", event.getResponseCode()); + put(keys, "serviceName", localPart(event.getServiceName())); + put(keys, "portName", localPart(event.getPortName())); + put(keys, "portTypeName", localPart(event.getPortTypeName())); + if (event.getFullContentFile() != null) { + put(keys, "fullContentFile", event.getFullContentFile().getAbsolutePath()); + } + put(keys, "headers", event.getHeaders().toString()); + log.info(getLogMessage(event)); + } finally { + for (String key : keys) { + MDC.remove(key); + } + } + + } + + private String localPart(QName name) { + return name == null ? null : name.getLocalPart(); + } + + private String getLogMessage(LogEvent event) { + return event.getPayload(); + } + + private void put(Set keys, String key, String value) { + if (value != null) { + MDC.put(key, value); + keys.add(key); + } + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/RESTLoggingTest.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..0ac025b --- /dev/null +++ b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/RESTLoggingTest.java @@ -0,0 +1,135 @@ +/** + * 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 java.io.IOException; +import java.net.MalformedURLException; +import java.util.Collections; +import java.util.List; + +import org.apache.cxf.endpoint.Server; +import org.apache.cxf.ext.logging.event.EventType; +import org.apache.cxf.ext.logging.event.LogEvent; +import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; +import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean; +import org.apache.cxf.jaxrs.client.WebClient; +import org.junit.Assert; +import org.junit.Test; + +public class RESTLoggingTest { + + private static final String SERVICE_URI = "http://localhost:5679/testrest"; + + @Test + public void testSlf4j() throws IOException { + LoggingFeature loggingFeature = new LoggingFeature(); + Server server = createService(loggingFeature); + server.start(); + WebClient client = createClient(loggingFeature); + String result = client.get(String.class); + Assert.assertEquals("test1", result); + server.destroy(); + } + + private WebClient createClient(LoggingFeature loggingFeature) { + JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean(); + bean.setAddress(SERVICE_URI + "/test1"); + bean.setFeatures(Collections.singletonList(loggingFeature)); + return bean.createWebClient(); + } + + private Server createService(LoggingFeature loggingFeature) { + JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean(); + factory.setAddress(SERVICE_URI); + factory.setFeatures(Collections.singletonList(loggingFeature)); + factory.setServiceBean(new TestServiceRest()); + return factory.create(); + } + + @Test + public void testEvents() throws MalformedURLException { + LoggingFeature loggingFeature = new LoggingFeature(); + TestEventSender sender = new TestEventSender(); + loggingFeature.setSender(sender); + Server server = createService(loggingFeature); + server.start(); + WebClient client = createClient(loggingFeature); + String result = client.get(String.class); + Assert.assertEquals("test1", result); + server.destroy(); + List events = sender.getEvents(); + Assert.assertEquals(4, events.size()); + checkRequestOut(events.get(0)); + checkRequestIn(events.get(1)); + checkResponseOut(events.get(2)); + checkResponseIn(events.get(3)); + } + + private void checkRequestOut(LogEvent requestOut) { + Assert.assertEquals(SERVICE_URI + "/test1", requestOut.getAddress()); + Assert.assertNull(requestOut.getContentType()); + Assert.assertEquals(EventType.REQ_OUT, requestOut.getType()); + Assert.assertNull(requestOut.getEncoding()); + Assert.assertNotNull(requestOut.getExchangeId()); + Assert.assertEquals("GET", requestOut.getHttpMethod()); + Assert.assertNotNull(requestOut.getMessageId()); + Assert.assertEquals("", requestOut.getPayload()); + } + + private void checkRequestIn(LogEvent requestIn) { + Assert.assertEquals(SERVICE_URI + "/test1", requestIn.getAddress()); + Assert.assertEquals("*/*", requestIn.getContentType()); + Assert.assertEquals(EventType.REQ_IN, requestIn.getType()); + Assert.assertNull(requestIn.getEncoding()); + Assert.assertNotNull(requestIn.getExchangeId()); + Assert.assertEquals("GET", requestIn.getHttpMethod()); + Assert.assertNotNull(requestIn.getMessageId()); + Assert.assertEquals("", requestIn.getPayload()); + } + + private void checkResponseOut(LogEvent responseOut) { + // Not yet available + Assert.assertNull(responseOut.getAddress()); + Assert.assertEquals("application/octet-stream", responseOut.getContentType()); + Assert.assertEquals(EventType.RESP_OUT, responseOut.getType()); + Assert.assertNull(responseOut.getEncoding()); + Assert.assertNotNull(responseOut.getExchangeId()); + + // Not yet available + Assert.assertNull(responseOut.getHttpMethod()); + Assert.assertNotNull(responseOut.getMessageId()); + Assert.assertEquals("test1", responseOut.getPayload()); + } + + private void checkResponseIn(LogEvent responseIn) { + // Not yet available + Assert.assertNull(responseIn.getAddress()); + Assert.assertEquals("application/octet-stream", responseIn.getContentType()); + Assert.assertEquals(EventType.RESP_IN, responseIn.getType()); + Assert.assertEquals("ISO-8859-1", responseIn.getEncoding()); + Assert.assertNotNull(responseIn.getExchangeId()); + + // Not yet available + Assert.assertNull(responseIn.getHttpMethod()); + Assert.assertNotNull(responseIn.getMessageId()); + Assert.assertEquals("test1", responseIn.getPayload()); + } + + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/SOAPLoggingTest.java ---------------------------------------------------------------------- diff --git a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/SOAPLoggingTest.java b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/SOAPLoggingTest.java new file mode 100644 index 0000000..79033da --- /dev/null +++ b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/SOAPLoggingTest.java @@ -0,0 +1,144 @@ +/** + * 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 java.net.MalformedURLException; +import java.util.Collections; +import java.util.List; + +import javax.jws.WebService; +import javax.xml.ws.Endpoint; + +import org.apache.cxf.ext.logging.event.EventType; +import org.apache.cxf.ext.logging.event.LogEvent; +import org.apache.cxf.feature.Feature; +import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; +import org.junit.Assert; +import org.junit.Test; + +public class SOAPLoggingTest { + + private static final String SERVICE_URI = "http://localhost:5678/test"; + + @WebService(endpointInterface = "org.apache.cxf.ext.logging.TestService") + public final class TestServiceImplementation implements TestService { + @Override + public String echo(String msg) { + return msg; + } + } + + @Test + public void testSlf4j() throws MalformedURLException { + TestService serviceImpl = new TestServiceImplementation(); + LoggingFeature loggingFeature = new LoggingFeature(); + Endpoint ep = Endpoint.publish(SERVICE_URI, serviceImpl, loggingFeature); + TestService client = createTestClient(loggingFeature); + client.echo("test"); + ep.stop(); + } + + @Test + public void testEvents() throws MalformedURLException { + TestService serviceImpl = new TestServiceImplementation(); + LoggingFeature loggingFeature = new LoggingFeature(); + TestEventSender sender = new TestEventSender(); + loggingFeature.setSender(sender); + Endpoint ep = Endpoint.publish(SERVICE_URI, serviceImpl, loggingFeature); + TestService client = createTestClient(loggingFeature); + client.echo("test"); + ep.stop(); + + List events = sender.getEvents(); + Assert.assertEquals(4, events.size()); + checkRequestOut(events.get(0)); + checkRequestIn(events.get(1)); + checkResponseOut(events.get(2)); + checkResponseIn(events.get(3)); + } + + private void checkRequestOut(LogEvent requestOut) { + Assert.assertEquals(SERVICE_URI, requestOut.getAddress()); + Assert.assertEquals("text/xml", requestOut.getContentType()); + Assert.assertEquals(EventType.REQ_OUT, requestOut.getType()); + Assert.assertEquals("UTF-8", requestOut.getEncoding()); + Assert.assertNotNull(requestOut.getExchangeId()); + Assert.assertEquals("POST", requestOut.getHttpMethod()); + Assert.assertNotNull(requestOut.getMessageId()); + Assert.assertTrue(requestOut.getPayload().contains("test")); + Assert.assertEquals("TestServicePort", requestOut.getPortName().getLocalPart()); + Assert.assertEquals("TestService", requestOut.getPortTypeName().getLocalPart()); + Assert.assertEquals("TestServiceService", requestOut.getServiceName().getLocalPart()); + } + + private void checkRequestIn(LogEvent requestIn) { + Assert.assertEquals(SERVICE_URI, requestIn.getAddress()); + Assert.assertEquals("text/xml; charset=UTF-8", requestIn.getContentType()); + Assert.assertEquals(EventType.REQ_IN, requestIn.getType()); + Assert.assertEquals("UTF-8", requestIn.getEncoding()); + Assert.assertNotNull(requestIn.getExchangeId()); + Assert.assertEquals("POST", requestIn.getHttpMethod()); + Assert.assertNotNull(requestIn.getMessageId()); + Assert.assertTrue(requestIn.getPayload().contains("test")); + Assert.assertEquals("TestServiceImplementationPort", requestIn.getPortName().getLocalPart()); + Assert.assertEquals("TestService", requestIn.getPortTypeName().getLocalPart()); + Assert.assertEquals("TestServiceImplementationService", requestIn.getServiceName().getLocalPart()); + } + + private void checkResponseOut(LogEvent responseOut) { + // Not yet available + Assert.assertNull(responseOut.getAddress()); + Assert.assertEquals("text/xml", responseOut.getContentType()); + Assert.assertEquals(EventType.RESP_OUT, responseOut.getType()); + Assert.assertEquals("UTF-8", responseOut.getEncoding()); + Assert.assertNotNull(responseOut.getExchangeId()); + + // Not yet available + Assert.assertNull(responseOut.getHttpMethod()); + Assert.assertNotNull(responseOut.getMessageId()); + Assert.assertTrue(responseOut.getPayload().contains("test")); + Assert.assertEquals("TestServiceImplementationPort", responseOut.getPortName().getLocalPart()); + Assert.assertEquals("TestService", responseOut.getPortTypeName().getLocalPart()); + Assert.assertEquals("TestServiceImplementationService", responseOut.getServiceName().getLocalPart()); + } + + private void checkResponseIn(LogEvent responseIn) { + Assert.assertNull(responseIn.getAddress()); + Assert.assertEquals("text/xml; charset=UTF-8", responseIn.getContentType()); + Assert.assertEquals(EventType.RESP_IN, responseIn.getType()); + Assert.assertEquals("UTF-8", responseIn.getEncoding()); + Assert.assertNotNull(responseIn.getExchangeId()); + + // Not yet available + Assert.assertNull(responseIn.getHttpMethod()); + Assert.assertNotNull(responseIn.getMessageId()); + Assert.assertTrue(responseIn.getPayload().contains("test")); + Assert.assertEquals("TestServicePort", responseIn.getPortName().getLocalPart()); + Assert.assertEquals("TestService", responseIn.getPortTypeName().getLocalPart()); + Assert.assertEquals("TestServiceService", responseIn.getServiceName().getLocalPart()); + } + + private TestService createTestClient(Feature feature) throws MalformedURLException { + JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); + factory.setAddress(SERVICE_URI); + factory.setFeatures(Collections.singletonList(feature)); + return factory.create(TestService.class); + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestEventSender.java ---------------------------------------------------------------------- diff --git a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestEventSender.java b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestEventSender.java new file mode 100644 index 0000000..44118c4 --- /dev/null +++ b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestEventSender.java @@ -0,0 +1,38 @@ +/** + * 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 java.util.ArrayList; +import java.util.List; + +import org.apache.cxf.ext.logging.event.LogEvent; +import org.apache.cxf.ext.logging.event.LogEventSender; + +final class TestEventSender implements LogEventSender { + private final List events = new ArrayList(); + + @Override + public void send(LogEvent event) { + events.add(event); + } + + public List getEvents() { + return events; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestService.java ---------------------------------------------------------------------- diff --git a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestService.java b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestService.java new file mode 100644 index 0000000..ff5cd06 --- /dev/null +++ b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestService.java @@ -0,0 +1,26 @@ +/** + * 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.jws.WebService; + +@WebService +public interface TestService { + String echo(String msg); +} http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestServiceRest.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..f56639d --- /dev/null +++ b/rt/features/logging/src/test/java/org/apache/cxf/ext/logging/TestServiceRest.java @@ -0,0 +1,31 @@ +/** + * 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.PathParam; + +public class TestServiceRest { + @GET + @Path("{msg}") + public String echo(@PathParam("msg") String msg) { + return msg; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/rt/features/logging/src/test/resources/log4j.properties ---------------------------------------------------------------------- diff --git a/rt/features/logging/src/test/resources/log4j.properties b/rt/features/logging/src/test/resources/log4j.properties new file mode 100644 index 0000000..5b083ba --- /dev/null +++ b/rt/features/logging/src/test/resources/log4j.properties @@ -0,0 +1,5 @@ +log4j.rootLogger=INFO,stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p %c - %X{address} - %m%n http://git-wip-us.apache.org/repos/asf/cxf/blob/28e8f04a/rt/pom.xml ---------------------------------------------------------------------- diff --git a/rt/pom.xml b/rt/pom.xml index fe8981f..c2f5c70 100644 --- a/rt/pom.xml +++ b/rt/pom.xml @@ -39,6 +39,7 @@ databinding/jibx bindings features/clustering + features/logging frontend/simple frontend/jaxws frontend/jaxrs