Merge "[AAI] Fix doc config files"
[aai/aai-common.git] / aai-els-onap-logging / src / main / java / org / onap / logging / ref / slf4j / ONAPLogAdapter.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.logging
4  * ================================================================================
5  * Copyright © 2018 Amdocs
6  * All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *    http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.logging.ref.slf4j;
23
24 import java.time.ZoneOffset;
25 import java.time.ZonedDateTime;
26 import java.time.format.DateTimeFormatter;
27 import java.util.UUID;
28
29 import javax.servlet.http.HttpServletRequest;
30
31 import org.slf4j.Logger;
32 import org.slf4j.MDC;
33 import org.slf4j.Marker;
34 import org.slf4j.event.Level;
35
36 /**
37  * Extensible adapter for cheaply meeting ONAP logging obligations using
38  * an SLF4J facade.
39  *
40  * <p>
41  * This can be used with any SLF4J-compatible logging provider, with
42  * appropriate provider configuration.
43  * </p>
44  *
45  * <p>
46  * The basics are that:
47  * <ul>
48  * <li>{@link #entering} sets all MDCs.</li>
49  * <li>{@link #exiting} unsets all MDCs *and* logs response information.</li>
50  * <li>{@link #invoke} logs and returns a UUID to passed during invocation,
51  * and optionally sets these for you on your downstream request by way of
52  * an adapter.</li>
53  * <li>Call {@link #getServiceDescriptor()} and its setters to set service-related MDCs.</li>
54  * <li>Call {@link #getResponseDescriptor()} and its setters to set response-related MDCs.</li>
55  * </ul>
56  * </p>
57  *
58  * <p>
59  * Minimal usage is:
60  * <ol>
61  * <li>#entering(RequestAdapter)</li>
62  * <li>#invoke, #invoke, ...</li>
63  * <li>#getResponse + setters (or #setResponse)</li>
64  * <li>#exiting</li>
65  * </ol>
66  * </p>
67  *
68  * <p>
69  * ... if you're happy for service information to be automatically derived as follows:
70  * <ul>
71  * <li><tt>ServiceName</tt> - from <tt>HttpServletRequest#getRequestURI()</tt></li>
72  * <li><tt>InstanceUUID</tt> - classloader-scope UUID.</li>
73  * </ul>
74  * </p>
75  *
76  * <p>
77  * ... and if those defaults don't suit, then you can override using properties on
78  * {@link #getServiceDescriptor()}, or by injecting your own adapter using
79  * {@link #setServiceDescriptor(ServiceDescriptor)}, or by overriding
80  * a <tt>protected</tt> methods like{@link #setEnteringMDCs}.
81  * </p>
82  *
83  * <p>
84  * For everything else:
85  * <ul>
86  * <li>The underlying SLF4J {@link Logger} can be retrieved using {@link #unwrap}.
87  * Use this or create your own using the usual SLF4J factor.</li>
88  * <li>Set whatever MDCs you like.</li>
89  * <li>Log whatever else you like.</li>
90  * </ul>
91  * </p>
92  */
93 public class ONAPLogAdapter {
94
95     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
96     //
97     // Constants.
98     //
99     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
100
101     /** String constant for messages <tt>ENTERING</tt>, <tt>EXITING</tt>, etc. */
102     private static final String EMPTY_MESSAGE = "";
103
104     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
105     //
106     // Fields.
107     //
108     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
109
110     /** Automatic UUID, overrideable per adapter or per invocation. */
111     private static UUID sInstanceUUID = UUID.randomUUID();
112
113     /** Logger delegate. */
114     private Logger mLogger;
115
116     /** Overrideable descriptor for the service doing the logging. */
117     private ServiceDescriptor mServiceDescriptor = new ServiceDescriptor();
118
119     /** Overrideable descriptor for the response returned by the service doing the logging. */
120     private ResponseDescriptor mResponseDescriptor = new ResponseDescriptor();
121
122     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
123     //
124     // Constructors.
125     //
126     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
127
128     /**
129      * Construct adapter.
130      *
131      * @param logger non-null logger.
132      */
133     public ONAPLogAdapter(final Logger logger) {
134         this.mLogger = checkNotNull(logger);
135     }
136
137     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
138     //
139     // Public methods.
140     //
141     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
142
143     /**
144      * Get logger.
145      *
146      * @return unwrapped logger.
147      */
148     public Logger unwrap() {
149         return this.mLogger;
150     }
151
152     /**
153      * Report <tt>ENTERING</tt> marker.
154      *
155      * @param request non-null incoming request (wrapper).
156      * @return this.
157      */
158     public ONAPLogAdapter entering(final RequestAdapter request) {
159
160         checkNotNull(request);
161
162         // Default the service name.
163
164         this.setEnteringMDCs(request);
165         this.mLogger.info(ONAPLogConstants.Markers.ENTRY, EMPTY_MESSAGE);
166
167         return this;
168     }
169
170     /**
171      * Report <tt>ENTERING</tt> marker.
172      *
173      * @param request non-null incoming request.
174      * @return this.
175      */
176     public ONAPLogAdapter entering(final HttpServletRequest request) {
177         return this.entering(new HttpServletRequestAdapter(checkNotNull(request)));
178     }
179
180     /**
181      * Report <tt>EXITING</tt> marker.
182      *
183      * @return this.
184      */
185     public ONAPLogAdapter exiting() {
186         try {
187             this.mResponseDescriptor.setMDCs();
188             this.mLogger.info(ONAPLogConstants.Markers.EXIT, EMPTY_MESSAGE);
189         } finally {
190             MDC.clear();
191         }
192         return this;
193     }
194
195     /**
196      * Report pending invocation with <tt>INVOKE</tt> marker.
197      *
198      * <p>
199      * If you call this variant, then YOU are assuming responsibility for
200      * setting the requisite ONAP headers.
201      * </p>
202      *
203      * @param sync whether synchronous.
204      * @return invocation ID to be passed with invocation.
205      */
206     public UUID invoke(final ONAPLogConstants.InvocationMode sync) {
207
208         final UUID invocationID = UUID.randomUUID();
209
210         // Derive SYNC/ASYNC marker.
211
212         final Marker marker = (sync == null) ? ONAPLogConstants.Markers.INVOKE : sync.getMarker();
213
214         // Log INVOKE*, with the invocationID as the message body.
215         // (We didn't really want this kind of behavior in the standard,
216         // but is it worse than new, single-message MDC?)
217
218         this.mLogger.info(marker, "{}", invocationID);
219         return invocationID;
220     }
221
222     /**
223      * Report pending invocation with <tt>INVOKE</tt> marker,
224      * setting standard ONAP logging headers automatically.
225      *
226      * @param builder request builder, for setting headers.
227      * @param sync whether synchronous, nullable.
228      * @return invocation ID to be passed with invocation.
229      */
230     public UUID invoke(final RequestBuilder builder, final ONAPLogConstants.InvocationMode sync) {
231
232         // Sync can be defaulted. Builder cannot.
233
234         checkNotNull(builder);
235
236         // Log INVOKE, and retain invocation ID for header + return.
237
238         final UUID invocationID = this.invoke(sync);
239
240         // Set standard HTTP headers on (southbound request) builder.
241
242         builder.setHeader(ONAPLogConstants.Headers.REQUEST_ID,
243                 defaultToEmpty(MDC.get(ONAPLogConstants.MDCs.REQUEST_ID)));
244         builder.setHeader(ONAPLogConstants.Headers.INVOCATION_ID, defaultToEmpty(invocationID));
245         builder.setHeader(ONAPLogConstants.Headers.PARTNER_NAME,
246                 defaultToEmpty(MDC.get(ONAPLogConstants.MDCs.PARTNER_NAME)));
247
248         return invocationID;
249     }
250
251     /**
252      * Report vanilla <tt>INVOKE</tt> marker.
253      *
254      * @param builder builder for downstream requests, if you want the
255      *        standard ONAP headers to be added automatically.
256      * @return invocation ID to be passed with invocation.
257      */
258     public UUID invoke(final RequestBuilder builder) {
259         return this.invoke(builder, (ONAPLogConstants.InvocationMode) null);
260     }
261
262     /**
263      * Get descriptor, for overriding service details.
264      * 
265      * @return non-null descriptor.
266      */
267     public ServiceDescriptor getServiceDescriptor() {
268         return checkNotNull(this.mServiceDescriptor);
269     }
270
271     /**
272      * Override {@link ServiceDescriptor}.
273      * 
274      * @param d non-null override.
275      * @return this.
276      */
277     public ONAPLogAdapter setServiceDescriptor(final ServiceDescriptor d) {
278         this.mServiceDescriptor = checkNotNull(d);
279         return this;
280     }
281
282     /**
283      * Get descriptor, for setting response details.
284      * 
285      * @return non-null descriptor.
286      */
287     public ResponseDescriptor getResponseDescriptor() {
288         return checkNotNull(this.mResponseDescriptor);
289     }
290
291     /**
292      * Override {@link ResponseDescriptor}.
293      * 
294      * @param d non-null override.
295      * @return this.
296      */
297     public ONAPLogAdapter setResponseDescriptor(final ResponseDescriptor d) {
298         this.mResponseDescriptor = checkNotNull(d);
299         return this;
300     }
301
302     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
303     //
304     // Protected methods.
305     //
306     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
307
308     /**
309      * Set MDCs that persist for the duration of an invocation.
310      *
311      * <p>
312      * It would be better to roll this into {@link #entering}, like
313      * with {@link #exiting}. Then it would be easier to do, but it
314      * would mean more work.
315      * </p>
316      *
317      * @param request incoming HTTP request.
318      * @return this.
319      */
320     protected ONAPLogAdapter setEnteringMDCs(final RequestAdapter<?> request) {
321
322         // Extract MDC values from standard HTTP headers.
323
324         final String requestID = defaultToUUID(request.getHeader(ONAPLogConstants.Headers.REQUEST_ID));
325         final String invocationID = defaultToUUID(request.getHeader(ONAPLogConstants.Headers.INVOCATION_ID));
326         final String partnerName = defaultToEmpty(request.getHeader(ONAPLogConstants.Headers.PARTNER_NAME));
327
328         // Set standard MDCs. Override this entire method if you want to set
329         // others, OR set them BEFORE or AFTER the invocation of #entering,
330         // depending on where you need them to appear, OR extend the
331         // ServiceDescriptor to add them.
332
333         MDC.put(ONAPLogConstants.MDCs.INVOKE_TIMESTAMP,
334                 ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT));
335         MDC.put(ONAPLogConstants.MDCs.REQUEST_ID, requestID);
336         MDC.put(ONAPLogConstants.MDCs.INVOCATION_ID, invocationID);
337         MDC.put(ONAPLogConstants.MDCs.PARTNER_NAME, partnerName);
338         MDC.put(ONAPLogConstants.MDCs.CLIENT_IP_ADDRESS, defaultToEmpty(request.getClientAddress()));
339         MDC.put(ONAPLogConstants.MDCs.SERVER_FQDN, defaultToEmpty(request.getServerAddress()));
340
341         // Delegate to the service adapter, for service-related DMCs.
342
343         this.mServiceDescriptor.setMDCs();
344
345         // Default the service name to the requestURI, in the event that
346         // no value has been provided.
347
348         if (MDC.get(ONAPLogConstants.MDCs.SERVICE_NAME) == null
349                 || MDC.get(ONAPLogConstants.MDCs.SERVICE_NAME).equalsIgnoreCase(EMPTY_MESSAGE)) {
350             MDC.put(ONAPLogConstants.MDCs.SERVICE_NAME, request.getRequestURI());
351         }
352
353         return this;
354     }
355
356     /**
357      * Dependency-free nullcheck.
358      *
359      * @param in to be checked.
360      * @param <T> argument (and return) type.
361      * @return input arg.
362      */
363     protected static <T> T checkNotNull(final T in) {
364         if (in == null) {
365             throw new NullPointerException();
366         }
367         return in;
368     }
369
370     /**
371      * Dependency-free string default.
372      *
373      * @param in to be filtered.
374      * @return input string or null.
375      */
376     protected static String defaultToEmpty(final Object in) {
377         if (in == null) {
378             return "";
379         }
380         return in.toString();
381     }
382
383     /**
384      * Dependency-free string default.
385      *
386      * @param in to be filtered.
387      * @return input string or null.
388      */
389     protected static String defaultToUUID(final String in) {
390         if (in == null) {
391             return UUID.randomUUID().toString();
392         }
393         return in;
394     }
395
396     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
397     //
398     // Inner classes.
399     //
400     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
401
402     /**
403      * Extensible descriptor for reporting service details.
404      *
405      * <p>
406      * In most cases extension isn't required.
407      * </p>
408      */
409     public static class ServiceDescriptor {
410
411         /** <tt>ServiceName</tt>. */
412         protected String mName;
413
414         /** <tt>InstanceUUID</tt>. */
415         protected String mUUID = sInstanceUUID.toString();
416
417         /**
418          * Set name.
419          * 
420          * @param name <tt>ServiceName</tt>.
421          * @return this.
422          */
423         public ServiceDescriptor setServiceName(final String name) {
424             this.mName = name;
425             return this;
426         }
427
428         /**
429          * Set name.
430          * 
431          * @param uuid <tt>InstanceUUID</tt>.
432          * @return this.
433          */
434         public ServiceDescriptor setServiceUUID(final String uuid) {
435             this.mUUID = uuid;
436             return this;
437         }
438
439         /**
440          * Set MDCs. Once set they remain set until everything is cleared.
441          */
442         protected void setMDCs() {
443             MDC.put(ONAPLogConstants.MDCs.SERVICE_NAME, defaultToEmpty(this.mName));
444             MDC.put(ONAPLogConstants.MDCs.INSTANCE_UUID, defaultToEmpty(this.mUUID));
445         }
446     }
447
448     /**
449      * Response is different in that response MDCs are normally only
450      * reported once, for a single log message. (But there's no method
451      * for clearing them, because this is only expected to be called
452      * during <tt>#exiting</tt>.)
453      */
454     public static class ResponseDescriptor {
455
456         /** Response errorcode. */
457         protected String mCode;
458
459         /** Response description. */
460         protected String mDescription;
461
462         /** Response severity. */
463         protected Level mSeverity;
464
465         /** Response status, of {<tt>COMPLETED</tt>, <tt>ERROR</tt>}. */
466         protected ONAPLogConstants.ResponseStatus mStatus;
467
468         /**
469          * Setter.
470          *
471          * @param code response (error) code.
472          * @return this.
473          */
474         public ResponseDescriptor setResponseCode(final String code) {
475             this.mCode = code;
476             return this;
477         }
478
479         /**
480          * Setter.
481          *
482          * @param description response description.
483          * @return this.
484          */
485         public ResponseDescriptor setResponseDescription(final String description) {
486             this.mDescription = description;
487             return this;
488         }
489
490         /**
491          * Setter.
492          *
493          * @param severity response outcome severity.
494          * @return this.
495          */
496         public ResponseDescriptor setResponseSeverity(final Level severity) {
497             this.mSeverity = severity;
498             return this;
499         }
500
501         /**
502          * Setter.
503          *
504          * @param status response overall status.
505          * @return this.
506          */
507         public ResponseDescriptor setResponseStatus(final ONAPLogConstants.ResponseStatus status) {
508             this.mStatus = status;
509             return this;
510         }
511
512         /**
513          * Overrideable method to set MDCs based on property values.
514          */
515         protected void setMDCs() {
516             MDC.put(ONAPLogConstants.MDCs.RESPONSE_CODE, defaultToEmpty(this.mCode));
517             MDC.put(ONAPLogConstants.MDCs.RESPONSE_DESCRIPTION, defaultToEmpty(this.mDescription));
518             MDC.put(ONAPLogConstants.MDCs.RESPONSE_SEVERITY, defaultToEmpty(this.mSeverity));
519             MDC.put(ONAPLogConstants.MDCs.RESPONSE_STATUS_CODE, defaultToEmpty(this.mStatus));
520         }
521     }
522
523     /**
524      * Adapter for reading information from an incoming HTTP request.
525      *
526      * <p>
527      * Incoming is generally easy, because in most cases you'll be able to
528      * get your hands on the <tt>HttpServletRequest</tt>.
529      * </p>
530      *
531      * <p>
532      * Perhaps should be generalized to refer to constants instead of
533      * requiring the implementation of specific methods.
534      * </p>
535      *
536      * @param <T> type, for chaining.
537      */
538     public interface RequestAdapter<T extends RequestAdapter> {
539
540         /**
541          * Get header by name.
542          * 
543          * @param name header name.
544          * @return header value, or null.
545          */
546         String getHeader(String name);
547
548         /**
549          * Get client address.
550          * 
551          * @return address, if available.
552          */
553         String getClientAddress();
554
555         /**
556          * Get server address.
557          * 
558          * @return address, if available.
559          */
560         String getServerAddress();
561
562         /**
563          * Get default service name, from service URI.
564          * 
565          * @return service name default.
566          */
567         String getRequestURI();
568     }
569
570     /**
571      * Default {@link RequestBuilder} impl for {@link HttpServletRequest}, which
572      * will should available for most incoming REST requests.
573      */
574     public static class HttpServletRequestAdapter implements RequestAdapter<HttpServletRequestAdapter> {
575
576         /** Wrapped HTTP request. */
577         private final HttpServletRequest mRequest;
578
579         /**
580          * Construct adapter for HTTP request.
581          * 
582          * @param request to be wrapped;
583          */
584         public HttpServletRequestAdapter(final HttpServletRequest request) {
585             this.mRequest = checkNotNull(request);
586         }
587
588         /**
589          * {@inheritDoc}
590          */
591         @Override
592         public String getHeader(final String name) {
593             return this.mRequest.getHeader(name);
594         }
595
596         /**
597          * {@inheritDoc}
598          */
599         @Override
600         public String getClientAddress() {
601             return this.mRequest.getRemoteAddr();
602         }
603
604         /**
605          * {@inheritDoc}
606          */
607         @Override
608         public String getServerAddress() {
609             return this.mRequest.getServerName();
610         }
611
612         /**
613          * {@inheritDoc}
614          */
615         @Override
616         public String getRequestURI() {
617             return this.mRequest.getRequestURI();
618         }
619     }
620
621     /**
622      * Header builder, which (unlike {@link RequestAdapter} will tend to
623      * vary a lot from caller to caller, since they each get to choose their
624      * own REST (or HTTP, or whatever) client APIs.
625      *
626      * <p>
627      * No default implementation, because there's no HTTP client that's
628      * sufficiently ubiquitous to warrant incurring a mandatory dependency.
629      * </p>
630      *
631      * @param <T> type, for chaining.
632      */
633     public interface RequestBuilder<T extends RequestBuilder> {
634
635         /**
636          * Set HTTP header.
637          * 
638          * @param name header name.
639          * @param value header value.
640          * @return this.
641          */
642         T setHeader(String name, String value);
643     }
644 }