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