Update Logging Specifications
[clamp.git] / src / main / java / org / onap / clamp / clds / util / LoggingUtils.java
index 71835fa..a20a700 100644 (file)
 \r
 package org.onap.clamp.clds.util;\r
 \r
+import com.att.eelf.configuration.EELFLogger;\r
+import com.att.eelf.configuration.EELFManager;\r
+\r
+import java.net.HttpURLConnection;\r
 import java.net.InetAddress;\r
 import java.net.UnknownHostException;\r
 import java.text.DateFormat;\r
@@ -30,26 +34,42 @@ import java.text.SimpleDateFormat;
 import java.util.Date;\r
 import java.util.TimeZone;\r
 import java.util.UUID;\r
+import java.time.ZoneOffset;\r
+import java.time.ZonedDateTime;\r
+import java.time.format.DateTimeFormatter;\r
 import javax.validation.constraints.NotNull;\r
+import javax.servlet.http.HttpServletRequest;\r
+\r
 import org.slf4j.MDC;\r
+import org.slf4j.event.Level;\r
+import org.springframework.security.core.context.SecurityContextHolder;\r
 \r
-import com.att.eelf.configuration.EELFLogger;\r
-import com.att.eelf.configuration.EELFManager;\r
+import org.onap.clamp.clds.service.DefaultUserNameHandler;\r
+import org.onap.logging.ref.slf4j.ONAPLogConstants;\r
 \r
 /**\r
  * This class handles the special info that appear in the log, like RequestID,\r
  * time context, ...\r
  */\r
-public final class LoggingUtils {\r
-       \r
-       protected static final EELFLogger logger = EELFManager.getInstance().getLogger(LoggingUtils.class);\r
+public class LoggingUtils {\r
+    protected static final EELFLogger logger = EELFManager.getInstance().getLogger(LoggingUtils.class);\r
 \r
     private static final DateFormat DATE_FORMAT = createDateFormat();\r
 \r
+    /** String constant for messages <tt>ENTERING</tt>, <tt>EXITING</tt>, etc. */\r
+    private static final String EMPTY_MESSAGE = "";\r
+    private static final String INVOCATIONID_OUT = "InvocationIDOut";\r
+    private static final String TARGET_ENTITY = "TargetEngity";\r
+\r
+    /** Logger delegate. */\r
+    private EELFLogger mLogger;\r
+    /** Automatic UUID, overrideable per adapter or per invocation. */\r
+    private static UUID sInstanceUUID = UUID.randomUUID();\r
     /**\r
-     * Private constructor to avoid creating instances of util class.\r
+     * Constructor\r
      */\r
-    private LoggingUtils() {\r
+    public LoggingUtils(final EELFLogger loggerP) {\r
+       this.mLogger = checkNotNull(loggerP);\r
     }\r
 \r
     /**\r
@@ -65,10 +85,10 @@ public final class LoggingUtils {
         //Defaulting to HTTP/1.1 protocol\r
         MDC.put("Protocol", "HTTP/1.1");\r
         try {\r
-               MDC.put("ServerFQDN", InetAddress.getLocalHost().getCanonicalHostName());\r
-               MDC.put("ServerIPAddress", InetAddress.getLocalHost().getHostAddress());\r
+            MDC.put("ServerFQDN", InetAddress.getLocalHost().getCanonicalHostName());\r
+            MDC.put("ServerIPAddress", InetAddress.getLocalHost().getHostAddress());\r
         } catch (UnknownHostException e) {\r
-               logger.error("Failed to initiate setRequestContext", e);\r
+            logger.error("Failed to initiate setRequestContext", e);\r
                }\r
     }\r
 \r
@@ -132,12 +152,10 @@ public final class LoggingUtils {
      * @return A string with the request ID\r
      */\r
     public static String getRequestId() {\r
-        String requestId;\r
-\r
-        requestId = (String) MDC.get("RequestID");\r
+        String requestId = (String) MDC.get(ONAPLogConstants.MDCs.REQUEST_ID);\r
         if (requestId == null || requestId.isEmpty()) {\r
             requestId = UUID.randomUUID().toString();\r
-            MDC.put("RequestId", requestId);\r
+            MDC.put(ONAPLogConstants.MDCs.REQUEST_ID, requestId);\r
         }\r
         return requestId;\r
     }\r
@@ -147,5 +165,169 @@ public final class LoggingUtils {
         dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));\r
         return dateFormat;\r
     }\r
+    \r
+    \r
+    \r
+    /*********************************************************************************************\r
+     * Method for ONAP Application Logging Specification v1.2\r
+     ********************************************************************************************/\r
+\r
+    /**\r
+     * Report <tt>ENTERING</tt> marker.\r
+     *\r
+     * @param request non-null incoming request (wrapper).\r
+     * @return this.\r
+     */\r
+    public void entering(HttpServletRequest request, String serviceName) {\r
+        checkNotNull(request);\r
+        // Extract MDC values from standard HTTP headers.\r
+        final String requestID = defaultToUUID(request.getHeader(ONAPLogConstants.Headers.REQUEST_ID));\r
+        final String invocationID = defaultToUUID(request.getHeader(ONAPLogConstants.Headers.INVOCATION_ID));\r
+        final String partnerName = defaultToEmpty(request.getHeader(ONAPLogConstants.Headers.PARTNER_NAME));\r
+        \r
+        // Default the partner name to the user name used to login to clamp\r
+        if (partnerName.equalsIgnoreCase(EMPTY_MESSAGE)) {\r
+            MDC.put(ONAPLogConstants.MDCs.PARTNER_NAME, new DefaultUserNameHandler().retrieveUserName(SecurityContextHolder.getContext()));\r
+        }\r
+\r
+        // Set standard MDCs. Override this entire method if you want to set\r
+        // others, OR set them BEFORE or AFTER the invocation of #entering,\r
+        // depending on where you need them to appear, OR extend the\r
+        // ServiceDescriptor to add them.\r
+        MDC.put(ONAPLogConstants.MDCs.ENTRY_TIMESTAMP,\r
+                ZonedDateTime.now(ZoneOffset.UTC)\r
+                        .format(DateTimeFormatter.ISO_INSTANT));\r
+        MDC.put(ONAPLogConstants.MDCs.REQUEST_ID, requestID);\r
+        MDC.put(ONAPLogConstants.MDCs.INVOCATION_ID, invocationID);\r
+        MDC.put(ONAPLogConstants.MDCs.CLIENT_IP_ADDRESS, defaultToEmpty(request.getRemoteAddr()));\r
+        MDC.put(ONAPLogConstants.MDCs.SERVER_FQDN, defaultToEmpty(request.getServerName()));\r
+        MDC.put(ONAPLogConstants.MDCs.INSTANCE_UUID, defaultToEmpty(sInstanceUUID));\r
+\r
+        // Default the service name to the requestURI, in the event that\r
+        // no value has been provided.\r
+        if (serviceName == null ||\r
+                       serviceName.equalsIgnoreCase(EMPTY_MESSAGE)) {\r
+            MDC.put(ONAPLogConstants.MDCs.SERVICE_NAME, request.getRequestURI());\r
+        }\r
+        \r
+        this.mLogger.info("ENTRY");\r
+    }\r
+\r
+    /**\r
+     * Report <tt>EXITING</tt> marker.\r
+     *\r
+     * @return this.\r
+     */\r
+    public void exiting(String code, String descrption, Level severity, ONAPLogConstants.ResponseStatus status) {\r
+        try {\r
+               MDC.put(ONAPLogConstants.MDCs.RESPONSE_CODE, defaultToEmpty(code));\r
+            MDC.put(ONAPLogConstants.MDCs.RESPONSE_DESCRIPTION, defaultToEmpty(descrption));\r
+            MDC.put(ONAPLogConstants.MDCs.RESPONSE_SEVERITY, defaultToEmpty(severity));\r
+            MDC.put(ONAPLogConstants.MDCs.RESPONSE_STATUS_CODE, defaultToEmpty(status));\r
+            this.mLogger.info("EXIT");\r
+        }\r
+        finally {\r
+            MDC.clear();\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Report pending invocation with <tt>INVOKE</tt> marker,\r
+     * setting standard ONAP logging headers automatically.\r
+     *\r
+     * @param builder request builder, for setting headers.\r
+     * @param sync whether synchronous, nullable.\r
+     * @return invocation ID to be passed with invocation.\r
+     */\r
+    public HttpURLConnection invoke(final HttpURLConnection con, String targetEntity, String targetServiceName) {\r
+        final String invocationID = UUID.randomUUID().toString();\r
+\r
+        // Set standard HTTP headers on (southbound request) builder.\r
+        con.setRequestProperty(ONAPLogConstants.Headers.REQUEST_ID,\r
+                defaultToEmpty(MDC.get(ONAPLogConstants.MDCs.REQUEST_ID)));\r
+        con.setRequestProperty(ONAPLogConstants.Headers.INVOCATION_ID,\r
+                       invocationID);\r
+        con.setRequestProperty(ONAPLogConstants.Headers.PARTNER_NAME,\r
+                defaultToEmpty(MDC.get(ONAPLogConstants.MDCs.PARTNER_NAME)));\r
+\r
+        invokeContext(targetEntity, targetServiceName, invocationID);\r
+\r
+        // Log INVOKE*, with the invocationID as the message body.\r
+        // (We didn't really want this kind of behavior in the standard,\r
+        // but is it worse than new, single-message MDC?)\r
+        this.mLogger.info("INVOKE");\r
+        this.mLogger.info("INVOKE-" + ONAPLogConstants.InvocationMode.SYNCHRONOUS.toString() + "{"+ invocationID +"}");\r
+        return con;\r
+    }\r
+    public void invokeReturn() {\r
+        // Add the Invoke-return marker and clear the needed MDC\r
+        this.mLogger.info("INVOKE-RETURN");\r
+        invokeReturnContext();\r
+    }\r
+\r
+    /**\r
+     * Dependency-free nullcheck.\r
+     *\r
+     * @param in to be checked.\r
+     * @param <T> argument (and return) type.\r
+     * @return input arg.\r
+     */\r
+    private static <T> T checkNotNull(final T in) {\r
+        if (in == null) {\r
+            throw new NullPointerException();\r
+        }\r
+        return in;\r
+    }\r
+\r
+    /**\r
+     * Dependency-free string default.\r
+     *\r
+     * @param in to be filtered.\r
+     * @return input string or null.\r
+     */\r
+    private static String defaultToEmpty(final Object in) {\r
+        if (in == null) {\r
+            return "";\r
+        }\r
+        return in.toString();\r
+    }\r
+\r
+    /**\r
+     * Dependency-free string default.\r
+     *\r
+     * @param in to be filtered.\r
+     * @return input string or null.\r
+     */\r
+    private static String defaultToUUID(final String in) {\r
+        if (in == null) {\r
+            return UUID.randomUUID().toString();\r
+        }\r
+        return in;\r
+    }\r
+\r
+    /**\r
+     * Set target related logging variables in thread local data via MDC\r
+     *\r
+     * @param targetEntity Target entity (an external/sub component, for ex. "sdc")\r
+     * @param targetServiceName Target service name (name of API invoked on target)\r
+     * @param invocationId The invocation ID\r
+     */\r
+    private void invokeContext (String targetEntity, String targetServiceName, String invocationID) {\r
+        MDC.put(TARGET_ENTITY, defaultToEmpty(targetEntity));\r
+        MDC.put(ONAPLogConstants.MDCs.TARGET_SERVICE_NAME, defaultToEmpty(targetServiceName));\r
+        MDC.put(INVOCATIONID_OUT, invocationID);\r
+        MDC.put(ONAPLogConstants.MDCs.INVOKE_TIMESTAMP,\r
+                ZonedDateTime.now(ZoneOffset.UTC)\r
+                        .format(DateTimeFormatter.ISO_INSTANT));\r
+    }\r
 \r
+    /**\r
+     * Clear target related logging variables in thread local data via MDC\r
+     *\r
+     */\r
+    private void invokeReturnContext () {\r
+        MDC.remove(TARGET_ENTITY);\r
+        MDC.remove(ONAPLogConstants.MDCs.TARGET_SERVICE_NAME);\r
+        MDC.remove(INVOCATIONID_OUT);\r
+    }\r
 }\r