2 * Copyright © 2018-2019 AT&T Intellectual Property.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package org.onap.ccsdk.cds.blueprintsprocessor.rest.service
19 import kotlinx.coroutines.*
20 import kotlinx.coroutines.reactor.ReactorContext
21 import kotlinx.coroutines.reactor.asCoroutineContext
22 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants.ONAP_INVOCATION_ID
23 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants.ONAP_PARTNER_NAME
24 import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintConstants.ONAP_REQUEST_ID
25 import org.onap.ccsdk.cds.controllerblueprints.core.MDCContext
26 import org.onap.ccsdk.cds.controllerblueprints.core.defaultToEmpty
27 import org.onap.ccsdk.cds.controllerblueprints.core.defaultToUUID
28 import org.onap.ccsdk.cds.controllerblueprints.core.logger
30 import org.springframework.http.server.reactive.ServerHttpRequest
31 import org.springframework.http.server.reactive.ServerHttpResponse
32 import reactor.core.Disposable
33 import reactor.core.publisher.Mono
34 import reactor.core.publisher.MonoSink
35 import java.net.InetAddress
36 import java.time.ZoneOffset
37 import java.time.ZonedDateTime
38 import java.time.format.DateTimeFormatter
39 import kotlin.coroutines.CoroutineContext
40 import kotlin.coroutines.EmptyCoroutineContext
42 class RestLoggerService {
43 private val log = logger(RestLoggerService::class)
46 fun entering(request: ServerHttpRequest) {
47 val localhost = InetAddress.getLocalHost()
48 val headers = request.headers
49 val requestID = headers.getFirst(ONAP_REQUEST_ID).defaultToUUID()
50 val invocationID = headers.getFirst(ONAP_INVOCATION_ID).defaultToUUID()
51 val partnerName = headers.getFirst(ONAP_PARTNER_NAME).defaultToEmpty()
52 MDC.put("InvokeTimestamp", ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT))
53 MDC.put("RequestID", requestID)
54 MDC.put("InvocationID", invocationID)
55 MDC.put("PartnerName", partnerName)
56 MDC.put("ClientIPAddress", request.remoteAddress?.address?.hostAddress.defaultToEmpty())
57 MDC.put("ServerFQDN",localhost.hostName.defaultToEmpty())
58 if (MDC.get("ServiceName") == null || MDC.get("ServiceName").equals("", ignoreCase = true)) {
59 MDC.put("ServiceName", request.uri.path)
63 fun exiting(request: ServerHttpRequest, response: ServerHttpResponse) {
65 val reqHeaders = request.headers
66 val resHeaders = response.headers
67 resHeaders[ONAP_REQUEST_ID] = MDC.get("RequestID")
68 resHeaders[ONAP_INVOCATION_ID] = MDC.get("InvocationID")
69 val partnerName = System.getProperty("APPNAME") ?: "BlueprintsProcessor"
70 resHeaders[ONAP_PARTNER_NAME] = partnerName
71 } catch (e: Exception) {
72 log.warn("couldn't set response headers", e)
80 /** Used in Rest controller API methods to populate MDC context to nested coroutines from reactor web filter context. */
81 @UseExperimental(InternalCoroutinesApi::class)
82 fun <T> monoMdc(context: CoroutineContext = EmptyCoroutineContext,
83 block: suspend CoroutineScope.() -> T?): Mono<T> = Mono.create { sink ->
85 val reactorContext = (context[ReactorContext]?.context?.putAll(sink.currentContext())
86 ?: sink.currentContext()).asCoroutineContext()
87 /** Populate MDC context only if present in Reactor Context */
88 val newContext = if (!reactorContext.context.isEmpty
89 && reactorContext.context.hasKey(MDCContext)) {
90 val mdcContext = reactorContext.context.get<MDCContext>(MDCContext)
91 GlobalScope.newCoroutineContext(context + reactorContext + mdcContext)
92 } else GlobalScope.newCoroutineContext(context + reactorContext)
94 val coroutine = MonoMDCCoroutine(newContext, sink)
95 sink.onDispose(coroutine)
96 coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
99 @InternalCoroutinesApi
100 class MonoMDCCoroutine<in T>(
101 parentContext: CoroutineContext,
102 private val sink: MonoSink<T>
103 ) : AbstractCoroutine<T>(parentContext, true), Disposable {
104 private var disposed = false
106 override fun onCompleted(value: T) {
108 if (value == null) sink.success() else sink.success(value)
112 override fun onCancelled(cause: Throwable, handled: Boolean) {
115 } else if (!handled) {
116 handleCoroutineException(context, cause)
120 override fun dispose() {
125 override fun isDisposed(): Boolean = disposed