
Author: ceki Date: Thu Jan 18 17:05:40 2007 New Revision: 1251 Added: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/pattern/FullResponseConverter.java logback/trunk/logback-access/src/main/java/ch/qos/logback/access/pattern/ResponseContentConverter.java Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/Constants.java logback/trunk/logback-access/src/main/java/ch/qos/logback/access/PatternLayout.java logback/trunk/logback-access/src/main/java/ch/qos/logback/access/jetty/JettyServerAdapter.java logback/trunk/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeFilter.java logback/trunk/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeHttpServletResponse.java logback/trunk/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeServletOutputStream.java logback/trunk/logback-access/src/main/java/ch/qos/logback/access/spi/AccessEvent.java logback/trunk/logback-access/src/main/java/ch/qos/logback/access/spi/ServerAdapter.java logback/trunk/logback-access/src/main/java/ch/qos/logback/access/tomcat/TomcatServerAdapter.java logback/trunk/logback-access/src/test/java/ch/qos/logback/access/pattern/helpers/DummyServerAdapter.java logback/trunk/logback-access/src/test/java/ch/qos/logback/access/pattern/helpers/DummyValuesAdapter.java Log: Added support for the http response, including contents. Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/Constants.java ============================================================================== --- logback/trunk/logback-access/src/main/java/ch/qos/logback/access/Constants.java (original) +++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/Constants.java Thu Jan 18 17:05:40 2007 @@ -1,7 +1,6 @@ package ch.qos.logback.access; public class Constants { - public static final String LB_INPUT_BUFFER = "LB_INPUT_BUFFER"; - + public static final String LB_OUTPUT_BUFFER = "LB_OUTPUT_BUFFER"; } Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/PatternLayout.java ============================================================================== --- logback/trunk/logback-access/src/main/java/ch/qos/logback/access/PatternLayout.java (original) +++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/PatternLayout.java Thu Jan 18 17:05:40 2007 @@ -15,6 +15,7 @@ import ch.qos.logback.access.pattern.ContentLengthConverter; import ch.qos.logback.access.pattern.DateConverter; import ch.qos.logback.access.pattern.FullRequestConverter; +import ch.qos.logback.access.pattern.FullResponseConverter; import ch.qos.logback.access.pattern.LineSeparatorConverter; import ch.qos.logback.access.pattern.LocalIPAddressConverter; import ch.qos.logback.access.pattern.LocalPortConverter; @@ -31,6 +32,7 @@ import ch.qos.logback.access.pattern.RequestProtocolConverter; import ch.qos.logback.access.pattern.RequestURIConverter; import ch.qos.logback.access.pattern.RequestURLConverter; +import ch.qos.logback.access.pattern.ResponseContentConverter; import ch.qos.logback.access.pattern.ResponseHeaderConverter; import ch.qos.logback.access.pattern.ServerNameConverter; import ch.qos.logback.access.pattern.StatusCodeConverter; @@ -135,7 +137,10 @@ defaultConverterMap.put("requestContent", RequestContentConverter.class.getName()); + defaultConverterMap.put("responseContent", ResponseContentConverter.class.getName()); + defaultConverterMap.put("fullRequest", FullRequestConverter.class.getName()); + defaultConverterMap.put("fullResponse", FullResponseConverter.class.getName()); defaultConverterMap.put("n", LineSeparatorConverter.class.getName()); Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/jetty/JettyServerAdapter.java ============================================================================== --- logback/trunk/logback-access/src/main/java/ch/qos/logback/access/jetty/JettyServerAdapter.java (original) +++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/jetty/JettyServerAdapter.java Thu Jan 18 17:05:40 2007 @@ -1,5 +1,10 @@ package ch.qos.logback.access.jetty; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +import org.mortbay.jetty.HttpFields; import org.mortbay.jetty.Request; import org.mortbay.jetty.Response; @@ -7,11 +12,11 @@ /** * A jetty specific implementation of the {@link ServerAdapter} interface. - * + * * @author Sébastien Pennec */ public class JettyServerAdapter implements ServerAdapter { - + Request request; Response response; @@ -19,7 +24,7 @@ this.request = jettyRequest; this.response = jettyResponse; } - + public long getContentLength() { return response.getContentCount(); } @@ -27,8 +32,18 @@ public int getStatusCode() { return response.getStatus(); } - + public String getResponseHeader(String key) { return response.getHeader(key); } + + public List<String> getResponseHeaderNameList() { + HttpFields httpFields = response.getHttpFields(); + List<String> hnList = new ArrayList<String>(); + Enumeration e = httpFields.getFieldNames(); + while (e.hasMoreElements()) { + hnList.add((String) e.nextElement()); + } + return hnList; + } } Added: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/pattern/FullResponseConverter.java ============================================================================== --- (empty file) +++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/pattern/FullResponseConverter.java Thu Jan 18 17:05:40 2007 @@ -0,0 +1,32 @@ +package ch.qos.logback.access.pattern; + +import java.util.List; + +import ch.qos.logback.access.spi.AccessEvent; +import ch.qos.logback.core.Layout; + +public class FullResponseConverter extends AccessConverter { + + @Override + public String convert(AccessEvent ae) { + StringBuffer buf = new StringBuffer(); + + buf.append("HTTP/1.1 "); + buf.append(ae.getStatusCode()); + buf.append(" NA"); + buf.append(Layout.LINE_SEP); + + List<String> hnList = ae.getResponseHeaderNameList(); + for(String headerName: hnList) { + buf.append(headerName); + buf.append(": "); + buf.append(ae.getResponseHeader(headerName)); + buf.append(Layout.LINE_SEP); + } + buf.append(Layout.LINE_SEP); + buf.append(ae.getResponseContent()); + buf.append(Layout.LINE_SEP); + return buf.toString(); + } + +} Added: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/pattern/ResponseContentConverter.java ============================================================================== --- (empty file) +++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/pattern/ResponseContentConverter.java Thu Jan 18 17:05:40 2007 @@ -0,0 +1,21 @@ +package ch.qos.logback.access.pattern; + +import ch.qos.logback.access.spi.AccessEvent; + +/** + * This class is tied to the <code>requestContent</code> conversion word. + * <p> + * It has been removed from the {@link ch.qos.logback.access.PatternLayout} since + * it needs further testing before wide use. + * <p> + * @author Ceki Gülcü + * @author Sébastien Pennec + */ +public class ResponseContentConverter extends AccessConverter { + + @Override + public String convert(AccessEvent accessEvent) { + return accessEvent.getResponseContent(); + } + +} Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeFilter.java ============================================================================== --- logback/trunk/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeFilter.java (original) +++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeFilter.java Thu Jan 18 17:05:40 2007 @@ -11,6 +11,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import ch.qos.logback.access.Constants; + public class TeeFilter implements Filter { public void destroy() { @@ -19,17 +21,33 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { - - if(request instanceof HttpServletRequest) { - HttpServletRequest httpRequest = (HttpServletRequest) request; - request = new TeeHttpServletRequest(httpRequest); - } - if(response instanceof HttpServletResponse) { - HttpServletResponse httpResponse = (HttpServletResponse) response; - response = new TeeHttpServletResponse(httpResponse); + + if (request instanceof HttpServletRequest) { + try { + TeeHttpServletRequest teeRequest = new TeeHttpServletRequest( + (HttpServletRequest) request); + TeeHttpServletResponse teeResponse = new TeeHttpServletResponse( + (HttpServletResponse) response); + + //System.out.println("BEFORE TeeFilter. filterChain.doFilter()"); + filterChain.doFilter(teeRequest, teeResponse); + //System.out.println("AFTER TeeFilter. filterChain.doFilter()"); + + teeResponse.finish(); + // let the output contents be available for later use by + // logback-access-logging + teeRequest.setAttribute(Constants.LB_OUTPUT_BUFFER, teeResponse + .getOutputBuffer()); + } catch (IOException e) { + e.printStackTrace(); + throw e; + } catch (ServletException e) { + e.printStackTrace(); + throw e; + } + } else { + filterChain.doFilter(request, response); } - - filterChain.doFilter(request, response); } Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeHttpServletResponse.java ============================================================================== --- logback/trunk/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeHttpServletResponse.java (original) +++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeHttpServletResponse.java Thu Jan 18 17:05:40 2007 @@ -1,6 +1,7 @@ package ch.qos.logback.access.servlet; import java.io.IOException; +import java.io.OutputStreamWriter; import java.io.PrintWriter; import javax.servlet.ServletOutputStream; @@ -9,27 +10,46 @@ public class TeeHttpServletResponse extends HttpServletResponseWrapper { - final TeeServletOutputStream teeServletOutputStream; + TeeServletOutputStream teeServletOutputStream; PrintWriter writer; - public TeeHttpServletResponse(HttpServletResponse httpServletResponse) - throws IOException { + public TeeHttpServletResponse(HttpServletResponse httpServletResponse) { super(httpServletResponse); - ServletOutputStream underlyingStream = httpServletResponse - .getOutputStream(); - teeServletOutputStream = new TeeServletOutputStream(underlyingStream); + //System.out.println("TeeHttpServletResponse.constructor called"); } @Override public ServletOutputStream getOutputStream() throws IOException { + //System.out.println("TeeHttpServletResponse.getOutputStream() called"); + if(teeServletOutputStream == null) { + teeServletOutputStream = new TeeServletOutputStream( + this.getResponse()); + } return teeServletOutputStream; } @Override public PrintWriter getWriter() throws IOException { - if (writer == null) - writer = new PrintWriter(getOutputStream()); - return writer; + //System.out.println("TeeHttpServletResponse.getWriter() called"); + if (this.writer == null) { + this.writer = new PrintWriter(new OutputStreamWriter(getOutputStream()), true); + } + return this.writer; } + @Override + public void flushBuffer() { + //System.out.println("TeeHttpServletResponse.flushBuffer() called"); + this.writer.flush(); + } + + byte[] getOutputBuffer() { + return teeServletOutputStream.getOutputBuffer(); + } + + + void finish() throws IOException { + this.writer.close(); + teeServletOutputStream.close(); + } } Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeServletOutputStream.java ============================================================================== --- logback/trunk/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeServletOutputStream.java (original) +++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeServletOutputStream.java Thu Jan 18 17:05:40 2007 @@ -4,44 +4,67 @@ import java.io.IOException; import javax.servlet.ServletOutputStream; +import javax.servlet.ServletResponse; public class TeeServletOutputStream extends ServletOutputStream { final ServletOutputStream underlyingStream; final ByteArrayOutputStream baos; - TeeServletOutputStream(ServletOutputStream underlyingStream) { - this.underlyingStream = underlyingStream; + + TeeServletOutputStream(ServletResponse httpServletResponse) throws IOException { + //System.out.println("TeeServletOutputStream.constructor() called"); + this.underlyingStream = httpServletResponse.getOutputStream(); baos = new ByteArrayOutputStream(); } + byte[] getOutputBuffer() { + return baos.toByteArray(); + } + @Override public void write(int val) throws IOException { + //System.out.println("XXXXXXXXXXXWRITE TeeServletOutputStream.write(int) called"); underlyingStream.write(val); baos.write(val); } @Override public void write(byte[] byteArray) throws IOException { - underlyingStream.write(byteArray); - baos.write(byteArray); + //System.out.println("WRITE TeeServletOutputStream.write(byte[]) called"); + write(byteArray, 0, byteArray.length); } @Override public void write(byte byteArray[], int offset, int length) throws IOException { + //System.out.println("WRITE TeeServletOutputStream.write(byte[], int, int) called"); + //System.out.println(new String(byteArray, offset, length)); underlyingStream.write(byteArray, offset, length); baos.write(byteArray, offset, length); } public void close() throws IOException { + // System.out.println("CLOSE TeeServletOutputStream.close() called"); + + // If the servlet accessing the stream is using a writer instead of + // an OutputStream, it will probably call os.close() begore calling + // writer.close. Thus, the undelying output stream will be called + // before the data sent to the writer could be flushed. + } + + public void finish() throws IOException { + //System.out.println("FINISH TeeServletOutputStream.close() called"); + flush(); underlyingStream.close(); baos.close(); } public void flush() throws IOException { + //System.out.println("FLUSH TeeServletOutputStream.flush() called"); underlyingStream.flush(); baos.flush(); } + } Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/spi/AccessEvent.java ============================================================================== --- logback/trunk/logback-access/src/main/java/ch/qos/logback/access/spi/AccessEvent.java (original) +++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/spi/AccessEvent.java Thu Jan 18 17:05:40 2007 @@ -3,6 +3,7 @@ import java.io.Serializable; import java.util.Enumeration; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.servlet.http.Cookie; @@ -41,7 +42,8 @@ String method; String serverName; String requestContent; - + String responseContent; + Map<String, String> requestHeaderMap; Map<String, Object> requestParameterMap; @@ -247,6 +249,10 @@ return serverAdapter.getResponseHeader(key); } + public List<String> getResponseHeaderNameList() { + return serverAdapter.getResponseHeaderNameList(); + } + /** * Attributes are not serialized * @@ -333,6 +339,23 @@ return requestContent; } + public String getResponseContent() { + if (responseContent != null) { + return responseContent; + } + // retreive the byte array previously placed by TeeFilter + byte[] outputBuffer = (byte[]) httpRequest.getAttribute(Constants.LB_OUTPUT_BUFFER); + + if (outputBuffer != null) { + responseContent = new String(outputBuffer); + } + + if (responseContent == null || responseContent.length() == 0) { + responseContent = EMPTY; + } + + return responseContent; + } public int getLocalPort() { if (localPort == SENTINEL) { if (httpRequest != null) { Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/spi/ServerAdapter.java ============================================================================== --- logback/trunk/logback-access/src/main/java/ch/qos/logback/access/spi/ServerAdapter.java (original) +++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/spi/ServerAdapter.java Thu Jan 18 17:05:40 2007 @@ -1,5 +1,7 @@ package ch.qos.logback.access.spi; +import java.util.List; + /** * An interface to access server-specific methods from * the server-independent AccessEvent. @@ -12,4 +14,6 @@ long getContentLength(); int getStatusCode(); String getResponseHeader(String key); + List<String> getResponseHeaderNameList(); + } Modified: logback/trunk/logback-access/src/main/java/ch/qos/logback/access/tomcat/TomcatServerAdapter.java ============================================================================== --- logback/trunk/logback-access/src/main/java/ch/qos/logback/access/tomcat/TomcatServerAdapter.java (original) +++ logback/trunk/logback-access/src/main/java/ch/qos/logback/access/tomcat/TomcatServerAdapter.java Thu Jan 18 17:05:40 2007 @@ -1,5 +1,8 @@ package ch.qos.logback.access.tomcat; +import java.util.ArrayList; +import java.util.List; + import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; @@ -7,11 +10,11 @@ /** * A tomcat specific implementation of the {@link ServerAdapter} interface. - * + * * @author Sébastien Pennec */ public class TomcatServerAdapter implements ServerAdapter { - + Request request; Response response; @@ -19,7 +22,7 @@ this.request = tomcatRequest; this.response = tomcatResponse; } - + public long getContentLength() { return response.getContentLength(); } @@ -27,8 +30,16 @@ public int getStatusCode() { return response.getStatus(); } - + public String getResponseHeader(String key) { return response.getHeader(key); } + + public List<String> getResponseHeaderNameList() { + List<String> hnList = new ArrayList<String>(); + for (String name : response.getHeaderNames()) { + hnList.add(name); + } + return hnList; + } } Modified: logback/trunk/logback-access/src/test/java/ch/qos/logback/access/pattern/helpers/DummyServerAdapter.java ============================================================================== --- logback/trunk/logback-access/src/test/java/ch/qos/logback/access/pattern/helpers/DummyServerAdapter.java (original) +++ logback/trunk/logback-access/src/test/java/ch/qos/logback/access/pattern/helpers/DummyServerAdapter.java Thu Jan 18 17:05:40 2007 @@ -1,5 +1,7 @@ package ch.qos.logback.access.pattern.helpers; +import java.util.List; + import ch.qos.logback.access.spi.ServerAdapter; public class DummyServerAdapter implements ServerAdapter { @@ -24,4 +26,8 @@ return response.getHeader(key); } + public List<String> getResponseHeaderNameList() { + return null; + } + } Modified: logback/trunk/logback-access/src/test/java/ch/qos/logback/access/pattern/helpers/DummyValuesAdapter.java ============================================================================== --- logback/trunk/logback-access/src/test/java/ch/qos/logback/access/pattern/helpers/DummyValuesAdapter.java (original) +++ logback/trunk/logback-access/src/test/java/ch/qos/logback/access/pattern/helpers/DummyValuesAdapter.java Thu Jan 18 17:05:40 2007 @@ -1,5 +1,7 @@ package ch.qos.logback.access.pattern.helpers; +import java.util.List; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -32,4 +34,8 @@ return 1; } + public List<String> getResponseHeaderNameList() { + return null; + } + }