Repo created
This commit is contained in:
parent
75dc487a7a
commit
39c29d175b
6317 changed files with 388324 additions and 2 deletions
15
core/logging/impl-composite/build.gradle.kts
Normal file
15
core/logging/impl-composite/build.gradle.kts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
plugins {
|
||||
id(ThunderbirdPlugins.Library.kmp)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "net.thunderbird.core.logging.composite"
|
||||
}
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
commonMain.dependencies {
|
||||
implementation(projects.core.logging.api)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package net.thunderbird.core.logging.composite
|
||||
|
||||
import net.thunderbird.core.logging.LogLevel
|
||||
import net.thunderbird.core.logging.LogLevelProvider
|
||||
import net.thunderbird.core.logging.LogSink
|
||||
|
||||
/**
|
||||
* A [LogSink] that aggregates multiple [LogSink] and forwards log events to them.
|
||||
*
|
||||
* This [CompositeLogSink] is useful when you want to log messages to multiple destinations
|
||||
* (e.g., console, file, etc.) without having to manage each [LogSink] individually.
|
||||
*
|
||||
* It checks the log level of each event against its own level and forwards the event
|
||||
* to all managed sinks that can handle the event's level.
|
||||
*
|
||||
* @param level The minimum log level this sink will process. Log events with a lower priority will be ignored.
|
||||
* @param manager The [CompositeLogSinkManager] that manages the collection of sinks.
|
||||
*/
|
||||
interface CompositeLogSink : LogSink {
|
||||
val manager: CompositeLogSinkManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [CompositeLogSink] with the specified log level and manager.
|
||||
*
|
||||
* @param logLevelProvider The minimum [LogLevel] for messages to be logged.
|
||||
* @param manager The [CompositeLogSinkManager] that manages the collection of sinks.
|
||||
* @param sinks A list of [LogSink] instances to be managed by this composite sink.
|
||||
* @return A new instance of [CompositeLogSink].
|
||||
*/
|
||||
fun CompositeLogSink(
|
||||
logLevelProvider: LogLevelProvider,
|
||||
manager: CompositeLogSinkManager = DefaultLogSinkManager(),
|
||||
sinks: List<LogSink> = emptyList(),
|
||||
): CompositeLogSink {
|
||||
return DefaultCompositeLogSink(logLevelProvider, manager, sinks)
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package net.thunderbird.core.logging.composite
|
||||
|
||||
import net.thunderbird.core.logging.LogSink
|
||||
|
||||
/**
|
||||
* CompositeLogSinkManager is responsible for managing a collection of [LogSink] instances.
|
||||
*/
|
||||
interface CompositeLogSinkManager {
|
||||
|
||||
/**
|
||||
* Retrieves all [LogSink] instances managed by this manager.
|
||||
*
|
||||
* @return A list of all sinks.
|
||||
*/
|
||||
fun getAll(): List<LogSink>
|
||||
|
||||
/**
|
||||
* Adds a [LogSink] to the manager.
|
||||
*
|
||||
* @param sink The [LogSink] to add.
|
||||
*/
|
||||
fun add(sink: LogSink)
|
||||
|
||||
/**
|
||||
* Adds multiple [LogSink] instances to the manager.
|
||||
*
|
||||
* @param sinks The list of [LogSink] to add.
|
||||
*/
|
||||
fun addAll(sinks: List<LogSink>)
|
||||
|
||||
/**
|
||||
* Removes a [LogSink] from the manager.
|
||||
*
|
||||
* @param sink The [LogSink] to remove.
|
||||
*/
|
||||
fun remove(sink: LogSink)
|
||||
|
||||
/**
|
||||
* Removes all [LogSink] instances from the manager.
|
||||
*/
|
||||
fun removeAll()
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package net.thunderbird.core.logging.composite
|
||||
|
||||
import net.thunderbird.core.logging.LogEvent
|
||||
import net.thunderbird.core.logging.LogLevel
|
||||
import net.thunderbird.core.logging.LogLevelProvider
|
||||
import net.thunderbird.core.logging.LogSink
|
||||
|
||||
internal class DefaultCompositeLogSink(
|
||||
private val logLevelProvider: LogLevelProvider,
|
||||
override val manager: CompositeLogSinkManager = DefaultLogSinkManager(),
|
||||
sinks: List<LogSink> = emptyList(),
|
||||
) : CompositeLogSink {
|
||||
override val level: LogLevel get() = logLevelProvider.current()
|
||||
|
||||
init {
|
||||
manager.addAll(sinks)
|
||||
}
|
||||
|
||||
override fun log(event: LogEvent) {
|
||||
if (canLog(event.level)) {
|
||||
manager.getAll().forEach { sink ->
|
||||
if (sink.canLog(event.level)) {
|
||||
sink.log(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package net.thunderbird.core.logging.composite
|
||||
|
||||
import net.thunderbird.core.logging.LogSink
|
||||
|
||||
/**
|
||||
* Default implementation of [CompositeLogSinkManager] that manages a collection of [LogSink] instances.
|
||||
*/
|
||||
internal class DefaultLogSinkManager : CompositeLogSinkManager {
|
||||
private val sinks: MutableList<LogSink> = mutableListOf()
|
||||
|
||||
override fun getAll(): List<LogSink> {
|
||||
return sinks.toList()
|
||||
}
|
||||
|
||||
override fun addAll(sinks: List<LogSink>) {
|
||||
sinks.forEach {
|
||||
add(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun add(sink: LogSink) {
|
||||
if (sink !in sinks) {
|
||||
sinks.add(sink)
|
||||
}
|
||||
}
|
||||
|
||||
override fun remove(sink: LogSink) {
|
||||
if (sink in sinks) {
|
||||
sinks.remove(sink)
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeAll() {
|
||||
sinks.clear()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
package net.thunderbird.core.logging.composite
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.hasSize
|
||||
import assertk.assertions.isEmpty
|
||||
import assertk.assertions.isEqualTo
|
||||
import net.thunderbird.core.logging.LogEvent
|
||||
import net.thunderbird.core.logging.LogLevel
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultCompositeLogSinkTest {
|
||||
|
||||
@Test
|
||||
fun `init should set initial sinks`() {
|
||||
// Arrange
|
||||
val sink1 = FakeLogSink(LogLevel.INFO)
|
||||
val sink2 = FakeLogSink(LogLevel.INFO)
|
||||
val sinkManager = FakeCompositeLogSinkManager()
|
||||
|
||||
// Act
|
||||
DefaultCompositeLogSink(
|
||||
logLevelProvider = { LogLevel.INFO },
|
||||
manager = sinkManager,
|
||||
sinks = listOf(sink1, sink2),
|
||||
)
|
||||
|
||||
// Assert
|
||||
assertThat(sinkManager.sinks).hasSize(2)
|
||||
assertThat(sinkManager.sinks[0]).isEqualTo(sink1)
|
||||
assertThat(sinkManager.sinks[1]).isEqualTo(sink2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `log should log to all sinks`() {
|
||||
// Arrange
|
||||
val sink1 = FakeLogSink(LogLevel.INFO)
|
||||
val sink2 = FakeLogSink(LogLevel.INFO)
|
||||
val sinkManager = FakeCompositeLogSinkManager(mutableListOf(sink1, sink2))
|
||||
|
||||
val testSubject = DefaultCompositeLogSink(
|
||||
logLevelProvider = { LogLevel.INFO },
|
||||
manager = sinkManager,
|
||||
)
|
||||
|
||||
// Act
|
||||
testSubject.log(LOG_EVENT)
|
||||
|
||||
// Assert
|
||||
assertThat(sink1.events).hasSize(1)
|
||||
assertThat(sink2.events).hasSize(1)
|
||||
assertThat(sink1.events[0]).isEqualTo(LOG_EVENT)
|
||||
assertThat(sink2.events[0]).isEqualTo(LOG_EVENT)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `log should not log if level is below threshold`() {
|
||||
// Arrange
|
||||
val sink1 = FakeLogSink(LogLevel.INFO)
|
||||
val sink2 = FakeLogSink(LogLevel.INFO)
|
||||
val sinkManager = FakeCompositeLogSinkManager(mutableListOf(sink1, sink2))
|
||||
|
||||
val testSubject = DefaultCompositeLogSink(
|
||||
logLevelProvider = { LogLevel.WARN },
|
||||
manager = sinkManager,
|
||||
)
|
||||
|
||||
// Act
|
||||
testSubject.log(LOG_EVENT)
|
||||
|
||||
// Assert
|
||||
assertThat(sink1.events).isEmpty()
|
||||
assertThat(sink2.events).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `log should not log if sink level is below threshold`() {
|
||||
// Arrange
|
||||
val sink1 = FakeLogSink(LogLevel.WARN)
|
||||
val sink2 = FakeLogSink(LogLevel.INFO)
|
||||
val sinkManager = FakeCompositeLogSinkManager(mutableListOf(sink1, sink2))
|
||||
|
||||
val testSubject = DefaultCompositeLogSink(
|
||||
logLevelProvider = { LogLevel.INFO },
|
||||
manager = sinkManager,
|
||||
)
|
||||
|
||||
// Act
|
||||
testSubject.log(LOG_EVENT)
|
||||
|
||||
// Assert
|
||||
assertThat(sink1.events).isEmpty()
|
||||
assertThat(sink2.events).hasSize(1)
|
||||
assertThat(sink2.events[0]).isEqualTo(LOG_EVENT)
|
||||
}
|
||||
|
||||
private companion object Companion {
|
||||
const val TIMESTAMP = 0L
|
||||
|
||||
val LOG_EVENT = LogEvent(
|
||||
level = LogLevel.INFO,
|
||||
tag = "TestTag",
|
||||
message = "Test message",
|
||||
timestamp = TIMESTAMP,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
package net.thunderbird.core.logging.composite
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.contains
|
||||
import assertk.assertions.hasSize
|
||||
import assertk.assertions.isEmpty
|
||||
import kotlin.test.Test
|
||||
import net.thunderbird.core.logging.LogLevel
|
||||
|
||||
class DefaultLogSinkManagerTest {
|
||||
|
||||
@Test
|
||||
fun `should have no sinks initially`() {
|
||||
// Arrange
|
||||
val sinkManager = DefaultLogSinkManager()
|
||||
|
||||
// Act
|
||||
val sinks = sinkManager.getAll()
|
||||
|
||||
// Assert
|
||||
assertThat(sinks).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should add and retrieve sinks`() {
|
||||
// Arrange
|
||||
val sinkManager = DefaultLogSinkManager()
|
||||
val sink = FakeLogSink(LogLevel.INFO)
|
||||
sinkManager.add(sink)
|
||||
|
||||
// Act
|
||||
val sinks = sinkManager.getAll()
|
||||
|
||||
// Assert
|
||||
assertThat(sinks.contains(sink))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should add multiple sinks`() {
|
||||
// Arrange
|
||||
val sinkManager = DefaultLogSinkManager()
|
||||
val sink1 = FakeLogSink(LogLevel.INFO)
|
||||
val sink2 = FakeLogSink(LogLevel.DEBUG)
|
||||
sinkManager.addAll(listOf(sink1, sink2))
|
||||
|
||||
// Act
|
||||
val sinks = sinkManager.getAll()
|
||||
|
||||
// Assert
|
||||
assertThat(sinks).hasSize(2)
|
||||
assertThat(sinks).contains(sink1)
|
||||
assertThat(sinks).contains(sink2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should remove sink`() {
|
||||
// Arrange
|
||||
val sinkManager = DefaultLogSinkManager()
|
||||
val sink = FakeLogSink(LogLevel.INFO)
|
||||
sinkManager.add(sink)
|
||||
|
||||
// Act
|
||||
sinkManager.remove(sink)
|
||||
val sinks = sinkManager.getAll()
|
||||
|
||||
// Assert
|
||||
assertThat(sinks).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should clear all sinks`() {
|
||||
// Arrange
|
||||
val sinkManager = DefaultLogSinkManager()
|
||||
val sink1 = FakeLogSink(LogLevel.INFO)
|
||||
val sink2 = FakeLogSink(LogLevel.DEBUG)
|
||||
sinkManager.add(sink1)
|
||||
sinkManager.add(sink2)
|
||||
|
||||
// Act
|
||||
sinkManager.removeAll()
|
||||
val sinks = sinkManager.getAll()
|
||||
|
||||
// Assert
|
||||
assertThat(sinks).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not add duplicate sinks`() {
|
||||
// Arrange
|
||||
val sinkManager = DefaultLogSinkManager()
|
||||
val sink = FakeLogSink(LogLevel.INFO)
|
||||
sinkManager.add(sink)
|
||||
|
||||
// Act
|
||||
sinkManager.add(sink)
|
||||
val sinks = sinkManager.getAll()
|
||||
|
||||
// Assert
|
||||
assertThat(sinks).hasSize(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not remove non-existent sinks`() {
|
||||
// Arrange
|
||||
val sinkManager = DefaultLogSinkManager()
|
||||
val sink = FakeLogSink(LogLevel.INFO)
|
||||
|
||||
// Act
|
||||
sinkManager.remove(sink)
|
||||
val sinks = sinkManager.getAll()
|
||||
|
||||
// Assert
|
||||
assertThat(sinks).isEmpty()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package net.thunderbird.core.logging.composite
|
||||
|
||||
import net.thunderbird.core.logging.LogSink
|
||||
|
||||
class FakeCompositeLogSinkManager(
|
||||
val sinks: MutableList<LogSink> = mutableListOf(),
|
||||
) : CompositeLogSinkManager {
|
||||
|
||||
override fun getAll(): List<LogSink> = sinks
|
||||
|
||||
override fun add(sink: LogSink) = Unit
|
||||
|
||||
override fun addAll(sinks: List<LogSink>) {
|
||||
this.sinks.addAll(sinks)
|
||||
}
|
||||
|
||||
override fun remove(sink: LogSink) = Unit
|
||||
|
||||
override fun removeAll() = Unit
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package net.thunderbird.core.logging.composite
|
||||
|
||||
import net.thunderbird.core.logging.LogEvent
|
||||
import net.thunderbird.core.logging.LogLevel
|
||||
import net.thunderbird.core.logging.LogSink
|
||||
|
||||
class FakeLogSink(override val level: LogLevel) : LogSink {
|
||||
|
||||
val events = mutableListOf<LogEvent>()
|
||||
|
||||
override fun log(event: LogEvent) {
|
||||
events.add(event)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue