50af1c68cd7338ba3ba6094c7919a1c6dadc7963
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / logging / LoggingContext.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *    http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20 package org.onap.aai.logging;
21
22 import java.net.InetAddress;
23 import java.net.UnknownHostException;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.Map;
27 import java.util.UUID;
28 import java.util.concurrent.TimeUnit;
29
30 import org.json.JSONArray;
31 import org.json.JSONException;
32 import org.json.JSONObject;
33 import org.onap.aai.exceptions.AAIException;
34 import org.slf4j.MDC;
35
36 import com.att.eelf.configuration.EELFLogger;
37 import com.att.eelf.configuration.EELFManager;
38
39 public class LoggingContext {
40
41         public enum StatusCode {
42                 COMPLETE,
43                 ERROR
44         }
45
46         private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(LoggingContext.class);
47
48         private static final String PREVIOUS_CONTEXTS_KEY = "_PREVIOUS_CONTEXTS";
49         
50         //Response codes from Logging Guidelines
51         public static final String SUCCESS = "0";
52         public static final String PERMISSION_ERROR = "100";
53         public static final String AVAILABILITY_TIMEOUT_ERROR = "200";
54         public static final String DATA_ERROR = "300";
55         public static final String SCHEMA_ERROR = "400";
56         public static final String BUSINESS_PROCESS_ERROR = "500";
57         public static final String UNKNOWN_ERROR = "900";
58         
59         public static final Map<String, String> responseMap = new HashMap();
60          
61     static {
62         responseMap.put(SUCCESS, "Success");
63         responseMap.put(UNKNOWN_ERROR, "Unknown error");
64     }
65         // Specific Log Event Fields
66         public static enum LoggingField {
67                 START_TIME("startTime"),
68                 REQUEST_ID("requestId"),
69                 SERVICE_INSTANCE_ID("serviceInstanceId"),
70                 SERVER_NAME("serverName"),
71                 SERVICE_NAME("serviceName"),
72                 PARTNER_NAME("partnerName"),
73                 STATUS_CODE("statusCode"),
74                 RESPONSE_CODE("responseCode"),
75                 RESPONSE_DESCRIPTION("responseDescription"),
76                 INSTANCE_UUID("instanceUUID"),
77                 SEVERITY("severity"),
78                 SERVER_IP_ADDRESS("serverIpAddress"),
79                 ELAPSED_TIME("elapsedTime"),
80                 SERVER("server"),
81                 CLIENT_IP_ADDRESS("clientIpAddress"),
82                 UNUSED("unused"),
83                 PROCESS_KEY("processKey"),
84                 CUSTOM_FIELD_1("customField1"),
85                 CUSTOM_FIELD_2("customField2"),
86                 CUSTOM_FIELD_3("customField3"),
87                 CUSTOM_FIELD_4("customField4"),
88                 
89                 // Specific Metric Log Event Fields
90                 TARGET_ENTITY("targetEntity"),
91                 TARGET_SERVICE_NAME("targetServiceName"),
92                 //A&AI Specific Log Event Fields
93                 COMPONENT("component"),
94                 STOP_WATCH_START("stopWatchStart");
95
96                 private final String text;
97
98                 private LoggingField(final String text) {
99                         this.text = text;
100                 }
101
102                 public String toString() {
103                         return text;
104                 }
105         }
106
107
108         public static void init() {
109                 LoggingContext.clear();
110                 LoggingContext.startTime();
111                 LoggingContext.server();
112                 LoggingContext.serverIpAddress();
113         }
114
115         public static void startTime() {
116                 MDC.put(LoggingField.START_TIME.toString(), LogFormatTools.getCurrentDateTime());
117         }
118
119         public static UUID requestId() {
120                 final String sUuid = MDC.get(LoggingField.REQUEST_ID.toString());
121
122                 if (sUuid == null) return null;
123
124                 return UUID.fromString(sUuid);
125         }
126
127         public static void requestId(UUID requestId) {
128                 MDC.put(LoggingField.REQUEST_ID.toString(), requestId.toString());
129         }
130
131         public static void requestId(String requestId) {
132                 try {
133                         if (requestId.contains(":")) {
134                                 String[] uuidParts = requestId.split(":");
135                                 requestId = uuidParts[0];
136                         }
137                         MDC.put(LoggingField.REQUEST_ID.toString(), UUID.fromString(requestId).toString());
138                 } catch (IllegalArgumentException e) {
139                         final UUID generatedRequestUuid = UUID.randomUUID();
140                         MDC.put(LoggingField.REQUEST_ID.toString(), generatedRequestUuid.toString());
141                         LoggingContext.save();
142                         // set response code to 0 since we don't know what the outcome of this request is yet
143                         String responseCode = LoggingContext.DATA_ERROR;
144                         LoggingContext.responseCode(responseCode);
145                         LoggingContext.responseDescription("Unable to use UUID " + requestId + " (Not formatted properly) ");
146                         LoggingContext.statusCode(StatusCode.ERROR);
147                         
148                         LOGGER.warn("Using generated UUID=" + generatedRequestUuid);
149                         LoggingContext.restore();
150
151                 }
152         }
153
154         public static void serviceInstanceId(String serviceInstanceId) {
155                 MDC.put(LoggingField.SERVICE_INSTANCE_ID.toString(), serviceInstanceId);
156         }
157
158         public static void serverName(String serverName) {
159                 MDC.put(LoggingField.SERVER_NAME.toString(), serverName);
160         }
161
162         public static void serviceName(String serviceName) {
163                 MDC.put(LoggingField.SERVICE_NAME.toString(), serviceName);
164         }
165
166         public static void partnerName(String partnerName) {
167                 MDC.put(LoggingField.PARTNER_NAME.toString(), partnerName);
168         }
169
170         public static void statusCode(StatusCode statusCode) {
171                 MDC.put(LoggingField.STATUS_CODE.toString(), statusCode.toString());
172         }
173
174         public static String responseCode() {
175                 return (String) MDC.get(LoggingField.RESPONSE_CODE.toString());
176         }
177
178         public static void responseCode(String responseCode) {
179                 MDC.put(LoggingField.RESPONSE_CODE.toString(), responseCode);
180         }
181
182         public static void responseDescription(String responseDescription) {
183                 MDC.put(LoggingField.RESPONSE_DESCRIPTION.toString(), responseDescription);
184         }
185
186         public static Object instanceUuid() {
187                 return UUID.fromString(MDC.get(LoggingField.INSTANCE_UUID.toString()));
188         }
189
190         public static void instanceUuid(UUID instanceUuid) {
191                 MDC.put(LoggingField.INSTANCE_UUID.toString(), instanceUuid.toString());
192         }
193
194         public static void severity(int severity) {
195                 MDC.put(LoggingField.SEVERITY.toString(), String.valueOf(severity));
196         }
197
198         public static void successStatusFields() {
199                 responseCode(SUCCESS);
200                 statusCode(StatusCode.COMPLETE);
201                 responseDescription("Success");
202         }
203         private static void serverIpAddress() {
204                 try {
205                         MDC.put(LoggingField.SERVER_IP_ADDRESS.toString(), InetAddress.getLocalHost().getHostAddress());
206                 } catch (UnknownHostException e) {
207                         LOGGER.warn("Unable to resolve server IP address - will not be displayed in logged events");
208                 }
209         }
210
211         public static void elapsedTime(long elapsedTime, TimeUnit timeUnit) {
212                 MDC.put(LoggingField.ELAPSED_TIME.toString(), String.valueOf(TimeUnit.MILLISECONDS.convert(elapsedTime, timeUnit)));
213         }
214
215         private static void server() {
216                 try {
217                         MDC.put(LoggingField.SERVER.toString(),  InetAddress.getLocalHost().getCanonicalHostName());
218                 } catch (UnknownHostException e) {
219                         LOGGER.warn("Unable to resolve server IP address - hostname will not be displayed in logged events");
220                 }
221         }
222
223         public static void clientIpAddress(InetAddress clientIpAddress) {
224                 MDC.put(LoggingField.CLIENT_IP_ADDRESS.toString(), clientIpAddress.getHostAddress());
225         }
226
227         public static void clientIpAddress(String clientIpAddress) {
228                 try {
229                         MDC.put(LoggingField.CLIENT_IP_ADDRESS.toString(), InetAddress.getByName(clientIpAddress).getHostAddress());
230                 } catch (UnknownHostException e) {
231                         //Ignore, will not be thrown since InetAddress.getByName(String) only
232                         //checks the validity of the passed in string
233                 }
234         }
235
236         public static void unused(String unused) {
237                 LOGGER.warn("Using field '" + LoggingField.UNUSED + "' (seems like this should go unused...)");
238                 MDC.put(LoggingField.UNUSED.toString(), unused);
239         }
240
241         public static void processKey(String processKey) {
242                 MDC.put(LoggingField.PROCESS_KEY.toString(), processKey);
243         }
244
245         public static String customField1() {
246                 return MDC.get(LoggingField.CUSTOM_FIELD_1.toString());
247         }
248
249         public static void customField1(String customField1) {
250                 MDC.put(LoggingField.CUSTOM_FIELD_1.toString(), customField1);
251         }
252
253         public static void customField2(String customField2) {
254                 MDC.put(LoggingField.CUSTOM_FIELD_2.toString(), customField2);
255         }
256
257         public static void customField3(String customField3) {
258                 MDC.put(LoggingField.CUSTOM_FIELD_3.toString(), customField3);
259         }
260
261         public static void customField4(String customField4) {
262                 MDC.put(LoggingField.CUSTOM_FIELD_4.toString(), customField4);
263         }
264
265         public static void component(String component) {
266                 MDC.put(LoggingField.COMPONENT.toString(), component);
267         }
268
269         public static void targetEntity(String targetEntity) {
270                 MDC.put(LoggingField.TARGET_ENTITY.toString(), targetEntity);
271         }
272
273         public static void targetServiceName(String targetServiceName) {
274                 MDC.put(LoggingField.TARGET_SERVICE_NAME.toString(), targetServiceName);
275         }
276
277         public static boolean isStopWatchStarted() {
278                 final String rawStopWatchStart = MDC.get(LoggingField.STOP_WATCH_START.toString());
279                 if (rawStopWatchStart == null) {
280                         return false;
281                 }
282                 return true;
283         }
284         public static void stopWatchStart() {
285                 MDC.put(LoggingField.STOP_WATCH_START.toString(), String.valueOf(System.nanoTime()));
286         }
287
288         public static double stopWatchStop() {
289                 final long stopWatchEnd = System.nanoTime();
290                 final String rawStopWatchStart = MDC.get(LoggingField.STOP_WATCH_START.toString());
291
292                 if (rawStopWatchStart == null) throw new StopWatchNotStartedException();
293
294                 final Long stopWatchStart = Long.valueOf(rawStopWatchStart);
295
296                 MDC.remove(LoggingField.STOP_WATCH_START.toString());
297
298                 final double elapsedTimeMillis = (stopWatchEnd - stopWatchStart) / 1000.0 / 1000.0;
299
300                 LoggingContext.elapsedTime((long) elapsedTimeMillis, TimeUnit.MILLISECONDS);
301
302                 return elapsedTimeMillis;
303         }
304
305         public static void put(String key, String value) {
306                 MDC.put(key, value);
307         }
308
309         public static void clear() {
310                 MDC.clear();
311         }
312
313         public static void remove(String key) {
314                 MDC.remove(key);
315         }
316
317         public static void save() {
318                 final JSONObject context = new JSONObject();
319
320                 for (LoggingField field : LoggingField.values()) {
321                         if (field == LoggingField.ELAPSED_TIME) continue;
322
323                         try {
324                                 context.put(field.toString(), MDC.get(field.toString()));
325                         } catch (JSONException e) {
326                                 //Ignore - only occurs when the key is null (which can't happen)
327                                 //                      or the value is invalid (everything is converted to a string
328                                 //                      before it get put() to the MDC)
329                         }
330                 }
331
332                 final String rawJsonArray = MDC.get(PREVIOUS_CONTEXTS_KEY);
333
334                 if (rawJsonArray == null) {
335                         final JSONArray stack = new JSONArray()
336                                                                                         .put(context);
337
338                         MDC.put(PREVIOUS_CONTEXTS_KEY, stack.toString());
339                 } else {
340                         try {
341                                 final JSONArray stack = new JSONArray(rawJsonArray)
342                                                                                                 .put(context);
343
344                                 MDC.put(PREVIOUS_CONTEXTS_KEY, stack.toString());
345                         } catch (JSONException e) {
346                                 //Ignore
347                         }
348                 }
349         }
350
351         public static void restore() {
352                 
353                 final String rawPreviousContexts = MDC.get(PREVIOUS_CONTEXTS_KEY);
354         
355                 if (rawPreviousContexts == null) {
356                         throw new LoggingContextNotExistsException();
357                 }
358
359                 try {
360                         final JSONArray previousContexts = new JSONArray(rawPreviousContexts);
361                         final JSONObject previousContext = previousContexts.getJSONObject(previousContexts.length() - 1);
362
363                         @SuppressWarnings("unchecked")
364                         final Iterator<String> keys = previousContext.keys();
365                         boolean foundElapsedTime = false;
366                         while (keys.hasNext()) {
367                                 final String key = keys.next();
368                                 if (LoggingField.ELAPSED_TIME.toString().equals(key)) {
369                                         foundElapsedTime = true;
370                                 }
371                                 try {
372                                         MDC.put(key, previousContext.getString(key));
373                                 } catch (JSONException e) {
374                                         //Ignore, only occurs when the key is null (cannot happen)
375                                         //                      or the value is invalid (they are all strings)
376                                 }
377                         }
378                         if ( !foundElapsedTime ) {
379                                 MDC.remove(LoggingField.ELAPSED_TIME.toString());
380                         }
381                         MDC.put(PREVIOUS_CONTEXTS_KEY, removeLast(previousContexts).toString());
382                 } catch (JSONException e) {
383                         //Ignore, the previousContext is serialized from a JSONObject
384                 }
385         }
386         public static void restoreIfPossible() {
387                 try {
388                         restore();
389                 }
390                 catch (LoggingContextNotExistsException e) {
391                         //Ignore
392                 }
393         }
394
395         /**
396          * AJSC declares an ancient version of org.json:json in one of the parent POMs of this project.
397          * I tried to update our version of that library in our POM, but it's ignored because of the way
398          * AJSC has organized their <dependencies>.  Had they put it into the <dependencyManagement> section,
399          * this method would not be necessary.
400          */
401         private static JSONArray removeLast(JSONArray previousContexts) {
402                 final JSONArray result = new JSONArray();
403
404                 for (int i = 0; i < previousContexts.length() - 1; i++) {
405                         try {
406                                 result.put(previousContexts.getJSONObject(i));
407                         } catch (JSONException e) {
408                                 //Ignore - not possible
409                         }
410                 }
411
412                 return result;
413         }
414
415         public static Map<String, String> getCopy() {
416                 final Map<String, String> copy = new HashMap<String, String> ();
417
418                 for (LoggingField field : LoggingField.values()) {
419                         final String value = MDC.get(field.toString());
420
421                         if (value != null) copy.put(field.toString(), value);
422                 }
423
424                 return copy;
425         }
426 }