diff options
Diffstat (limited to 'simulator/odcsim/odcsim-engine-omega/src/main')
6 files changed, 1383 insertions, 0 deletions
diff --git a/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaSimulationEngine.kt b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaSimulationEngine.kt new file mode 100644 index 00000000..e675b877 --- /dev/null +++ b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaSimulationEngine.kt @@ -0,0 +1,264 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.engine.omega + +import com.atlarge.odcsim.Domain +import com.atlarge.odcsim.SimulationContext +import com.atlarge.odcsim.SimulationEngine +import com.atlarge.odcsim.engine.omega.logging.LoggerImpl +import java.time.Clock +import java.time.Instant +import java.time.ZoneId +import java.util.PriorityQueue +import java.util.UUID +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.coroutineContext +import kotlinx.coroutines.CancellableContinuation +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.CoroutineName +import kotlinx.coroutines.Delay +import kotlinx.coroutines.DisposableHandle +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.InternalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.Runnable +import kotlinx.coroutines.SupervisorJob +import org.jetbrains.annotations.Async +import org.slf4j.Logger + +/** + * The reference implementation of the [SimulationEngine] instance for the OpenDC simulation core. + * + * This engine implementation is a single-threaded implementation, running logical processes synchronously and + * provides a single priority queue for all events (messages, ticks, etc) that occur. + * + * @param name The name of the engine instance. + */ +public class OmegaSimulationEngine(override val name: String) : SimulationEngine { + /** + * The state of the actor system. + */ + private var state: SimulationEngineState = SimulationEngineState.CREATED + + /** + * The clock tracking the simulation time. + */ + private val clock: VirtualClock = VirtualClock(0) + + /** + * The event queue to process + */ + private val queue: PriorityQueue<Event> = PriorityQueue(Comparator<Event> { lhs, rhs -> + // Note that Comparator gives better performance than Comparable according to + // profiling + val cmp = lhs.time.compareTo(rhs.time) + if (cmp == 0) lhs.id.compareTo(rhs.id) else cmp + }) + + /** + * The active processes in the simulation engine. + */ + private val registry: MutableMap<String, Domain> = HashMap() + + /** + * A unique increasing identifier assigned to each event, needed because otherwise two events occurring in sequence + * (but at the same time) may be differently ordered in the internal priority queue (queue) since it does not + * guarantee insertion order. + */ + private var nextId: Long = 0 + + override fun newDomain(): Domain = newDomain(null) + + override fun newDomain(name: String): Domain = newDomain(name, null) + + override suspend fun run() { + check(state != SimulationEngineState.TERMINATED) { "The simulation engine is terminated" } + + if (state == SimulationEngineState.CREATED) { + state = SimulationEngineState.STARTED + } + + val job = coroutineContext[Job] + + while (job?.isActive == true) { + val event = queue.peek() ?: break + val delivery = event.time + + // A message should never be delivered out of order in this single-threaded implementation. Assert for + // sanity + assert(delivery >= clock.time) { "Message delivered out of order [expected=$delivery, actual=${clock.time}]" } + + clock.time = delivery + queue.poll() + + process(event) + } + } + + override suspend fun terminate() { + state = SimulationEngineState.TERMINATED + } + + /** + * Schedule the specified event to be processed by the engine. + */ + private fun schedule(@Async.Schedule event: Event) { + assert(event.time >= clock.time) { "Message scheduled in the past [received=${event.time}, actual=${clock.time}]" } + queue.add(event) + } + + /** + * Process the delivery of an event. + */ + @OptIn(ExperimentalCoroutinesApi::class) + private fun process(@Async.Execute event: Event) { + // This has been inlined into this method for performance + when (event) { + is Event.Dispatch -> + event.block.run() + is Event.Resume -> + with(event.continuation) { event.dispatcher.resumeUndispatched(Unit) } + is Event.Timeout -> + if (!event.isCancelled) + event.block.run() + } + } + + /** + * Spawn a new simulation domain. + */ + private fun newDomainImpl(name: String, parent: DomainImpl?): Domain { + val domain = DomainImpl(name, parent) + require(domain.path !in registry) { "Domain name $name not unique" } + registry[domain.path] = domain + return domain + } + + private fun newDomain(parent: DomainImpl?): Domain { + val name = "$" + UUID.randomUUID() + return newDomainImpl(name, parent) + } + + private fun newDomain(name: String, parent: DomainImpl?): Domain { + require(name.isNotEmpty()) { "Domain name may not be empty" } + require(!name.startsWith("$")) { "Domain name may not start with $-sign" } + require(!name.contains("/")) { "Domain name may not contain /" } + return newDomainImpl(name, parent) + } + + private inner class DomainImpl(override val name: String, parent: DomainImpl?) : SimulationContext, Domain { + val job: Job = SupervisorJob(parent?.job) + val path: String = (parent?.path ?: "") + "/$name" + + @InternalCoroutinesApi + private val dispatcher = object : CoroutineDispatcher(), Delay { + // CoroutineDispatcher + override fun dispatch(context: CoroutineContext, block: Runnable) { + schedule(Event.Dispatch(clock.time, nextId++, block)) + } + + // Delay + override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) { + schedule(Event.Resume(clock.time + timeMillis, nextId++, this, continuation)) + } + + override fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle { + val event = Event.Timeout(clock.time + timeMillis, nextId++, block) + schedule(event) + return event + } + } + + private val exceptionHandler = CoroutineExceptionHandler { _, exception -> + log.error("Uncaught exception", exception) + } + + // SimulationContext + override val key: CoroutineContext.Key<*> = SimulationContext.Key + + override val domain: Domain = this + + override val clock: VirtualClock + get() = this@OmegaSimulationEngine.clock + + override val log: Logger by lazy(LazyThreadSafetyMode.NONE) { LoggerImpl.invoke(this) } + + override fun newDomain(): Domain = this@OmegaSimulationEngine.newDomain(this) + + override fun newDomain(name: String): Domain = this@OmegaSimulationEngine.newDomain(name, this) + + // Domain + override val parent: Domain = parent ?: this + + @InternalCoroutinesApi + override val coroutineContext: CoroutineContext = this + CoroutineName(name) + dispatcher + job + exceptionHandler + + override fun toString(): String = path + } + + /** + * Enumeration to track the state of the actor system. + */ + private enum class SimulationEngineState { + CREATED, STARTED, TERMINATED + } + + /** + * A wrapper around a message that has been scheduled for processing. + * + * @property time The point in time to deliver the message. + */ + private sealed class Event(val time: Long, val id: Long) { + class Dispatch(time: Long, id: Long, val block: Runnable) : Event(time, id) { + override fun toString(): String = "Dispatch[$time]" + } + + class Resume(time: Long, id: Long, val dispatcher: CoroutineDispatcher, val continuation: CancellableContinuation<Unit>) : Event(time, id) { + override fun toString(): String = "Resume[$time]" + } + + class Timeout(time: Long, id: Long, val block: Runnable, var isCancelled: Boolean = false) : Event(time, id), DisposableHandle { + override fun dispose() { + isCancelled = true + } + + override fun toString(): String = "Timeout[$time]" + } + } + + /** + * A virtual [Clock] implementation for keeping track of simulation time. + */ + private data class VirtualClock(var time: Long) : Clock() { + override fun withZone(zone: ZoneId?): Clock = throw NotImplementedError() + + override fun getZone(): ZoneId = ZoneId.systemDefault() + + override fun instant(): Instant = Instant.ofEpochMilli(time) + + override fun millis(): Long = time + } +} diff --git a/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaSimulationEngineProvider.kt b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaSimulationEngineProvider.kt new file mode 100644 index 00000000..5dba3233 --- /dev/null +++ b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaSimulationEngineProvider.kt @@ -0,0 +1,37 @@ +/* + * MIT License + * + * Copyright (c) 2018 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.engine.omega + +import com.atlarge.odcsim.SimulationEngine +import com.atlarge.odcsim.SimulationEngineProvider +import java.util.ServiceLoader + +/** + * An [SimulationEngineProvider] for the Omega engine, used by the [ServiceLoader] API to create + * [OmegaSimulationEngine] instances. + */ +public class OmegaSimulationEngineProvider : SimulationEngineProvider { + override operator fun invoke(name: String): SimulationEngine = OmegaSimulationEngine(name) +} diff --git a/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LocationAwareLoggerImpl.kt b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LocationAwareLoggerImpl.kt new file mode 100644 index 00000000..fca4826e --- /dev/null +++ b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LocationAwareLoggerImpl.kt @@ -0,0 +1,567 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.engine.omega.logging + +import com.atlarge.odcsim.SimulationContext +import org.slf4j.Logger +import org.slf4j.Marker +import org.slf4j.helpers.MessageFormatter +import org.slf4j.spi.LocationAwareLogger + +/** + * An actor-specific [Logger] implementation that is aware of the calling location. + * + * @param ctx The owning [SimulationContext] of this logger. + * @param delegate The [LocationAwareLogger] to delegate the messages to. + */ +internal class LocationAwareLoggerImpl( + ctx: SimulationContext, + private val delegate: LocationAwareLogger +) : LoggerImpl(ctx), Logger by delegate { + /** + * The fully qualified name of this class. + */ + private val fqcn = LocationAwareLoggerImpl::class.java.name + + override fun trace(format: String?, arg: Any?) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun trace(format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun trace(format: String?, argArray: Array<Any?>) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message + delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, argArray, null) + } + } + + override fun trace(msg: String?, t: Throwable?) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, msg, null, t) + } + } + + override fun trace(marker: Marker?, msg: String?) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, msg, null, null) + } + } + + override fun trace(marker: Marker?, format: String?, arg: Any?) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun trace(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun trace(marker: Marker?, format: String?, argArray: Array<Any?>) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message + delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, argArray, null) + } + } + + override fun trace(marker: Marker?, msg: String?, t: Throwable?) { + if (!delegate.isTraceEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, msg, null, t) + } + } + + override fun debug(msg: String?) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, null) + } + } + + override fun debug(format: String?, arg: Any?) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun debug(format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun debug(format: String?, argArray: Array<Any?>) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + val ft = MessageFormatter.arrayFormat(format, argArray) + delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, ft.message, ft.argArray, ft.throwable) + } + } + + override fun debug(msg: String?, t: Throwable?) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, t) + } + } + + override fun debug(marker: Marker?, msg: String?) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, null) + } + } + + override fun debug(marker: Marker?, format: String?, arg: Any?) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + val ft = MessageFormatter.format(format, arg) + delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, ft.message, ft.argArray, ft.throwable) + } + } + + override fun debug(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun debug(marker: Marker?, format: String?, argArray: Array<Any?>) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + val ft = MessageFormatter.arrayFormat(format, argArray) + delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, ft.message, argArray, ft.throwable) + } + } + + override fun debug(marker: Marker?, msg: String?, t: Throwable?) { + if (!delegate.isDebugEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, t) + } + } + + override fun info(msg: String?) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, msg, null, null) + } + } + + override fun info(format: String?, arg: Any?) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun info(format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun info(format: String?, argArray: Array<Any?>) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message + delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, argArray, null) + } + } + + override fun info(msg: String?, t: Throwable?) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, msg, null, t) + } + } + + override fun info(marker: Marker?, msg: String?) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, msg, null, null) + } + } + + override fun info(marker: Marker?, format: String?, arg: Any?) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun info(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun info(marker: Marker?, format: String?, argArray: Array<Any?>) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message + delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, argArray, null) + } + } + + override fun info(marker: Marker?, msg: String?, t: Throwable?) { + if (!delegate.isInfoEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, msg, null, t) + } + } + + override fun warn(msg: String?) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, msg, null, null) + } + } + + override fun warn(format: String?, arg: Any?) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun warn(format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun warn(format: String?, argArray: Array<Any?>) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message + delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, argArray, null) + } + } + + override fun warn(msg: String?, t: Throwable?) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, msg, null, t) + } + } + + override fun warn(marker: Marker?, msg: String?) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, msg, null, null) + } + } + + override fun warn(marker: Marker?, format: String?, arg: Any?) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun warn(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun warn(marker: Marker?, format: String?, argArray: Array<Any?>) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message + delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, argArray, null) + } + } + + override fun warn(marker: Marker?, msg: String?, t: Throwable?) { + if (!delegate.isWarnEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, msg, null, t) + } + } + + override fun error(msg: String?) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, msg, null, null) + } + } + + override fun error(format: String?, arg: Any?) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun error(format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun error(format: String?, argArray: Array<Any?>) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message + delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, argArray, null) + } + } + + override fun error(msg: String?, t: Throwable?) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, msg, null, t) + } + } + + override fun error(marker: Marker?, msg: String?) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, msg, null, null) + } + } + + override fun error(marker: Marker?, format: String?, arg: Any?) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg).message + delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg), null) + } + } + + override fun error(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.format(format, arg1, arg2).message + delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg1, arg2), null) + } + } + + override fun error(marker: Marker?, format: String?, argArray: Array<Any?>) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message + delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, argArray, null) + } + } + + override fun error(marker: Marker?, msg: String?, t: Throwable?) { + if (!delegate.isErrorEnabled) { + return + } + + withMdc { + delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, msg, null, t) + } + } +} diff --git a/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LocationIgnorantLoggerImpl.kt b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LocationIgnorantLoggerImpl.kt new file mode 100644 index 00000000..856cecfa --- /dev/null +++ b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LocationIgnorantLoggerImpl.kt @@ -0,0 +1,440 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.engine.omega.logging + +import com.atlarge.odcsim.SimulationContext +import org.slf4j.Logger +import org.slf4j.Marker + +/** + * A [Logger] implementation that is not aware of the calling location. + * + * @param ctx The owning [SimulationContext] of this logger. + * @param delegate The [Logger] to delegate the messages to. + */ +internal class LocationIgnorantLoggerImpl( + ctx: SimulationContext, + private val delegate: Logger +) : LoggerImpl(ctx), Logger by delegate { + override fun warn(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(marker, format, arg1, arg2) } + } + + override fun warn(format: String?, arg1: Any?, arg2: Any?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(format, arg1, arg2) } + } + + override fun warn(msg: String?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(msg) } + } + + override fun warn(marker: Marker?, format: String?, arg: Any?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(marker, format, arg) } + } + + override fun warn(marker: Marker?, format: String?, vararg arguments: Any?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(marker, format, arguments) } + } + + override fun warn(format: String?, arg: Any?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(format, arg) } + } + + override fun warn(marker: Marker?, msg: String?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(marker, msg) } + } + + override fun warn(msg: String?, t: Throwable?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(msg, t) } + } + + override fun warn(format: String?, vararg arguments: Any?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(format, *arguments) } + } + + override fun warn(marker: Marker?, msg: String?, t: Throwable?) { + if (!isWarnEnabled) { + return + } + + withMdc { delegate.warn(marker, msg, t) } + } + + override fun info(marker: Marker?, format: String?, vararg arguments: Any?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(marker, format, *arguments) } + } + + override fun info(format: String?, arg: Any?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(format, arg) } + } + + override fun info(marker: Marker?, msg: String?, t: Throwable?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(marker, msg, t) } + } + + override fun info(msg: String?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(msg) } + } + + override fun info(format: String?, vararg arguments: Any?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(format, *arguments) } + } + + override fun info(format: String?, arg1: Any?, arg2: Any?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(format, arg1, arg2) } + } + + override fun info(marker: Marker?, msg: String?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(marker, msg) } + } + + override fun info(marker: Marker?, format: String?, arg: Any?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(marker, format, arg) } + } + + override fun info(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(marker, format, arg1, arg2) } + } + + override fun info(msg: String?, t: Throwable?) { + if (!isInfoEnabled) { + return + } + + withMdc { delegate.info(msg, t) } + } + + override fun error(msg: String?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(msg) } + } + + override fun error(marker: Marker?, msg: String?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(marker, msg) } + } + + override fun error(format: String?, vararg arguments: Any?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(format, *arguments) } + } + + override fun error(format: String?, arg: Any?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(format, arg) } + } + + override fun error(msg: String?, t: Throwable?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(msg, t) } + } + + override fun error(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(marker, format, arg1, arg2) } + } + + override fun error(marker: Marker?, format: String?, vararg arguments: Any?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(marker, format, *arguments) } + } + + override fun error(marker: Marker?, msg: String?, t: Throwable?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(marker, msg, t) } + } + + override fun error(format: String?, arg1: Any?, arg2: Any?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(format, arg1, arg2) } + } + + override fun error(marker: Marker?, format: String?, arg: Any?) { + if (!isErrorEnabled) { + return + } + + withMdc { delegate.error(marker, format, arg) } + } + + override fun debug(format: String?, vararg arguments: Any?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(format, *arguments) } + } + + override fun debug(format: String?, arg1: Any?, arg2: Any?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(format, arg1, arg2) } + } + + override fun debug(msg: String?, t: Throwable?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(msg, t) } + } + + override fun debug(format: String?, arg: Any?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(format, arg) } + } + + override fun debug(marker: Marker?, msg: String?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(marker, msg) } + } + + override fun debug(msg: String?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(msg) } + } + + override fun debug(marker: Marker?, msg: String?, t: Throwable?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(marker, msg, t) } + } + + override fun debug(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(marker, format, arg1, arg2) } + } + + override fun debug(marker: Marker?, format: String?, arg: Any?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(marker, format, arg) } + } + + override fun debug(marker: Marker?, format: String?, vararg arguments: Any?) { + if (!isDebugEnabled) { + return + } + + withMdc { delegate.debug(marker, format, *arguments) } + } + + override fun trace(format: String?, arg: Any?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(format, arg) } + } + + override fun trace(marker: Marker?, msg: String?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(marker, msg) } + } + + override fun trace(msg: String?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(msg) } + } + + override fun trace(msg: String?, t: Throwable?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(msg, t) } + } + + override fun trace(format: String?, arg1: Any?, arg2: Any?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(format, arg1, arg2) } + } + + override fun trace(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(marker, format, arg1, arg2) } + } + + override fun trace(marker: Marker?, format: String?, arg: Any?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(marker, format, arg) } + } + + override fun trace(marker: Marker?, format: String?, vararg argArray: Any?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(marker, format, *argArray) } + } + + override fun trace(marker: Marker?, msg: String?, t: Throwable?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(marker, msg, t) } + } + + override fun trace(format: String?, vararg arguments: Any?) { + if (!isTraceEnabled) { + return + } + + withMdc { delegate.trace(format, *arguments) } + } +} diff --git a/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LoggerImpl.kt b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LoggerImpl.kt new file mode 100644 index 00000000..1adcfdc0 --- /dev/null +++ b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LoggerImpl.kt @@ -0,0 +1,74 @@ +/* + * MIT License + * + * Copyright (c) 2019 atlarge-research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.atlarge.odcsim.engine.omega.logging + +import com.atlarge.odcsim.SimulationContext +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.slf4j.MDC +import org.slf4j.spi.LocationAwareLogger + +/** + * An actor-specific [Logger] implementation. + * + * @param ctx The owning [SimulationContext] of this logger. + */ +internal abstract class LoggerImpl internal constructor(protected val ctx: SimulationContext) : Logger { + /** + * Configure [MDC] with actor-specific information. + */ + protected inline fun withMdc(block: () -> Unit) { + MDC.put(MDC_PROCESS_REF, ctx.domain.name) + MDC.put(MDC_PROCESS_TIME, String.format("%d", ctx.clock.millis())) + try { + block() + } finally { + MDC.remove(MDC_PROCESS_TIME) + MDC.remove(MDC_PROCESS_REF) + } + } + + /** + * Mapped Diagnostic Context (MDC) attribute names. + */ + companion object { + val MDC_PROCESS_TIME = "process.time" + val MDC_PROCESS_REF = "process.ref" + + /** + * Create a [Logger] for the specified [ActorContext]. + * + * @param ctx The actor context to create the logger for. + */ + operator fun invoke(ctx: SimulationContext): Logger { + val logger = LoggerFactory.getLogger(ctx.javaClass) + return if (logger is LocationAwareLogger) { + LocationAwareLoggerImpl(ctx, logger) + } else { + LocationIgnorantLoggerImpl(ctx, logger) + } + } + } +} diff --git a/simulator/odcsim/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.SimulationEngineProvider b/simulator/odcsim/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.SimulationEngineProvider new file mode 100644 index 00000000..1131cebd --- /dev/null +++ b/simulator/odcsim/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.SimulationEngineProvider @@ -0,0 +1 @@ +com.atlarge.odcsim.engine.omega.OmegaSimulationEngineProvider |
