cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cschnei...@apache.org
Subject cxf git commit: [CXF-6286] New logging feature as separate module
Date Wed, 11 Mar 2015 15:18:29 GMT
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 <chris@die-schneider.net>
Authored: Wed Mar 11 16:16:55 2015 +0100
Committer: Christian Schneider <chris@die-schneider.net>
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 @@
+<project
+    xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.cxf</groupId>
+        <artifactId>cxf-parent</artifactId>
+        <version>3.1.0-SNAPSHOT</version>
+        <relativePath>../../../parent/pom.xml</relativePath>
+    </parent>
+    <artifactId>logging</artifactId>
+    <name>logging</name>
+    <groupId>org.apache.cxf.ext</groupId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-bindings-soap</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <version>1.7.7</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.easymock</groupId>
+            <artifactId>easymock</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-frontend-jaxws</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-rs-client</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-transports-http-jetty</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
\ 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<Message> {
+    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.
+ * <pre>
+ * <![CDATA[
+    <jaxws:endpoint ...>
+      <jaxws:features>
+       <bean class="org.apache.cxf.ext.logging.LoggingFeature"/>
+      </jaxws:features>
+    </jaxws:endpoint>
+  ]]>
+  </pre>
+ */
+@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<Message> {
+    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<String> BINARY_CONTENT_MEDIA_TYPES;
+    static {
+        BINARY_CONTENT_MEDIA_TYPES = new HashSet<String>();
+        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<String, String> 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<? extends Object> principalIt = getJAASPrincipals();
+        while (principalIt.hasNext()) {
+            principals.append(principalIt.next());
+            if (principalIt.hasNext()) {
+                principals.append(",");
+            }
+        }
+        return principals.toString();
+    }
+
+    private Iterator<? extends Object> getJAASPrincipals() {
+        Subject subject = Subject.getSubject(AccessController.getContext());
+        return subject != null && subject.getPrincipals() != null
+            ? subject.getPrincipals().iterator() : Collections.emptyIterator();
+    }
+
+    private Map<String, String> getHeaders(Message message) {
+        Map<String, List<String>> headers = CastUtils.cast((Map<?, ?>)message.get(Message.PROTOCOL_HEADERS));
+        Map<String, String> result = new HashMap<>();
+        for (String key : headers.keySet()) {
+            List<String> 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<String, String> 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<String, String> getHeaders() {
+        return headers;
+    }
+
+    public void setHeaders(Map<String, String> 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<String> keys = new HashSet<String>(); 
+        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<String> 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<LogEvent> 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<LogEvent> 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("<arg0>test</arg0>"));
+        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("<arg0>test</arg0>"));
+        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("<return>test</return>"));
+        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("<return>test</return>"));
+        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<LogEvent> events = new ArrayList<LogEvent>();
+
+    @Override
+    public void send(LogEvent event) {
+        events.add(event);
+    }
+    
+    public List<LogEvent> 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 @@
         <module>databinding/jibx</module>
         <module>bindings</module>
         <module>features/clustering</module>
+        <module>features/logging</module>
         <module>frontend/simple</module>
         <module>frontend/jaxws</module>
         <module>frontend/jaxrs</module>


Mime
View raw message