1 /* Copyright (c) 2019 AT&T Intellectual Property. #
\r
3 # Licensed under the Apache License, Version 2.0 (the "License"); #
\r
4 # you may not use this file except in compliance with the License. #
\r
5 # You may obtain a copy of the License at #
\r
7 # http://www.apache.org/licenses/LICENSE-2.0 #
\r
9 # Unless required by applicable law or agreed to in writing, software #
\r
10 # distributed under the License is distributed on an "AS IS" BASIS, #
\r
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
\r
12 # See the License for the specific language governing permissions and #
\r
13 # limitations under the License. #
\r
14 ##############################################################################*/
\r
17 package org.oran.otf.spring.configuration;
\r
19 import java.io.BufferedInputStream;
\r
20 import java.io.ByteArrayOutputStream;
\r
21 import java.io.FilterOutputStream;
\r
22 import java.io.IOException;
\r
23 import java.io.InputStream;
\r
24 import java.io.OutputStream;
\r
25 import java.net.URI;
\r
26 import java.nio.charset.Charset;
\r
27 import java.util.ArrayList;
\r
28 import java.util.Base64;
\r
29 import java.util.List;
\r
30 import java.util.Objects;
\r
31 import java.util.logging.Level;
\r
32 import java.util.logging.Logger;
\r
33 import javax.ws.rs.WebApplicationException;
\r
34 import javax.ws.rs.client.ClientRequestContext;
\r
35 import javax.ws.rs.client.ClientRequestFilter;
\r
36 import javax.ws.rs.client.ClientResponseContext;
\r
37 import javax.ws.rs.client.ClientResponseFilter;
\r
38 import javax.ws.rs.container.ContainerRequestContext;
\r
39 import javax.ws.rs.container.ContainerRequestFilter;
\r
40 import javax.ws.rs.container.ContainerResponseContext;
\r
41 import javax.ws.rs.container.ContainerResponseFilter;
\r
42 import javax.ws.rs.core.FeatureContext;
\r
43 import javax.ws.rs.core.MultivaluedMap;
\r
44 import javax.ws.rs.ext.WriterInterceptor;
\r
45 import javax.ws.rs.ext.WriterInterceptorContext;
\r
46 import org.glassfish.jersey.logging.LoggingFeature;
\r
47 import org.glassfish.jersey.message.MessageUtils;
\r
49 public class OTFLoggingFeature extends LoggingFeature implements ContainerRequestFilter, ContainerResponseFilter,
\r
50 ClientRequestFilter, ClientResponseFilter, WriterInterceptor {
\r
52 private static final boolean printEntity = true;
\r
53 private static final int maxEntitySize = 8 * 1024;
\r
54 private final Logger logger = Logger.getLogger("OTFLoggingFeature");
\r
55 private static final String ENTITY_LOGGER_PROPERTY = OTFLoggingFeature.class.getName();
\r
56 private static final String NOTIFICATION_PREFIX = "* ";
\r
57 private static final String REQUEST_PREFIX = "> ";
\r
58 private static final String RESPONSE_PREFIX = "< ";
\r
59 private static final String AUTHORIZATION = "Authorization";
\r
60 private static final String EQUAL = " = ";
\r
61 private static final String HEADERS_SEPARATOR = ", ";
\r
62 private static List<String> requestHeaders;
\r
65 requestHeaders = new ArrayList<>();
\r
66 requestHeaders.add(AUTHORIZATION);
\r
69 public OTFLoggingFeature(Logger logger, Level level, Verbosity verbosity, Integer maxEntitySize) {
\r
70 super(logger, level, verbosity, maxEntitySize);
\r
74 public boolean configure(FeatureContext context) {
\r
75 context.register(this);
\r
79 private Object getEmail(Object authorization){
\r
81 String encoded = ((String) authorization).split(" ")[1];
\r
82 String decoded = new String(Base64.getDecoder().decode(encoded));
\r
83 return decoded.split(":")[0];
\r
85 catch (Exception e){
\r
86 return authorization;
\r
91 public void filter(final ClientRequestContext context) {
\r
92 final StringBuilder b = new StringBuilder();
\r
93 printHeaders(b, context.getStringHeaders());
\r
94 printRequestLine(b, "Sending client request", context.getMethod(), context.getUri());
\r
96 if (printEntity && context.hasEntity()) {
\r
97 final OutputStream stream = new LoggingStream(b, context.getEntityStream());
\r
98 context.setEntityStream(stream);
\r
99 context.setProperty(ENTITY_LOGGER_PROPERTY, stream);
\r
100 // not calling log(b) here - it will be called by the interceptor
\r
107 public void filter(final ClientRequestContext requestContext, final ClientResponseContext responseContext) throws IOException {
\r
108 final StringBuilder b = new StringBuilder();
\r
109 printResponseLine(b, "Client response received", responseContext.getStatus());
\r
111 if (printEntity && responseContext.hasEntity()) {
\r
112 responseContext.setEntityStream(logInboundEntity(b, responseContext.getEntityStream(),
\r
113 MessageUtils.getCharset(responseContext.getMediaType())));
\r
119 public void filter(final ContainerRequestContext context) throws IOException {
\r
120 final StringBuilder b = new StringBuilder();
\r
121 printHeaders(b, context.getHeaders());
\r
122 printRequestLine(b, "Server has received a request", context.getMethod(), context.getUriInfo().getRequestUri());
\r
124 if (printEntity && context.hasEntity()) {
\r
125 context.setEntityStream(logInboundEntity(b, context.getEntityStream(), MessageUtils.getCharset(context.getMediaType())));
\r
131 public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) {
\r
132 final StringBuilder b = new StringBuilder();
\r
133 printResponseLine(b, "Server responded with a response", responseContext.getStatus());
\r
135 if (printEntity && responseContext.hasEntity()) {
\r
136 final OutputStream stream = new LoggingStream(b, responseContext.getEntityStream());
\r
137 responseContext.setEntityStream(stream);
\r
138 requestContext.setProperty(ENTITY_LOGGER_PROPERTY, stream);
\r
139 // not calling log(b) here - it will be called by the interceptor
\r
146 public void aroundWriteTo(final WriterInterceptorContext writerInterceptorContext) throws IOException, WebApplicationException {
\r
147 final LoggingStream stream = (LoggingStream) writerInterceptorContext.getProperty(ENTITY_LOGGER_PROPERTY);
\r
148 writerInterceptorContext.proceed();
\r
149 if (stream != null) {
\r
150 log(stream.getStringBuilder(MessageUtils.getCharset(writerInterceptorContext.getMediaType())));
\r
154 private static class LoggingStream extends FilterOutputStream {
\r
155 private final StringBuilder b;
\r
156 private final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
\r
158 LoggingStream(final StringBuilder b, final OutputStream inner) {
\r
164 StringBuilder getStringBuilder(Charset charset) {
\r
165 // write entity to the builder
\r
166 final byte[] entity = byteArrayOutputStream.toByteArray();
\r
168 b.append(new String(entity, 0, Math.min(entity.length, maxEntitySize), charset));
\r
169 if (entity.length > maxEntitySize) {
\r
170 b.append("...more...");
\r
177 public void write(final int i) throws IOException {
\r
178 if (byteArrayOutputStream.size() <= maxEntitySize) {
\r
179 byteArrayOutputStream.write(i);
\r
185 private void printHeaders(StringBuilder b, MultivaluedMap<String, String> headers) {
\r
186 for (String header : requestHeaders) {
\r
187 if (Objects.nonNull(headers.get(header))) {
\r
188 if(header.equalsIgnoreCase("Authorization")){
\r
189 b.append(header).append(EQUAL).append(getEmail(headers.get(header).get(0))).append(HEADERS_SEPARATOR);
\r
192 b.append(header).append(EQUAL).append(headers.get(header)).append(HEADERS_SEPARATOR);
\r
196 int lastIndex = b.lastIndexOf(HEADERS_SEPARATOR);
\r
197 if (lastIndex != -1) {
\r
198 b.delete(lastIndex, lastIndex + HEADERS_SEPARATOR.length());
\r
203 private void log(final StringBuilder b) {
\r
204 String message = b.toString();
\r
205 if (logger != null) {
\r
206 logger.info(message);
\r
210 private void printRequestLine(final StringBuilder b, final String note, final String method, final URI uri) {
\r
211 b.append(NOTIFICATION_PREFIX)
\r
213 .append(" on thread ").append(Thread.currentThread().getId())
\r
214 .append(REQUEST_PREFIX).append(method).append(" ")
\r
215 .append(uri.toASCIIString()).append("\n");
\r
218 private void printResponseLine(final StringBuilder b, final String note, final int status) {
\r
219 b.append(NOTIFICATION_PREFIX)
\r
221 .append(" on thread ").append(Thread.currentThread().getId())
\r
222 .append(RESPONSE_PREFIX)
\r
223 .append(Integer.toString(status))
\r
227 private InputStream logInboundEntity(final StringBuilder b, InputStream stream, final Charset charset) throws IOException {
\r
228 if (!stream.markSupported()) {
\r
229 stream = new BufferedInputStream(stream);
\r
231 stream.mark(maxEntitySize + 1);
\r
232 final byte[] entity = new byte[maxEntitySize + 1];
\r
233 final int entitySize = stream.read(entity);
\r
234 b.append(new String(entity, 0, Math.min(entitySize, maxEntitySize), charset));
\r
235 if (entitySize > maxEntitySize) {
\r
236 b.append("...more...");
\r