Generate InvocationID UUID on outgoing requests and use X-InvocationID
[logging-analytics.git] / reference / logging-slf4j / src / test / java / org / onap / logging / ref / slf4j / ONAPLogAdapterTest.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.util.HashMap;
25 import java.util.Map;
26 import java.util.UUID;
27
28 import javax.xml.bind.DatatypeConverter;
29
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32 import org.slf4j.MDC;
33 import org.slf4j.event.Level;
34 import org.springframework.mock.web.MockHttpServletRequest;
35 import org.testng.Assert;
36 import org.testng.annotations.AfterMethod;
37 import org.testng.annotations.Test;
38 import static org.hamcrest.MatcherAssert.assertThat;
39 import static org.hamcrest.core.Is.is;
40 import static org.hamcrest.core.IsEqual.equalTo;
41 import static org.hamcrest.core.IsNot.not;
42 import static org.hamcrest.core.IsSame.sameInstance;
43 import static org.hamcrest.core.StringContains.containsString;
44 import static org.hamcrest.core.StringEndsWith.endsWith;
45 import static org.hamcrest.core.Every.everyItem;
46 import static org.hamcrest.core.IsInstanceOf.instanceOf;
47 import static org.hamcrest.core.IsNull.notNullValue;
48 import static org.hamcrest.core.IsNull.nullValue;
49 import static org.hamcrest.core.StringStartsWith.startsWith;
50 import static org.hamcrest.beans.HasProperty.hasProperty;
51 import static org.hamcrest.number.IsCloseTo.closeTo;
52 import static org.hamcrest.number.OrderingComparison.greaterThan;
53 import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo;
54 import static org.hamcrest.number.OrderingComparison.lessThan;
55 import static org.hamcrest.number.OrderingComparison.lessThanOrEqualTo;
56 import static org.hamcrest.collection.IsArray.array;
57 import static org.hamcrest.collection.IsArrayContaining.hasItemInArray;
58 import static org.hamcrest.collection.IsArrayContainingInAnyOrder.arrayContainingInAnyOrder;
59 import static org.hamcrest.collection.IsIn.isIn;
60 import static org.hamcrest.collection.IsIn.isOneOf;
61 import static org.hamcrest.collection.IsMapContaining.hasKey;
62 import static org.hamcrest.collection.IsMapContaining.hasEntry;
63 import static org.hamcrest.collection.IsMapContaining.hasValue;
64 import static org.hamcrest.text.IsEqualIgnoringWhiteSpace.equalToIgnoringWhiteSpace;
65 import static org.hamcrest.text.IsEqualIgnoringCase.equalToIgnoringCase;
66 import static org.hamcrest.text.IsEmptyString.isEmptyOrNullString;
67 import static org.hamcrest.text.IsEmptyString.isEmptyString;
68 import static org.hamcrest.text.StringContainsInOrder.stringContainsInOrder;
69 import static org.hamcrest.xml.HasXPath.hasXPath;
70
71 import org.testng.Assert;
72 import org.testng.annotations.Test;
73
74 import static org.hamcrest.MatcherAssert.assertThat;
75 import static org.hamcrest.core.Is.is;
76 import static org.hamcrest.core.IsNot.not;
77 import static org.hamcrest.core.IsNull.notNullValue;
78 import static org.hamcrest.core.IsNull.nullValue;
79 import static org.hamcrest.core.IsSame.sameInstance;
80
81 /**
82  * Tests for {@link ONAPLogAdapter}.
83  */
84 public class ONAPLogAdapterTest {
85
86     /**
87      * Ensure that MDCs are cleared after each testcase.
88      */
89     @AfterMethod
90     public void resetMDCs() {
91         MDC.clear();
92     }
93
94     /**
95      * Test nullcheck.
96      */
97     @Test
98     public void testCheckNotNull() {
99
100         ONAPLogAdapter.checkNotNull("");
101
102         try {
103             ONAPLogAdapter.checkNotNull(null);
104             Assert.fail("Should throw NullPointerException");
105         }
106         catch (final NullPointerException e) {
107
108         }
109     }
110
111     /**
112      * Test defaulting of nulls.
113      */
114     @Test
115     public void testDefaultToEmpty() {
116         assertThat(ONAPLogAdapter.defaultToEmpty("123"), is("123"));
117         assertThat(ONAPLogAdapter.defaultToEmpty(Integer.valueOf(1984)), is("1984"));
118         assertThat(ONAPLogAdapter.defaultToEmpty(null), is(""));
119     }
120
121     /**
122      * Test defaulting of nulls.
123      */
124     @Test
125     public void testDefaultToUUID() {
126         assertThat(ONAPLogAdapter.defaultToUUID("123"), is("123"));
127         UUID.fromString(ONAPLogAdapter.defaultToUUID(null));
128     }
129
130     /**
131      * Test ENTERING.
132      */
133     @Test
134     public void testEntering() {
135
136         final Logger logger = LoggerFactory.getLogger(this.getClass());
137         final ONAPLogAdapter adapter = new ONAPLogAdapter(logger);
138         final MockHttpServletRequest http = new MockHttpServletRequest();
139         http.setRequestURI("uri123");
140         http.setServerName("local123");
141         http.setRemoteAddr("remote123");
142         http.addHeader("X-ONAP-RequestID", "request123");
143         http.addHeader("X-InvocationID", "invocation123");
144         http.addHeader("X-ONAP-PartnerName", "partner123");
145
146         try {
147             adapter.getServiceDescriptor().setServiceName("uri123");
148             adapter.entering(http);
149             final Map<String, String> mdcs = MDC.getCopyOfContextMap();
150             assertThat(mdcs.get("RequestID"), is("request123"));
151             assertThat(mdcs.get("InvocationID"), is("invocation123"));
152             assertThat(mdcs.get("PartnerName"), is("partner123"));
153             assertThat(mdcs.get("ServiceName"), is("uri123"));
154             assertThat(mdcs.get("ServerFQDN"), is("local123"));
155             assertThat(mdcs.get("ClientIPAddress"), is("remote123"));
156
157             // Timestamp format and value:
158
159             final String invokeTimestampString = mdcs.get("InvokeTimestamp");
160             assertThat(invokeTimestampString, notNullValue());
161             assertThat(invokeTimestampString, endsWith("Z"));
162             final long invokeTimestamp = DatatypeConverter.parseDateTime(invokeTimestampString).getTimeInMillis();
163             assertThat(Math.abs(System.currentTimeMillis() - invokeTimestamp), lessThan(5000L));
164         }
165         finally {
166             MDC.clear();
167         }
168     }
169     
170     /**
171      * Test ENTERING with an EMPTY_STRING serviceName.
172      */
173     @Test
174     public void testEnteringWithEMPTY_STRING_serviceName() {
175
176         final Logger logger = LoggerFactory.getLogger(this.getClass());
177         final ONAPLogAdapter adapter = new ONAPLogAdapter(logger);
178         final MockHttpServletRequest http = new MockHttpServletRequest();
179         http.setRequestURI("uri123");
180         http.setServerName("local123");
181         http.setRemoteAddr("remote123");
182         http.addHeader("X-ONAP-RequestID", "request123");
183         http.addHeader("X-InvocationID", "invocation123");
184         http.addHeader("X-ONAP-PartnerName", "partner123");
185
186         try {
187             // an empty string should kick in setting the actual service name (treated same as null)
188             adapter.getServiceDescriptor().setServiceName("");
189             adapter.entering(http);
190             final Map<String, String> mdcs = MDC.getCopyOfContextMap();
191             assertThat(mdcs.get("RequestID"), is("request123"));
192             assertThat(mdcs.get("InvocationID"), is("invocation123"));
193             assertThat(mdcs.get("PartnerName"), is("partner123"));
194             assertThat(mdcs.get("ServiceName"), is("uri123"));
195             assertThat(mdcs.get("ServerFQDN"), is("local123"));
196             assertThat(mdcs.get("ClientIPAddress"), is("remote123"));
197
198             // Timestamp format and value:
199
200             final String invokeTimestampString = mdcs.get("InvokeTimestamp");
201             assertThat(invokeTimestampString, notNullValue());
202             assertThat(invokeTimestampString, endsWith("Z"));
203             final long invokeTimestamp = DatatypeConverter.parseDateTime(invokeTimestampString).getTimeInMillis();
204             assertThat(Math.abs(System.currentTimeMillis() - invokeTimestamp), lessThan(5000L));
205         }
206         finally {
207             MDC.clear();
208         }
209     }
210
211     @Test
212     public void testSetServiceDescriptor() {
213         final ONAPLogAdapter.ServiceDescriptor override = new ONAPLogAdapter.ServiceDescriptor();
214         final Logger logger = LoggerFactory.getLogger(this.getClass());
215         final ONAPLogAdapter adapter = new ONAPLogAdapter(logger);
216         final ONAPLogAdapter.ServiceDescriptor before = adapter.getServiceDescriptor();
217         adapter.setServiceDescriptor(override);
218         final ONAPLogAdapter.ServiceDescriptor after = adapter.getServiceDescriptor();
219         assertThat(after, not(sameInstance(before)));
220         assertThat(after, is(override));
221     }
222
223     @Test
224     public void testSetResponseDescriptor() {
225         final ONAPLogAdapter.ResponseDescriptor override = new ONAPLogAdapter.ResponseDescriptor();
226         final Logger logger = LoggerFactory.getLogger(this.getClass());
227         final ONAPLogAdapter adapter = new ONAPLogAdapter(logger);
228         final ONAPLogAdapter.ResponseDescriptor before = adapter.getResponseDescriptor();
229         adapter.setResponseDescriptor(override);
230         final ONAPLogAdapter.ResponseDescriptor after = adapter.getResponseDescriptor();
231         assertThat(after, not(sameInstance(before)));
232         assertThat(after, is(override));
233     }
234
235     @Test
236     public void testUnwrap() {
237         final Logger logger = LoggerFactory.getLogger(this.getClass());
238         final ONAPLogAdapter adapter = new ONAPLogAdapter(logger);
239         assertThat(adapter.unwrap(), is(logger));
240     }
241
242     /**
243      * Test EXITING.
244      */
245     @Test
246     public void testExiting() {
247
248         final Logger logger = LoggerFactory.getLogger(this.getClass());
249         final ONAPLogAdapter adapter = new ONAPLogAdapter(logger);
250
251         try {
252             MDC.put("somekey", "somevalue");
253             assertThat(MDC.get("somekey"), is("somevalue"));
254             adapter.exiting();
255             assertThat(MDC.get("somekey"), nullValue());
256         }
257         finally {
258             MDC.clear();
259         }
260     }
261
262     /**
263      * Test INVOKE.
264      */
265     @Test
266     public void testInvokeSyncAsyncNull() {
267
268         final Logger logger = LoggerFactory.getLogger(this.getClass());
269         final ONAPLogAdapter adapter = new ONAPLogAdapter(logger);
270
271         final UUID syncUUID = adapter.invoke(ONAPLogConstants.InvocationMode.SYNCHRONOUS);
272         assertThat(syncUUID, notNullValue());
273
274         final UUID asyncUUID = adapter.invoke(ONAPLogConstants.InvocationMode.SYNCHRONOUS);
275         assertThat(asyncUUID, notNullValue());
276
277         final UUID agnosticUUID = adapter.invoke((ONAPLogConstants.InvocationMode)null);
278         assertThat(agnosticUUID, notNullValue());
279
280     }
281
282     /**
283      * Test INVOKE, with RequestAdapter.
284      */
285     @Test
286     public void testInvokeWithAdapter() throws Exception {
287
288         final Logger logger = LoggerFactory.getLogger(this.getClass());
289         final ONAPLogAdapter adapter = new ONAPLogAdapter(logger);
290
291         final Map<String, String> headers = new HashMap<>();
292         final ONAPLogAdapter.RequestBuilder builder = new ONAPLogAdapter.RequestBuilder<ONAPLogAdapter.RequestBuilder>() {
293             @Override
294             public ONAPLogAdapter.RequestBuilder setHeader(final String name, final String value) {
295                 headers.put(name, value);
296                 return this;
297             }
298         };
299
300         try {
301             final UUID uuid = adapter.invoke(builder, ONAPLogConstants.InvocationMode.SYNCHRONOUS);
302             assertThat(uuid, notNullValue());
303             assertThat(headers.get(ONAPLogConstants.Headers.INVOCATION_ID), is(uuid.toString()));
304             assertThat(headers.containsKey(ONAPLogConstants.Headers.PARTNER_NAME), is(true));
305             assertThat(headers.containsKey(ONAPLogConstants.Headers.REQUEST_ID), is(true));
306         }
307         finally {
308             MDC.clear();
309         }
310     }
311
312     /**
313      * Test INVOKE, with RequestAdapter.
314      */
315     @Test
316     public void testInvokeWithAdapterAndNull() throws Exception {
317
318         final Logger logger = LoggerFactory.getLogger(this.getClass());
319         final ONAPLogAdapter adapter = new ONAPLogAdapter(logger);
320
321         final Map<String, String> headers = new HashMap<>();
322         final ONAPLogAdapter.RequestBuilder builder = new ONAPLogAdapter.RequestBuilder<ONAPLogAdapter.RequestBuilder>() {
323             @Override
324             public ONAPLogAdapter.RequestBuilder setHeader(final String name, final String value) {
325                 headers.put(name, value);
326                 return this;
327             }
328         };
329
330         try {
331             final UUID uuid = adapter.invoke(builder);
332             assertThat(uuid, notNullValue());
333             assertThat(headers.get(ONAPLogConstants.Headers.INVOCATION_ID), is(uuid.toString()));
334             assertThat(headers.containsKey(ONAPLogConstants.Headers.PARTNER_NAME), is(true));
335             assertThat(headers.containsKey(ONAPLogConstants.Headers.REQUEST_ID), is(true));
336         }
337         finally {
338             MDC.clear();
339         }
340     }
341
342     @Test
343     public void testHttpServletRequestAdapter() {
344
345         final UUID uuid = UUID.randomUUID();
346         final MockHttpServletRequest request = new MockHttpServletRequest();
347         request.addHeader("uuid", uuid.toString());
348         request.setRequestURI("/ctx0");
349         request.setServerName("srv0");
350
351         final ONAPLogAdapter.HttpServletRequestAdapter adapter
352                 = new ONAPLogAdapter.HttpServletRequestAdapter(request);
353         assertThat(adapter.getHeader("uuid"), is(uuid.toString()));
354         assertThat(adapter.getRequestURI(), is("/ctx0"));
355         assertThat(adapter.getServerAddress(), is("srv0"));
356     }
357
358     @Test
359     public void testServiceDescriptor() {
360         final String uuid = UUID.randomUUID().toString();
361
362         final ONAPLogAdapter.ServiceDescriptor adapter
363                 = new ONAPLogAdapter.ServiceDescriptor();
364         adapter.setServiceUUID(uuid);
365         adapter.setServiceName("name0");
366
367         assertThat(MDC.get(ONAPLogConstants.MDCs.SERVICE_NAME), nullValue());
368         assertThat(MDC.get(ONAPLogConstants.MDCs.INSTANCE_UUID), nullValue());
369
370         adapter.setMDCs();
371
372         assertThat(MDC.get(ONAPLogConstants.MDCs.SERVICE_NAME), is("name0"));
373         assertThat(MDC.get(ONAPLogConstants.MDCs.INSTANCE_UUID), is(uuid));
374     }
375
376     @Test
377     public void testResponseDescriptor() {
378         final String uuid = UUID.randomUUID().toString();
379
380         final ONAPLogAdapter.ResponseDescriptor adapter
381                 = new ONAPLogAdapter.ResponseDescriptor();
382         adapter.setResponseCode("code0");
383         adapter.setResponseDescription("desc0");
384         adapter.setResponseSeverity(Level.INFO);
385         adapter.setResponseStatus(ONAPLogConstants.ResponseStatus.COMPLETE);
386
387         assertThat(MDC.get(ONAPLogConstants.MDCs.RESPONSE_CODE), nullValue());
388         assertThat(MDC.get(ONAPLogConstants.MDCs.RESPONSE_DESCRIPTION), nullValue());
389         assertThat(MDC.get(ONAPLogConstants.MDCs.RESPONSE_SEVERITY), nullValue());
390         assertThat(MDC.get(ONAPLogConstants.MDCs.RESPONSE_STATUS_CODE), nullValue());
391
392         adapter.setMDCs();
393
394         assertThat(MDC.get(ONAPLogConstants.MDCs.RESPONSE_CODE), is("code0"));
395         assertThat(MDC.get(ONAPLogConstants.MDCs.RESPONSE_DESCRIPTION), is("desc0"));
396         assertThat(MDC.get(ONAPLogConstants.MDCs.RESPONSE_SEVERITY), is("INFO"));
397         assertThat(MDC.get(ONAPLogConstants.MDCs.RESPONSE_STATUS_CODE), is("COMPLETE"));
398     }
399
400     /**
401      * Exercise the contract, for a caller that's happy to have their
402      * service name automatically derived. (This validates nothing
403      * and achieves nothing; it's just to provide an example of minimal usage).
404      */
405     @Test
406     public void testContract() {
407
408         // Note no wrapper around HttpServletRequest, which will work for
409         // most invocations (since they come via HTTP), but otherwise
410         // can implement your own RequestAdapter.
411
412         final Logger logger = LoggerFactory.getLogger(this.getClass());
413         final ONAPLogAdapter adapter = new ONAPLogAdapter(logger);
414         final MockHttpServletRequest http = new MockHttpServletRequest();
415
416         // Immediately log ENTERING marker, with global MDCs.
417
418         adapter.entering(http);
419         try {
420
421             // Generate (and log) an invocationID, then use it to
422             // invoke another component.
423
424             final RESTClient client = new RESTClient();             // implements ONAPLogAdapter.RequestBuilder<RESTClient>.
425             adapter.invoke(client, ONAPLogConstants.InvocationMode.SYNCHRONOUS);
426             final RESTRequest request = null;                       // TODO: build real request.
427             final RESTResponse response = client.execute(request);  // TODO: handle real response.
428
429             // Set response details prior to #exiting.
430             // (Obviously there'd be errorhandling, etc. IRL).
431
432             adapter.getResponseDescriptor()
433                     .setResponseCode((String)null)
434                     .setResponseSeverity(Level.INFO)
435                     .setResponseStatus(ONAPLogConstants.ResponseStatus.COMPLETE);
436         }
437         finally {
438
439             // Return, logging EXIT marker, with response MDCs.
440
441             adapter.exiting();
442         }
443     }
444
445     /**
446      * Dummy class, for example code.
447      */
448     static class RESTClient implements ONAPLogAdapter.RequestBuilder<RESTClient> {
449
450         @Override
451         public RESTClient setHeader(final String name, final String value) {
452             return null;
453         }
454
455         RESTResponse execute(RESTRequest request) {
456             return null;
457         }
458     }
459
460     /**
461      * Dummy class, for example code.
462      */
463     static class RESTRequest {
464
465     }
466
467     /**
468      * Dummy class, for example code.
469      */
470     static class RESTResponse {
471
472     }
473 }