All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.io.klogging.internal.Dispatcher.kt Maven / Gradle / Ivy

Go to download

Kotlin logging library with structured logging and coroutines support

The newest version!
/*

   Copyright 2021-2024 Michael Strasser.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       https://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/

package io.klogging.internal

import io.klogging.AtomicMutableMap
import io.klogging.Level
import io.klogging.Level.TRACE
import io.klogging.events.LogEvent

/** Object that handles dispatching of [LogEvent]s to zero or more sinks. */
internal object Dispatcher {

    @Suppress("TYPE_ALIAS")
    private val sinkCache: AtomicMutableMap, List> = AtomicMutableMap()

    /**
     * Dispatch a [LogEvent] to selected targets. Base context items are included here.
     *
     * Each is dispatched in a separate coroutine.
     *
     * @param logEvent event to dispatch
     */
    internal suspend fun send(logEvent: LogEvent) {
        // If we are tracing Klogging, add event ID to the items map.
        val event = logEvent.addContext(traceContext(logEvent) + KloggingEngine.baseContextItems)

        cachedSinksFor(logEvent.logger, logEvent.level)
            .forEach { sink ->
                trace("Dispatcher", "Dispatching event ${event.id} to sink ${sink.name}")
                sink.send(event)
            }
    }

    /**
     * Dispatch a [LogEvent] directly to each sink.
     * @param logEvent event to dispatch directly
     */
    internal fun sendDirect(logEvent: LogEvent) {
        // If we are tracing Klogging, add event ID to the items map.
        val event = logEvent.addContext(traceContext(logEvent) + KloggingEngine.baseContextItems)

        cachedSinksFor(logEvent.logger, logEvent.level)
            .forEach { sink ->
                trace("Dispatcher", "Dispatching event ${event.id} directly to sink ${sink.name}")
                sink.sendDirect(event)
            }
    }

    private fun traceContext(logEvent: LogEvent) =
        if (KloggingEngine.kloggingMinLogLevel() == TRACE) {
            mapOf("eventId" to logEvent.id)
        } else {
            mapOf()
        }

    /**
     * Simple caching wrapper for [sinksFor] function.
     * @param loggerName name of the logger
     * @param level: level of the event
     * @return sinks to dispatch from the specified logger and level
     */
    internal fun cachedSinksFor(loggerName: String, level: Level): List =
        sinkCache.getOrPut(Pair(loggerName, level)) { sinksFor(loggerName, level) }

    /**
     * Clear the internal cache of sinks, to be called whenever the global Klogging
     * sinks are set [KloggingEngine.setSinks].
     */
    internal fun clearCache() {
        sinkCache.clear()
    }

    /**
     * Calculate the sinks for the specified logger and level.
     *
     * Logging configurations are evaluated in the order they are specified.
     * Matching stops on successful match if the `stopOnMatch` property is
     * `true` (it is false by default).
     *
     * @param loggerName name of the logger
     * @param level level at which to emit logs
     *
     * @return the list of [Sink]s for this logger at this level, which may be empty
     */
    internal fun sinksFor(loggerName: String, level: Level): List {
        var keepMatching = true
        val sinkNames = KloggingEngine.configs()
            .filter { config ->
                val matches = config.nameMatcher(loggerName)
                (keepMatching && matches).also {
                    keepMatching = keepMatching && !(matches && config.stopOnMatch)
                }
            }
            .flatMap { config -> config.ranges }
            .filter { range -> level in range }
            .flatMap { range -> range.sinkNames }
            .distinct()
        return KloggingEngine.sinks()
            .filterKeys { key -> key in sinkNames }
            .map { entry -> entry.value }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy