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