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