This project has retired. For details please refer to its Attic page.
LoggingFilter xref

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.chemistry.opencmis.server.support.filter;
20  
21  import java.io.BufferedReader;
22  import java.io.ByteArrayOutputStream;
23  import java.io.File;
24  import java.io.FileReader;
25  import java.io.FileWriter;
26  import java.io.IOException;
27  import java.io.PrintWriter;
28  import java.io.StringReader;
29  import java.io.StringWriter;
30  import java.io.Writer;
31  import java.text.SimpleDateFormat;
32  import java.util.Date;
33  import java.util.Enumeration;
34  import java.util.HashMap;
35  import java.util.Locale;
36  import java.util.Map;
37  import java.util.Scanner;
38  
39  import javax.servlet.Filter;
40  import javax.servlet.FilterChain;
41  import javax.servlet.FilterConfig;
42  import javax.servlet.ServletException;
43  import javax.servlet.ServletInputStream;
44  import javax.servlet.ServletOutputStream;
45  import javax.servlet.ServletRequest;
46  import javax.servlet.ServletResponse;
47  import javax.servlet.http.Cookie;
48  import javax.servlet.http.HttpServletRequest;
49  import javax.servlet.http.HttpServletRequestWrapper;
50  import javax.servlet.http.HttpServletResponse;
51  import javax.servlet.http.HttpServletResponseWrapper;
52  import javax.xml.transform.OutputKeys;
53  import javax.xml.transform.Source;
54  import javax.xml.transform.Transformer;
55  import javax.xml.transform.TransformerFactory;
56  import javax.xml.transform.stream.StreamResult;
57  import javax.xml.transform.stream.StreamSource;
58  
59  import org.apache.commons.logging.Log;
60  import org.apache.commons.logging.LogFactory;
61  
62  public class LoggingFilter implements Filter {
63  
64      private static final Log log = LogFactory.getLog(LoggingFilter.class);
65      private static int REQUEST_NO = 0;
66      private static final SimpleDateFormat FORMAT = new SimpleDateFormat("EEE MMM dd hh:mm:ss a z yyyy", Locale.US);
67      private String logDir;
68      private boolean prettyPrint = true;
69      private boolean logHeaders = true;
70      private int indent = -1;
71  
72      public void init(FilterConfig cfg) throws ServletException {
73          
74          String val; 
75          logDir = cfg.getInitParameter("LogDir");
76          if (null == logDir || logDir.length() == 0)
77              logDir = System.getProperty("java.io.tmpdir");
78          if (null == logDir|| logDir.length() == 0)
79              logDir = "." + File.separator;
80  
81          if (!logDir.endsWith(File.separator))
82              logDir += File.separator;
83  
84          val = cfg.getInitParameter("Indent");
85          if (null != val)
86              indent = Integer.parseInt(val);
87          if (indent < 0)
88              indent = 4;
89  
90          val = cfg.getInitParameter("PrettyPrint");
91          if (null != val)
92              prettyPrint = Boolean.parseBoolean(val);
93  
94          val = cfg.getInitParameter("LogHeaders");
95          if (null != val)
96              logHeaders = Boolean.parseBoolean(val);
97      }
98  
99      public void destroy() {
100     }
101 
102     public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException,
103             ServletException {
104         log.debug("Logging filter doFilter");
105 
106         if (resp instanceof HttpServletResponse && req instanceof HttpServletRequest) {
107             LoggingRequestWrapper logReq = new LoggingRequestWrapper((HttpServletRequest)req);
108             LoggingResponseWrapper logResponse = new LoggingResponseWrapper((HttpServletResponse)resp);
109             
110             chain.doFilter(logReq, logResponse);
111 
112             int reqNo = getNextRequestNumber();
113             String requestFileName = getRequestFileName(reqNo);
114             String cType = logReq.getContentType();
115             String xmlRequest = logReq.getPayload();
116             StringBuffer sb = new StringBuffer();
117             
118             if (logHeaders)
119                 logHeaders(logReq, sb);
120 
121             if (xmlRequest == null || xmlRequest.length() == 0)
122                 xmlRequest = "";
123 
124             if (prettyPrint && cType != null) {
125                 if (cType.startsWith("multipart")) {
126                     xmlRequest = processMultipart(cType, xmlRequest);
127                 } else if (cType.contains("xml")) { 
128                     xmlRequest = prettyPrintXml(xmlRequest, indent);
129                 }
130             }
131             
132             xmlRequest = sb.toString() + xmlRequest;
133             log.debug("Found request: " + requestFileName + ": " + xmlRequest);
134             writeTextToFile(requestFileName, xmlRequest);
135             
136 
137             sb = new StringBuffer();
138             cType = logResponse.getContentType();
139             String xmlResponse = logResponse.getPayload();
140             String responseFileName = getResponseFileName(reqNo);
141             
142             if (logHeaders) {
143                 logHeaders(logResponse, req.getProtocol(), sb);
144             }
145             
146             if (xmlResponse == null || xmlResponse.length() == 0) 
147                 xmlResponse = "";
148 
149             if (prettyPrint && cType != null) {
150                 if (cType.startsWith("multipart")) {
151                     xmlResponse = processMultipart(cType, xmlResponse);
152                 } else if (cType.contains("xml")) { 
153                     xmlResponse = prettyPrintXml(xmlResponse, indent);
154                 } else if (cType.contains("json")) { 
155                     xmlResponse = prettyPrintJson(xmlResponse, indent);
156                 }
157             }
158                 
159             xmlResponse = sb.toString() + xmlResponse;
160             log.debug("Found response: " + responseFileName  + ": " + xmlResponse);
161             writeTextToFile(responseFileName, xmlResponse);
162         } else {            
163             chain.doFilter(req, resp);
164         }
165     }
166     
167     private void writeTextToFile(String filename, String content) {
168         PrintWriter pw = null;
169         FileWriter fw = null;
170         try {
171             fw = new FileWriter(filename);
172             pw = new PrintWriter(fw);
173 
174             Scanner scanner = new Scanner(content);
175             while (scanner.hasNextLine()) {
176                 String line = scanner.nextLine();
177                 pw.println(line);
178             }
179 
180             pw.flush();
181         } catch (IOException e) {
182             e.printStackTrace();
183         } finally {
184             if (pw != null)
185                 pw.close();
186             if (fw != null)
187                 try {
188                     fw.close();
189                 } catch (IOException e) {
190                     e.printStackTrace();
191                 }
192         }
193     }
194     
195     private static String prettyPrintXml(String input, int indent) {
196         try {
197             Source xmlInput = new StreamSource(new StringReader(input));
198             StringWriter stringWriter = new StringWriter();
199             StreamResult xmlOutput = new StreamResult(stringWriter);
200             TransformerFactory transformerFactory = TransformerFactory.newInstance();
201             transformerFactory.setAttribute("indent-number", indent);
202             Transformer transformer = transformerFactory.newTransformer(); 
203             transformer.setOutputProperty(OutputKeys.INDENT, "yes");
204             transformer.transform(xmlInput, xmlOutput);
205             return xmlOutput.getWriter().toString();
206         } catch (Exception e) {
207             throw new RuntimeException(e); // simple exception handling, please review it
208         }
209     }
210     
211     private static String prettyPrintJson(String input, int indent) {
212         JsonPrettyPrinter pp = new JsonPrettyPrinter(indent);
213         return pp.prettyPrint(input);
214     }
215     
216     private String processMultipart(String cType, String messageBody) throws IOException {
217         int beginIndex = cType.indexOf("boundary=\"") + 10;
218         int endIndex = cType.indexOf("\"", beginIndex);
219         if (endIndex < 0)
220             endIndex = cType.length();
221         String boundary = "--" + cType.substring(beginIndex, endIndex);
222         log.debug("Boundary = " + boundary);
223         BufferedReader in = new BufferedReader(new StringReader(messageBody));
224         StringBuffer out = new StringBuffer();
225         String line;
226         ByteArrayOutputStream xmlBodyBuffer = new ByteArrayOutputStream();
227         boolean boundaryFound;
228         
229         boolean inXmlOrJsonBody = false;
230         boolean inXmlOrJsonPart = false;
231         boolean isXml;
232         while ((line = in.readLine()) != null) {
233             if (inXmlOrJsonPart) {
234                 if (line.startsWith("<?xml") || line.startsWith("{")) {
235                     inXmlOrJsonBody = true;
236                     isXml = line.startsWith("<?xml");
237                     xmlBodyBuffer.write(line.getBytes(), 0, line.length());                   
238                     while (inXmlOrJsonBody)  {
239                         line = in.readLine();
240                         boundaryFound = line.startsWith(boundary);
241                         if (boundaryFound) {
242                             log.debug("Leaving XML body: " + line);
243                             inXmlOrJsonBody = false;
244                             inXmlOrJsonPart = false;
245                             if (isXml)
246                                 out.append(prettyPrintXml(xmlBodyBuffer.toString(), indent));
247                             else
248                                 out.append(prettyPrintJson(xmlBodyBuffer.toString(), indent));
249                             out.append(line).append("\n");
250                         } else
251                             xmlBodyBuffer.write(line.getBytes(), 0, line.length());
252                     }               
253                 } else { 
254                     log.debug("in XML part is: " + line);
255                     out.append(line).append("\n");
256                 }
257 
258             } else {
259                 log.debug("not in XML part: " + line);
260                 out.append(line).append("\n");
261                 boundaryFound = line.startsWith(boundary);
262                 if (boundaryFound) {
263                     log.debug("Boundardy found!");
264                     inXmlOrJsonPart = true;
265                 }
266             }
267         }
268         in.close();        
269         log.debug("End parsing multipart.");
270         
271         return out.toString();
272     }
273 
274     @SuppressWarnings("rawtypes")
275     private void logHeaders(LoggingRequestWrapper req, StringBuffer sb) {
276         sb.append(req.getMethod());
277         sb.append(" ");
278         sb.append(req.getRequestURI());
279         String queryString = req.getQueryString();
280         if (null != queryString && queryString.length() > 0) {
281             sb.append("?");
282             sb.append(queryString);
283         }
284         sb.append(" ");
285         sb.append(req.getProtocol());
286         sb.append("\n");
287         Enumeration headerNames = req.getHeaderNames();
288         while (headerNames.hasMoreElements()) {
289             String headerName = headerNames.nextElement().toString();
290             headerName = headerName.substring(0, 1).toUpperCase() + headerName.substring(1);
291             sb.append(headerName + ": ");
292             sb.append(req.getHeader(headerName));
293             sb.append("\n");
294         }
295         sb.append("\n");
296     }
297 
298     private void logHeaders(LoggingResponseWrapper resp, String protocol, StringBuffer sb) {
299         sb.append(protocol);
300         sb.append(" ");
301         sb.append(String.valueOf(resp.getStatus()));
302         sb.append("\n");
303         Map<String, String> headers = resp.getHeaders();
304         for ( Map.Entry<String, String> header: headers.entrySet()) {
305             sb.append(header.getKey());
306             sb.append(": ");
307             sb.append(header.getValue());
308             sb.append("\n");            
309         }
310         sb.append("\n");
311     }
312 
313     private String getRequestFileName(int no) {
314         return logDir + String.format("%05d-request.log", no);
315     }
316     
317     private String getResponseFileName(int no) {
318         return logDir + String.format("%05d-response.log", no);
319     }
320     
321     private static synchronized int getNextRequestNumber() {
322         return REQUEST_NO++;
323     }
324     
325     private class LoggingRequestWrapper extends HttpServletRequestWrapper {
326         
327         private LoggingInputStream is;
328    
329         public LoggingRequestWrapper(HttpServletRequest request) throws IOException {
330            super(request);
331         }
332    
333         @Override
334         public ServletInputStream getInputStream() throws IOException {
335             this.is = new LoggingInputStream(super.getInputStream());
336             return is;
337         }
338    
339         public String getPayload() {
340            return null == is ? "" : is.getPayload();
341         }
342      }
343    
344      private class LoggingInputStream extends ServletInputStream {
345    
346         private ByteArrayOutputStream baous = new ByteArrayOutputStream();
347         private ServletInputStream is;
348    
349         public LoggingInputStream(ServletInputStream is) {
350            super();
351            this.is = is;
352         }
353    
354         // Since we are not sure which method is used just overwrite all 4 of them:
355         @Override
356         public int read() throws IOException {
357            int ch = is.read();
358            if (ch != -1) {
359               baous.write(ch);
360            }
361            return ch;
362         }
363    
364         @Override
365         public int read(byte[] b) throws IOException {
366            int ch = is.read(b);
367            if (ch != -1) {
368               baous.write(b, 0, ch);
369            }
370            return ch;
371         }
372    
373         @Override
374         public int read(byte[] b, int o, int l) throws IOException {
375            int ch = is.read(b, o, l);
376            if (ch != -1) {
377               baous.write(b, o, ch);
378            }
379            return ch;
380         }
381         
382         @Override
383         public int readLine(byte[] b, int o, int l) throws IOException {
384            int ch = is.readLine(b, o, l);
385            if (ch != -1) {
386                baous.write(b, o, ch);
387            }
388            return ch;
389         }
390    
391         public String getPayload() {
392            return baous.toString();
393         }
394      }
395      
396      private class LoggingResponseWrapper extends HttpServletResponseWrapper {
397          
398          private LoggingOutputStream os;
399          private PrintWriter writer;
400          private int statusCode;
401          private Map<String, String> headers = new HashMap<String, String>();
402          String encoding;
403          
404          public LoggingResponseWrapper(HttpServletResponse response) throws IOException {
405             super(response);
406             this.os = new LoggingOutputStream(response.getOutputStream());
407          }
408     
409          @Override
410          public PrintWriter getWriter() {
411             try {
412                 if (null == writer)
413                     writer = new PrintWriter(this.getOutputStream());
414                 return writer;
415             } catch (IOException e) {
416                 log.error("Failed to get PrintWriter in LoggingFilter: "+ e);
417                 e.printStackTrace();
418                 return null;             
419             }
420          }
421          
422          @Override
423          public ServletOutputStream getOutputStream() throws IOException {
424             return os;
425          }
426     
427          public String getPayload() {
428             return os.getPayload();
429          }
430          
431          @Override
432          public void addCookie(Cookie cookie) {
433              super.addCookie(cookie);
434              String value;
435              if (headers.containsKey("Cookie")) {
436                  value = headers.get("Cookie") + "; " + cookie.toString();
437              } else
438                  value = cookie.toString();
439              headers.put("Cookie", value);
440          }
441          
442          @Override
443          public void setContentType(String type) {
444              super.setContentType(type);
445              if (headers.containsKey("Content-Type")) {
446                  String cType = headers.get("Content-Type");
447                  int pos = cType.indexOf(";charset=");
448                  if (pos < 0 && encoding != null)
449                      type = cType + ";charset=" + encoding;
450                  else if (pos >= 0)
451                      encoding = null;                 
452              }
453              headers.put("Content-Type", type);             
454          }
455          
456          @Override         
457          public void setCharacterEncoding(java.lang.String charset) {
458              super.setCharacterEncoding(charset);
459              encoding = charset;
460              if (headers.containsKey("Content-Type")) {
461                  String cType = headers.get("Content-Type");
462                  int pos = cType.indexOf(";charset=");
463                  if (pos >=0)
464                      cType = cType.substring(0, pos) + ";charset=" + encoding;
465                  else
466                      cType = cType + ";charset=" + encoding;
467                  headers.put("Content-Type", cType);
468              }
469          }
470          
471          @Override
472          public void setContentLength(int len) {
473              super.setContentLength(len);
474              headers.put("Content-Length", String.valueOf(len));                          
475          }
476          
477          private String getDateString(long date) {
478              return FORMAT.format(new Date(date));             
479          }
480          
481          @Override
482          public void setDateHeader(String name, long date) {
483              super.setDateHeader(name, date);
484              headers.put(name, String.valueOf(getDateString(date)));
485          }
486          
487          @Override
488          public void addDateHeader(String name, long date) {
489              super.addDateHeader(name, date);
490              if (headers.containsKey(name)) {
491                  headers.put(name, headers.get(name) + "; " + getDateString(date));
492              } else {
493                  headers.put(name, String.valueOf(getDateString(date)));
494              }
495          }
496          
497          @Override
498          public void setHeader(String name, String value) {
499              super.setHeader(name, value);
500              headers.put(name, String.valueOf(value));
501          }
502 
503          @Override
504          public void addHeader(String name, String value) {
505              super.addHeader(name, value);
506              if (headers.containsKey(name)) {
507                  headers.put(name, headers.get(name) + "; " + value);
508              } else {
509                  headers.put(name, String.valueOf(value));
510              }
511          }
512          
513          @Override
514          public void setIntHeader(String name, int value) {
515              super.setIntHeader(name, value);
516              headers.put(name, String.valueOf(value));
517          }
518          
519          @Override
520          public void addIntHeader(String name, int value) {
521              super.addIntHeader(name, value);
522              if (headers.containsKey(name)) {
523                  headers.put(name, headers.get(name) + "; " + String.valueOf(value));
524              } else {
525                  headers.put(name, String.valueOf(value));
526              }
527          }
528          
529          @Override
530          public void sendError(int sc) throws IOException {
531              statusCode = sc;
532              super.sendError(sc);
533          }
534 
535          @Override
536          public void sendError(int sc, String msg) throws IOException {
537              statusCode = sc;
538              super.sendError(sc, msg);
539          }
540 
541          @Override
542          public void sendRedirect(String location) throws IOException {
543              statusCode = 302;
544              super.sendRedirect(location);
545          }
546 
547          @Override
548          public void setStatus(int sc) {
549              statusCode = sc;
550              super.setStatus(sc);
551          }
552 
553          public int getStatus() {
554              return statusCode;
555          }
556 
557          public Map<String, String> getHeaders() {
558              return headers;
559          }
560       }
561     
562       private class LoggingOutputStream extends ServletOutputStream {
563           private ByteArrayOutputStream baous = new ByteArrayOutputStream();
564           private ServletOutputStream os;
565      
566           public LoggingOutputStream(ServletOutputStream os) {
567              super();
568              this.os = os;
569           }
570           
571           public String getPayload() {
572               return new String(baous.toString());
573            }
574 
575         @Override
576         public void write(byte[] b, int off, int len) {
577             try {
578                 baous.write(b, off, len);
579                 os.write(b, off, len);
580             } catch (IOException e) {
581                 throw new RuntimeException(e);
582             }
583         }
584         
585         @Override
586         public
587         void write(byte[] b) {
588             try {
589                 baous.write(b);
590                 os.write(b);
591             } catch (IOException e) {
592                 throw new RuntimeException(e);
593             }
594         }
595          
596         @Override
597         public void write(int ch) throws IOException {
598             baous.write(ch);
599             os.write(ch);
600         }
601       }
602 }