updating logging filters
[logging-analytics.git] / reference / logging-filter / logging-filter-base / src / main / java / org / onap / logging / filter / base / PayloadLoggingServletFilter.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - Logging
4  * ================================================================================
5  * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Modifications Copyright (C) 2018 IBM.
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  * 
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  * 
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.logging.filter.base;
24
25 import java.io.BufferedReader;
26 import java.io.ByteArrayInputStream;
27 import java.io.ByteArrayOutputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.InputStreamReader;
31 import java.io.PrintWriter;
32 import java.util.Enumeration;
33 import java.util.zip.GZIPInputStream;
34 import javax.servlet.Filter;
35 import javax.servlet.FilterChain;
36 import javax.servlet.FilterConfig;
37 import javax.servlet.ReadListener;
38 import javax.servlet.ServletException;
39 import javax.servlet.ServletInputStream;
40 import javax.servlet.ServletOutputStream;
41 import javax.servlet.ServletRequest;
42 import javax.servlet.ServletResponse;
43 import javax.servlet.WriteListener;
44 import javax.servlet.http.HttpServletRequest;
45 import javax.servlet.http.HttpServletRequestWrapper;
46 import javax.servlet.http.HttpServletResponse;
47 import javax.servlet.http.HttpServletResponseWrapper;
48 import javax.ws.rs.core.HttpHeaders;
49
50 public class PayloadLoggingServletFilter implements Filter {
51
52     private static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PayloadLoggingServletFilter.class);
53     private static final String REDACTED = "***REDACTED***";
54
55     private static class ByteArrayServletStream extends ServletOutputStream {
56         ByteArrayOutputStream baos;
57
58         ByteArrayServletStream(ByteArrayOutputStream baos) {
59             this.baos = baos;
60         }
61
62         @Override
63         public void write(int param) throws IOException {
64             baos.write(param);
65         }
66
67         @Override
68         public boolean isReady() {
69             return true;
70         }
71
72         @Override
73         public void setWriteListener(WriteListener arg0) {
74             // this method does nothing
75         }
76     }
77
78     private static class ByteArrayPrintWriter extends PrintWriter {
79         private ByteArrayOutputStream baos;
80         private int errorCode = -1;
81         private String errorMsg = "";
82         private boolean errored = false;
83
84         public ByteArrayPrintWriter(ByteArrayOutputStream out) {
85             super(out);
86             this.baos = out;
87         }
88
89         public ServletOutputStream getStream() {
90             return new ByteArrayServletStream(baos);
91         }
92
93         public Boolean hasErrored() {
94             return errored;
95         }
96
97         public int getErrorCode() {
98             return errorCode;
99         }
100
101         public String getErrorMsg() {
102             return errorMsg;
103         }
104
105         public void setError(int code) {
106             errorCode = code;
107             errored = true;
108         }
109
110         public void setError(int code, String msg) {
111             errorMsg = msg;
112             errorCode = code;
113             errored = true;
114         }
115
116     }
117
118     private class BufferedServletInputStream extends ServletInputStream {
119         ByteArrayInputStream bais;
120
121         public BufferedServletInputStream(ByteArrayInputStream bais) {
122             this.bais = bais;
123         }
124
125         @Override
126         public int available() {
127             return bais.available();
128         }
129
130         @Override
131         public int read() {
132             return bais.read();
133         }
134
135         @Override
136         public int read(byte[] buf, int off, int len) {
137             return bais.read(buf, off, len);
138         }
139
140         @Override
141         public boolean isFinished() {
142             return available() < 1;
143         }
144
145         @Override
146         public boolean isReady() {
147             return true;
148         }
149
150         @Override
151         public void setReadListener(ReadListener arg0) {
152             // this method does nothing
153         }
154
155     }
156
157     private class BufferedRequestWrapper extends HttpServletRequestWrapper {
158         ByteArrayInputStream bais;
159         ByteArrayOutputStream baos;
160         BufferedServletInputStream bsis;
161         byte[] buffer;
162
163         public BufferedRequestWrapper(HttpServletRequest req) throws IOException {
164             super(req);
165
166             InputStream is = req.getInputStream();
167             baos = new ByteArrayOutputStream();
168             byte[] buf = new byte[1024];
169             int letti;
170             while ((letti = is.read(buf)) > 0) {
171                 baos.write(buf, 0, letti);
172             }
173             buffer = baos.toByteArray();
174         }
175
176         @Override
177         public ServletInputStream getInputStream() {
178             try {
179                 bais = new ByteArrayInputStream(buffer);
180                 bsis = new BufferedServletInputStream(bais);
181             } catch (Exception ex) {
182                 log.error("Exception in getInputStream", ex);
183             }
184             return bsis;
185         }
186
187         public byte[] getBuffer() {
188             return buffer;
189         }
190     }
191
192     @Override
193     public void init(FilterConfig filterConfig) throws ServletException {
194         // this method does nothing
195     }
196
197     @Override
198     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
199             throws IOException, ServletException {
200         final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
201         BufferedRequestWrapper bufferedRequest = new BufferedRequestWrapper(httpRequest);
202
203         StringBuilder requestHeaders = new StringBuilder("REQUEST|");
204         requestHeaders.append(httpRequest.getMethod());
205         requestHeaders.append(":");
206         requestHeaders.append(httpRequest.getRequestURL().toString());
207         requestHeaders.append("|");
208         requestHeaders.append(getSecureRequestHeaders(httpRequest));
209         log.info(requestHeaders.toString());
210
211         log.info("REQUEST BODY|" + new String(bufferedRequest.getBuffer()));
212
213         final HttpServletResponse response = (HttpServletResponse) servletResponse;
214         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
215         final ByteArrayPrintWriter pw = new ByteArrayPrintWriter(baos);
216
217         HttpServletResponse wrappedResp = new HttpServletResponseWrapper(response) {
218             @Override
219             public PrintWriter getWriter() {
220                 return pw;
221             }
222
223             @Override
224             public ServletOutputStream getOutputStream() {
225                 return pw.getStream();
226             }
227
228             @Override
229             public void sendError(int sc) throws IOException {
230                 super.sendError(sc);
231                 pw.setError(sc);
232
233             }
234
235             @Override
236             public void sendError(int sc, String msg) throws IOException {
237                 super.sendError(sc, msg);
238                 pw.setError(sc, msg);
239             }
240         };
241
242         try {
243             filterChain.doFilter(bufferedRequest, wrappedResp);
244         } catch (Exception e) {
245             log.error("Chain Exception", e);
246             throw e;
247         } finally {
248             try {
249                 byte[] bytes = baos.toByteArray();
250                 StringBuilder responseHeaders = new StringBuilder("RESPONSE HEADERS|");
251                 responseHeaders.append(formatResponseHeaders(response));
252                 responseHeaders.append("Status:");
253                 responseHeaders.append(response.getStatus());
254                 responseHeaders.append(";IsCommited:" + wrappedResp.isCommitted());
255
256                 log.info(responseHeaders.toString());
257
258                 if ("gzip".equals(response.getHeader("Content-Encoding"))) {
259                     log.info("UNGZIPED RESPONSE BODY|" + decompressGZIPByteArray(bytes));
260                 } else {
261                     log.info("RESPONSE BODY|" + new String(bytes));
262                 }
263
264                 if (pw.hasErrored()) {
265                     log.info("ERROR RESPONSE|" + pw.getErrorCode() + ":" + pw.getErrorMsg());
266                 } else {
267                     if (!wrappedResp.isCommitted()) {
268                         response.getOutputStream().write(bytes);
269                         response.getOutputStream().flush();
270                     }
271                 }
272             } catch (Exception e) {
273                 log.error("Exception in response filter", e);
274             }
275         }
276     }
277
278     @Override
279     public void destroy() {
280         // this method does nothing
281     }
282
283     private String decompressGZIPByteArray(byte[] bytes) {
284         BufferedReader in = null;
285         InputStreamReader inR = null;
286         ByteArrayInputStream byteS = null;
287         GZIPInputStream gzS = null;
288         StringBuilder str = new StringBuilder();
289         try {
290             byteS = new ByteArrayInputStream(bytes);
291             gzS = new GZIPInputStream(byteS);
292             inR = new InputStreamReader(gzS);
293             in = new BufferedReader(inR);
294
295             if (in != null) {
296                 String content;
297                 while ((content = in.readLine()) != null) {
298                     str.append(content);
299                 }
300             }
301
302         } catch (Exception e) {
303             log.error("Failed get read GZIPInputStream", e);
304         } finally {
305             if (byteS != null)
306                 try {
307                     byteS.close();
308                 } catch (IOException e1) {
309                     log.error("Failed to close ByteStream", e1);
310                 }
311             if (gzS != null)
312                 try {
313                     gzS.close();
314                 } catch (IOException e2) {
315                     log.error("Failed to close GZStream", e2);
316                 }
317             if (inR != null)
318                 try {
319                     inR.close();
320                 } catch (IOException e3) {
321                     log.error("Failed to close InputReader", e3);
322                 }
323             if (in != null)
324                 try {
325                     in.close();
326                 } catch (IOException e) {
327                     log.error("Failed to close BufferedReader", e);
328                 }
329         }
330         return str.toString();
331     }
332
333     protected String getSecureRequestHeaders(HttpServletRequest httpRequest) {
334         StringBuilder sb = new StringBuilder();
335         String header;
336         for (Enumeration<String> e = httpRequest.getHeaderNames(); e.hasMoreElements();) {
337             header = e.nextElement();
338             sb.append(header);
339             sb.append(":");
340             if (header.equalsIgnoreCase(HttpHeaders.AUTHORIZATION)) {
341                 sb.append(REDACTED);
342             } else {
343                 sb.append(httpRequest.getHeader(header));
344             }
345             sb.append(";");
346         }
347         return sb.toString();
348     }
349
350     protected String formatResponseHeaders(HttpServletResponse response) {
351         StringBuilder sb = new StringBuilder();
352         for (String headerName : response.getHeaderNames()) {
353             sb.append(headerName);
354             sb.append(":");
355             sb.append(response.getHeader(headerName));
356             sb.append(";");
357         }
358         return sb.toString();
359     }
360 }