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.camunda.configuration;
\r
19 import org.glassfish.jersey.logging.LoggingFeature;
\r
20 import org.glassfish.jersey.message.MessageUtils;
\r
22 import javax.ws.rs.WebApplicationException;
\r
23 import javax.ws.rs.client.ClientRequestContext;
\r
24 import javax.ws.rs.client.ClientRequestFilter;
\r
25 import javax.ws.rs.client.ClientResponseContext;
\r
26 import javax.ws.rs.client.ClientResponseFilter;
\r
27 import javax.ws.rs.container.ContainerRequestContext;
\r
28 import javax.ws.rs.container.ContainerRequestFilter;
\r
29 import javax.ws.rs.container.ContainerResponseContext;
\r
30 import javax.ws.rs.container.ContainerResponseFilter;
\r
31 import javax.ws.rs.core.FeatureContext;
\r
32 import javax.ws.rs.core.MultivaluedMap;
\r
33 import javax.ws.rs.ext.WriterInterceptor;
\r
34 import javax.ws.rs.ext.WriterInterceptorContext;
\r
36 import java.net.URI;
\r
37 import java.nio.charset.Charset;
\r
38 import java.util.ArrayList;
\r
39 import java.util.Base64;
\r
40 import java.util.List;
\r
41 import java.util.Objects;
\r
42 import java.util.logging.Level;
\r
43 import java.util.logging.Logger;
\r
45 public class OTFLoggingFeature extends LoggingFeature implements ContainerRequestFilter, ContainerResponseFilter,
\r
46 ClientRequestFilter, ClientResponseFilter, WriterInterceptor {
\r
48 private static final boolean printEntity = true;
\r
49 private static final int maxEntitySize = 8 * 1024;
\r
50 private final Logger logger = Logger.getLogger("OTFLoggingFeature");
\r
51 private static final String ENTITY_LOGGER_PROPERTY = OTFLoggingFeature.class.getName();
\r
52 private static final String NOTIFICATION_PREFIX = "* ";
\r
53 private static final String REQUEST_PREFIX = "> ";
\r
54 private static final String RESPONSE_PREFIX = "< ";
\r
55 private static final String AUTHORIZATION = "Authorization";
\r
56 private static final String EQUAL = " = ";
\r
57 private static final String HEADERS_SEPARATOR = ", ";
\r
58 private static List<String> requestHeaders;
\r
61 requestHeaders = new ArrayList<>();
\r
62 requestHeaders.add(AUTHORIZATION);
\r
65 public OTFLoggingFeature(Logger logger, Level level, Verbosity verbosity, Integer maxEntitySize) {
\r
66 super(logger, level, verbosity, maxEntitySize);
\r
70 public boolean configure(FeatureContext context) {
\r
71 context.register(this);
\r
75 private Object getEmail(Object authorization){
\r
77 String encoded = ((String) authorization).split(" ")[1];
\r
78 String decoded = new String(Base64.getDecoder().decode(encoded));
\r
79 return decoded.split(":")[0];
\r
81 catch (Exception e){
\r
82 return authorization;
\r
87 public void filter(final ClientRequestContext context) {
\r
88 final StringBuilder b = new StringBuilder();
\r
89 printHeaders(b, context.getStringHeaders());
\r
90 printRequestLine(b, "Sending client request", context.getMethod(), context.getUri());
\r
92 if (printEntity && context.hasEntity()) {
\r
93 final OutputStream stream = new LoggingStream(b, context.getEntityStream());
\r
94 context.setEntityStream(stream);
\r
95 context.setProperty(ENTITY_LOGGER_PROPERTY, stream);
\r
96 // not calling log(b) here - it will be called by the interceptor
\r
103 public void filter(final ClientRequestContext requestContext, final ClientResponseContext responseContext) throws IOException {
\r
104 final StringBuilder b = new StringBuilder();
\r
105 printResponseLine(b, "Client response received", responseContext.getStatus());
\r
107 if (printEntity && responseContext.hasEntity()) {
\r
108 responseContext.setEntityStream(logInboundEntity(b, responseContext.getEntityStream(),
\r
109 MessageUtils.getCharset(responseContext.getMediaType())));
\r
115 public void filter(final ContainerRequestContext context) throws IOException {
\r
116 final StringBuilder b = new StringBuilder();
\r
117 printHeaders(b, context.getHeaders());
\r
118 printRequestLine(b, "Server has received a request", context.getMethod(), context.getUriInfo().getRequestUri());
\r
120 if (printEntity && context.hasEntity()) {
\r
121 context.setEntityStream(logInboundEntity(b, context.getEntityStream(), MessageUtils.getCharset(context.getMediaType())));
\r
127 public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) {
\r
128 final StringBuilder b = new StringBuilder();
\r
129 printResponseLine(b, "Server responded with a response", responseContext.getStatus());
\r
131 if (printEntity && responseContext.hasEntity()) {
\r
132 final OutputStream stream = new LoggingStream(b, responseContext.getEntityStream());
\r
133 responseContext.setEntityStream(stream);
\r
134 requestContext.setProperty(ENTITY_LOGGER_PROPERTY, stream);
\r
135 // not calling log(b) here - it will be called by the interceptor
\r
142 public void aroundWriteTo(final WriterInterceptorContext writerInterceptorContext) throws IOException, WebApplicationException {
\r
143 final LoggingStream stream = (LoggingStream) writerInterceptorContext.getProperty(ENTITY_LOGGER_PROPERTY);
\r
144 writerInterceptorContext.proceed();
\r
145 if (stream != null) {
\r
146 log(stream.getStringBuilder(MessageUtils.getCharset(writerInterceptorContext.getMediaType())));
\r
150 private static class LoggingStream extends FilterOutputStream {
\r
151 private final StringBuilder b;
\r
152 private final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
\r
154 LoggingStream(final StringBuilder b, final OutputStream inner) {
\r
160 StringBuilder getStringBuilder(Charset charset) {
\r
161 // write entity to the builder
\r
162 final byte[] entity = byteArrayOutputStream.toByteArray();
\r
164 b.append(new String(entity, 0, Math.min(entity.length, maxEntitySize), charset));
\r
165 if (entity.length > maxEntitySize) {
\r
166 b.append("...more...");
\r
173 public void write(final int i) throws IOException {
\r
174 if (byteArrayOutputStream.size() <= maxEntitySize) {
\r
175 byteArrayOutputStream.write(i);
\r
181 private void printHeaders(StringBuilder b, MultivaluedMap<String, String> headers) {
\r
182 for (String header : requestHeaders) {
\r
183 if (Objects.nonNull(headers.get(header))) {
\r
184 if(header.equalsIgnoreCase("Authorization")){
\r
185 b.append(header).append(EQUAL).append(getEmail(headers.get(header).get(0))).append(HEADERS_SEPARATOR);
\r
188 b.append(header).append(EQUAL).append(headers.get(header)).append(HEADERS_SEPARATOR);
\r
192 int lastIndex = b.lastIndexOf(HEADERS_SEPARATOR);
\r
193 if (lastIndex != -1) {
\r
194 b.delete(lastIndex, lastIndex + HEADERS_SEPARATOR.length());
\r
199 private void log(final StringBuilder b) {
\r
200 String message = b.toString();
\r
201 if (logger != null) {
\r
202 logger.info(message);
\r
206 private void printRequestLine(final StringBuilder b, final String note, final String method, final URI uri) {
\r
207 b.append(NOTIFICATION_PREFIX)
\r
209 .append(" on thread ").append(Thread.currentThread().getId())
\r
210 .append(REQUEST_PREFIX).append(method).append(" ")
\r
211 .append(uri.toASCIIString()).append("\n");
\r
214 private void printResponseLine(final StringBuilder b, final String note, final int status) {
\r
215 b.append(NOTIFICATION_PREFIX)
\r
217 .append(" on thread ").append(Thread.currentThread().getId())
\r
218 .append(RESPONSE_PREFIX)
\r
219 .append(Integer.toString(status))
\r
223 private InputStream logInboundEntity(final StringBuilder b, InputStream stream, final Charset charset) throws IOException {
\r
224 if (!stream.markSupported()) {
\r
225 stream = new BufferedInputStream(stream);
\r
227 stream.mark(maxEntitySize + 1);
\r
228 final byte[] entity = new byte[maxEntitySize + 1];
\r
229 final int entitySize = stream.read(entity);
\r
230 b.append(new String(entity, 0, Math.min(entitySize, maxEntitySize), charset));
\r
231 if (entitySize > maxEntitySize) {
\r
232 b.append("...more...");
\r