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.time.ZoneOffset
36 import java.time.ZonedDateTime
37 import java.time.format.DateTimeFormatter
38 import kotlin.coroutines.CoroutineContext
39 import kotlin.coroutines.EmptyCoroutineContext
41 class RestLoggerService {
42 private val log = logger(RestLoggerService::class)
45 fun entering(request: ServerHttpRequest) {
46 val headers = request.headers
47 val requestID = headers.getFirst(ONAP_REQUEST_ID).defaultToUUID()
48 val invocationID = headers.getFirst(ONAP_INVOCATION_ID).defaultToUUID()
49 val partnerName = headers.getFirst(ONAP_PARTNER_NAME).defaultToEmpty()
50 MDC.put("InvokeTimestamp", ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT))
51 MDC.put("RequestID", requestID)
52 MDC.put("InvocationID", invocationID)
53 MDC.put("PartnerName", partnerName)
54 MDC.put("ClientIPAddress", request.remoteAddress?.address?.hostAddress.defaultToEmpty())
55 MDC.put("ServerFQDN", request.remoteAddress?.hostString.defaultToEmpty())
56 if (MDC.get("ServiceName") == null || MDC.get("ServiceName").equals("", ignoreCase = true)) {
57 MDC.put("ServiceName", request.uri.path)
61 fun exiting(request: ServerHttpRequest, response: ServerHttpResponse) {
63 val reqHeaders = request.headers
64 val resHeaders = response.headers
65 resHeaders[ONAP_REQUEST_ID] = MDC.get("RequestID")
66 resHeaders[ONAP_INVOCATION_ID] = MDC.get("InvocationID")
67 val partnerName = System.getProperty("APPNAME") ?: "BlueprintsProcessor"
68 resHeaders[ONAP_PARTNER_NAME] = partnerName
69 } catch (e: Exception) {
70 log.warn("couldn't set response headers", e)
78 /** Used in Rest controller API methods to populate MDC context to nested coroutines from reactor web filter context. */
79 @UseExperimental(InternalCoroutinesApi::class)
80 fun <T> monoMdc(context: CoroutineContext = EmptyCoroutineContext,
81 block: suspend CoroutineScope.() -> T?): Mono<T> = Mono.create { sink ->
83 val reactorContext = (context[ReactorContext]?.context?.putAll(sink.currentContext())
84 ?: sink.currentContext()).asCoroutineContext()
85 /** Populate MDC context only if present in Reactor Context */
86 val newContext = if (!reactorContext.context.isEmpty
87 && reactorContext.context.hasKey(MDCContext)) {
88 val mdcContext = reactorContext.context.get<MDCContext>(MDCContext)
89 GlobalScope.newCoroutineContext(context + reactorContext + mdcContext)
90 } else GlobalScope.newCoroutineContext(context + reactorContext)
92 val coroutine = MonoMDCCoroutine(newContext, sink)
93 sink.onDispose(coroutine)
94 coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
97 @InternalCoroutinesApi
98 class MonoMDCCoroutine<in T>(
99 parentContext: CoroutineContext,
100 private val sink: MonoSink<T>
101 ) : AbstractCoroutine<T>(parentContext, true), Disposable {
102 private var disposed = false
104 override fun onCompleted(value: T) {
106 if (value == null) sink.success() else sink.success(value)
110 override fun onCancelled(cause: Throwable, handled: Boolean) {
113 } else if (!handled) {
114 handleCoroutineException(context, cause)
118 override fun dispose() {
123 override fun isDisposed(): Boolean = disposed