4da7dcd0eaf7c5c420a7818d8aa09b1e0a6eca54
[ccsdk/cds.git] /
1 /*
2  * Copyright © 2018-2019 AT&T Intellectual Property.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.onap.ccsdk.cds.blueprintsprocessor.core.service
18
19 import kotlinx.coroutines.AbstractCoroutine
20 import kotlinx.coroutines.InternalCoroutinesApi
21 import kotlinx.coroutines.handleCoroutineException
22 import org.onap.ccsdk.cds.controllerblueprints.core.logger
23 import org.slf4j.MDC
24 import org.springframework.http.server.reactive.ServerHttpRequest
25 import org.springframework.http.server.reactive.ServerHttpResponse
26 import reactor.core.Disposable
27 import reactor.core.publisher.MonoSink
28 import java.time.ZoneOffset
29 import java.time.ZonedDateTime
30 import java.time.format.DateTimeFormatter
31 import java.util.*
32 import kotlin.coroutines.CoroutineContext
33
34 class LoggingService {
35     private val log = logger(LoggingService::class)
36
37     companion object {
38         const val ONAP_REQUEST_ID = "X-ONAP-RequestID"
39         const val ONAP_INVOCATION_ID = "X-ONAP-InvocationID"
40         const val ONAP_PARTNER_NAME = "X-ONAP-PartnerName"
41     }
42
43     fun entering(request: ServerHttpRequest) {
44         val headers = request.headers
45         val requestID = defaultToUUID(headers.getFirst(ONAP_REQUEST_ID))
46         val invocationID = defaultToUUID(headers.getFirst(ONAP_INVOCATION_ID))
47         val partnerName = defaultToEmpty(headers.getFirst(ONAP_PARTNER_NAME))
48         MDC.put("InvokeTimestamp", ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT))
49         MDC.put("RequestID", requestID)
50         MDC.put("InvocationID", invocationID)
51         MDC.put("PartnerName", partnerName)
52         MDC.put("ClientIPAddress", defaultToEmpty(request.remoteAddress?.address?.hostAddress))
53         MDC.put("ServerFQDN", defaultToEmpty(request.remoteAddress?.hostString))
54         if (MDC.get("ServiceName") == null || MDC.get("ServiceName").equals("", ignoreCase = true)) {
55             MDC.put("ServiceName", request.uri.path)
56         }
57     }
58
59     fun exiting(request: ServerHttpRequest, response: ServerHttpResponse) {
60         try {
61             val reqHeaders = request.headers
62             val resHeaders = response.headers
63             resHeaders[ONAP_REQUEST_ID] = MDC.get("RequestID")
64             resHeaders[ONAP_INVOCATION_ID] = MDC.get("InvocationID")
65         } catch (e: Exception) {
66             log.warn("couldn't set response headers", e)
67         } finally {
68             MDC.clear()
69         }
70     }
71
72     private fun defaultToEmpty(input: Any?): String {
73         return input?.toString() ?: ""
74     }
75
76     private fun defaultToUUID(input: String?): String {
77         return input ?: UUID.randomUUID().toString()
78     }
79 }
80
81
82 @InternalCoroutinesApi
83 class MonoMDCCoroutine<in T>(
84         parentContext: CoroutineContext,
85         private val sink: MonoSink<T>
86 ) : AbstractCoroutine<T>(parentContext, true), Disposable {
87     private var disposed = false
88
89     override fun onCompleted(value: T) {
90         if (!disposed) {
91             if (value == null) sink.success() else sink.success(value)
92         }
93     }
94
95     override fun onCancelled(cause: Throwable, handled: Boolean) {
96         if (!disposed) {
97             sink.error(cause)
98         } else if (!handled) {
99             handleCoroutineException(context, cause)
100         }
101     }
102
103     override fun dispose() {
104         disposed = true
105         cancel()
106     }
107
108     override fun isDisposed(): Boolean = disposed
109 }