e8d2813aad119283685dae0a9ab4f1509ffd9384
[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.zip.GZIPInputStream;
33 import javax.servlet.Filter;
34 import javax.servlet.FilterChain;
35 import javax.servlet.FilterConfig;
36 import javax.servlet.ReadListener;
37 import javax.servlet.ServletException;
38 import javax.servlet.ServletInputStream;
39 import javax.servlet.ServletOutputStream;
40 import javax.servlet.ServletRequest;
41 import javax.servlet.ServletResponse;
42 import javax.servlet.WriteListener;
43 import javax.servlet.http.HttpServletRequest;
44 import javax.servlet.http.HttpServletRequestWrapper;
45 import javax.servlet.http.HttpServletResponse;
46 import javax.servlet.http.HttpServletResponseWrapper;
47
48 public class PayloadLoggingServletFilter extends AbstractServletFilter implements Filter {
49
50     private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PayloadLoggingServletFilter.class);
51     private static final int defaultMaxSize = 100000;
52     private static Integer maxResponseSize;
53     private static Integer maxRequestSize;
54
55     public PayloadLoggingServletFilter() {
56         String maxRequestSizeOverride = System.getProperty("FILTER_MAX_REQUEST_SIZE");
57         if (maxRequestSizeOverride != null) {
58             maxRequestSize = Integer.valueOf(maxRequestSizeOverride);
59         } else {
60             maxRequestSize = defaultMaxSize;
61         }
62
63         String maxResponseSizeOverride = System.getProperty("FILTER_MAX_RESPONSE_SIZE");
64         if (maxResponseSizeOverride != null) {
65             maxResponseSize = Integer.valueOf(maxResponseSizeOverride);
66         } else {
67             maxResponseSize = defaultMaxSize;
68         }
69     }
70
71     private static class ByteArrayServletStream extends ServletOutputStream {
72         ByteArrayOutputStream baos;
73
74         ByteArrayServletStream(ByteArrayOutputStream baos) {
75             this.baos = baos;
76         }
77
78         @Override
79         public void write(int param) throws IOException {
80             baos.write(param);
81         }
82
83         @Override
84         public boolean isReady() {
85             return true;
86         }
87
88         @Override
89         public void setWriteListener(WriteListener arg0) {
90             // this method does nothing
91         }
92     }
93
94
95     private static class ByteArrayPrintWriter extends PrintWriter {
96         private ByteArrayOutputStream baos;
97         private int errorCode = -1;
98         private String errorMsg = "";
99         private boolean errored = false;
100
101         public ByteArrayPrintWriter(ByteArrayOutputStream out) {
102             super(out);
103             this.baos = out;
104         }
105
106         public ServletOutputStream getStream() {
107             return new ByteArrayServletStream(baos);
108         }
109
110         public Boolean hasErrored() {
111             return errored;
112         }
113
114         public int getErrorCode() {
115             return errorCode;
116         }
117
118         public String getErrorMsg() {
119             return errorMsg;
120         }
121
122         public void setError(int code) {
123             errorCode = code;
124             errored = true;
125         }
126
127         public void setError(int code, String msg) {
128             errorMsg = msg;
129             errorCode = code;
130             errored = true;
131         }
132
133     }
134
135
136     private class BufferedServletInputStream extends ServletInputStream {
137         ByteArrayInputStream bais;
138
139         public BufferedServletInputStream(ByteArrayInputStream bais) {
140             this.bais = bais;
141         }
142
143         @Override
144         public int available() {
145             return bais.available();
146         }
147
148         @Override
149         public int read() {
150             return bais.read();
151         }
152
153         @Override
154         public int read(byte[] buf, int off, int len) {
155             return bais.read(buf, off, len);
156         }
157
158         @Override
159         public boolean isFinished() {
160             return available() < 1;
161         }
162
163         @Override
164         public boolean isReady() {
165             return true;
166         }
167
168         @Override
169         public void setReadListener(ReadListener arg0) {
170             // this method does nothing
171         }
172
173     }
174
175
176     private class BufferedRequestWrapper extends HttpServletRequestWrapper {
177         ByteArrayInputStream bais;
178         ByteArrayOutputStream baos;
179         BufferedServletInputStream bsis;
180         byte[] buffer;
181
182         public BufferedRequestWrapper(HttpServletRequest req) throws IOException {
183             super(req);
184
185             InputStream is = req.getInputStream();
186             baos = new ByteArrayOutputStream();
187             byte[] buf = new byte[1024];
188             int letti;
189             while ((letti = is.read(buf)) > 0) {
190                 baos.write(buf, 0, letti);
191             }
192             buffer = baos.toByteArray();
193         }
194
195         @Override
196         public ServletInputStream getInputStream() {
197             try {
198                 bais = new ByteArrayInputStream(buffer);
199                 bsis = new BufferedServletInputStream(bais);
200             } catch (Exception ex) {
201                 log.error("Exception in getInputStream", ex);
202             }
203             return bsis;
204         }
205
206         public byte[] getBuffer() {
207             return buffer;
208         }
209     }
210
211     @Override
212     public void init(FilterConfig filterConfig) throws ServletException {
213         // this method does nothing
214     }
215
216     @Override
217     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
218             throws IOException, ServletException {
219         final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
220         BufferedRequestWrapper bufferedRequest = new BufferedRequestWrapper(httpRequest);
221
222         StringBuilder requestHeaders = new StringBuilder("REQUEST|");
223         requestHeaders.append(httpRequest.getMethod());
224         requestHeaders.append(":");
225         requestHeaders.append(httpRequest.getRequestURL().toString());
226         requestHeaders.append("|");
227         requestHeaders.append(getSecureRequestHeaders(httpRequest));
228
229         log.info(requestHeaders.toString());
230
231         byte[] buffer = bufferedRequest.getBuffer();
232         if (buffer.length < maxRequestSize) {
233             log.info("REQUEST BODY|{}", new String(buffer));
234         } else {
235             log.info("REQUEST BODY|{}", new String(buffer, 0, maxRequestSize));
236         }
237
238         final HttpServletResponse response = (HttpServletResponse) servletResponse;
239         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
240         final ByteArrayPrintWriter pw = new ByteArrayPrintWriter(baos);
241
242         HttpServletResponse wrappedResp = new HttpServletResponseWrapper(response) {
243             @Override
244             public PrintWriter getWriter() {
245                 return pw;
246             }
247
248             @Override
249             public ServletOutputStream getOutputStream() {
250                 return pw.getStream();
251             }
252
253             @Override
254             public void sendError(int sc) throws IOException {
255                 super.sendError(sc);
256                 pw.setError(sc);
257             }
258
259             @Override
260             public void sendError(int sc, String msg) throws IOException {
261                 super.sendError(sc, msg);
262                 pw.setError(sc, msg);
263             }
264         };
265
266         try {
267             filterChain.doFilter(bufferedRequest, wrappedResp);
268         } catch (Exception e) {
269             log.error("Chain Exception", e);
270             throw e;
271         } finally {
272             try {
273                 byte[] bytes = baos.toByteArray();
274                 StringBuilder responseHeaders = new StringBuilder();
275                 responseHeaders.append("RESPONSE HEADERS|").append(formatResponseHeaders(response));
276                 responseHeaders.append("Status:").append(response.getStatus());
277                 responseHeaders.append(";IsCommitted:").append(wrappedResp.isCommitted());
278
279                 log.info(responseHeaders.toString());
280
281                 if ("gzip".equals(response.getHeader("Content-Encoding"))) {
282                     log.info("UNGZIPED RESPONSE BODY|{}", decompressGZIPByteArray(bytes));
283                 } else {
284                     if (bytes.length < maxResponseSize) {
285                         log.info("RESPONSE BODY|{}", new String(bytes));
286                     } else {
287                         log.info("RESPONSE BODY|{}", new String(bytes, 0, maxResponseSize));
288                     }
289                 }
290
291                 if (pw.hasErrored()) {
292                     log.info("ERROR RESPONSE|{}:{}", pw.getErrorCode(), pw.getErrorMsg());
293                 } else if (!wrappedResp.isCommitted()) {
294                     response.getOutputStream().write(bytes);
295                     response.getOutputStream().flush();
296                 }
297             } catch (Exception e) {
298                 log.error("Exception in response filter", e);
299             }
300         }
301     }
302
303     @Override
304     public void destroy() {
305         // this method does nothing
306     }
307
308     private String decompressGZIPByteArray(byte[] bytes) {
309         StringBuilder str = new StringBuilder();
310         try (BufferedReader in =
311                 new BufferedReader(new InputStreamReader(new GZIPInputStream(new ByteArrayInputStream(bytes))))) {
312             String content;
313             while ((content = in.readLine()) != null) {
314                 str.append(content);
315             }
316         } catch (Exception e) {
317             log.error("Failed get read GZIPInputStream", e);
318         }
319         return str.toString();
320     }
321
322 }