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