commit 61ae182a17a32f3ee0e42380ef31849a40395886
parent cfe4bde1d5f83f95599a192334c4535ad1be7421
Author: Antoine A <>
Date: Mon, 26 May 2025 16:04:03 +0200
common: replace logback with custom logger
Diffstat:
11 files changed, 122 insertions(+), 55 deletions(-)
diff --git a/bank/build.gradle b/bank/build.gradle
@@ -52,7 +52,5 @@ shadowJar {
exclude(dependency("com.kohlschutter.junixsocket:junixsocket-core:.*"))
// CLI
exclude(dependency("com.github.ajalt.mordant:mordant:.*"))
- // Logging
- exclude(dependency("ch.qos.logback:logback-classic:.*"))
}
}
\ No newline at end of file
diff --git a/bank/src/main/resources/logback.xml b/bank/src/main/resources/logback.xml
@@ -1,15 +0,0 @@
-<configuration>
- <appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender">
- <target>System.err</target>
- <encoder>
- <pattern>%d{dd-MMM-yyyy'T'HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern>
- </encoder>
- </appender>
-
- <logger name="ktor" level="WARN" />
- <logger name="com.zaxxer.hikari" level="OFF" />
-
- <root level="DEBUG">
- <appender-ref ref="STDERR" />
- </root>
-</configuration>
-\ No newline at end of file
diff --git a/bank/src/main/resources/static/README.txt b/bank/src/main/resources/static/README.txt
@@ -1 +0,0 @@
-The spa.html file is generated from merchant-backoffice.git -> /packages/bank/
-\ No newline at end of file
diff --git a/common/build.gradle b/common/build.gradle
@@ -16,7 +16,6 @@ compileTestKotlin.kotlinOptions.jvmTarget = "17"
sourceSets.main.java.srcDirs = ["src/main/kotlin"]
dependencies {
- implementation("ch.qos.logback:logback-classic:1.5.17")
implementation("org.slf4j:slf4j-api:2.0.17")
// Crypto
implementation("org.bouncycastle:bcprov-jdk18on:1.80")
@@ -35,6 +34,7 @@ dependencies {
implementation("io.ktor:ktor-server-forwarded-header:$ktor_version")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
implementation("io.ktor:ktor-server-test-host:$ktor_version")
+ implementation("io.ktor:ktor-server-call-id:$ktor_version")
implementation("com.github.ajalt.clikt:clikt:$clikt_version")
diff --git a/common/src/main/kotlin/Cli.kt b/common/src/main/kotlin/Cli.kt
@@ -1,6 +1,6 @@
/*
* This file is part of LibEuFin.
- * Copyright (C) 2023-2024 Taler Systems S.A.
+ * Copyright (C) 2023-2025 Taler Systems S.A.
* LibEuFin is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -39,9 +39,8 @@ import org.slf4j.event.Level
private val logger: Logger = LoggerFactory.getLogger("libeufin-config")
fun cliCmd(logger: Logger, level: Level, lambda: suspend () -> Unit) {
- // Set root log level
- val root = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as ch.qos.logback.classic.Logger
- root.level = ch.qos.logback.classic.Level.convertAnSLF4JLevel(level)
+ // Set log level
+ TalerServiceProvider.currentLevel = level
// Run cli command catching all errors
try {
runBlocking {
diff --git a/common/src/main/kotlin/Config.kt b/common/src/main/kotlin/Config.kt
@@ -1,6 +1,6 @@
/*
* This file is part of LibEuFin.
- * Copyright (C) 2024 Taler Systems S.A.
+ * Copyright (C) 2024-2025 Taler Systems S.A.
* LibEuFin is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -19,8 +19,6 @@
package tech.libeufin.common
-import ch.qos.logback.core.util.Loader
-
/**
* Putting those values into the 'attributes' container because they
* are needed by the util routines that do NOT have Sandbox and Nexus
@@ -30,9 +28,9 @@ import ch.qos.logback.core.util.Loader
* into circular dependency.
*/
val VERSION: String by lazy {
- Loader.getResource(
- "version.txt", ClassLoader.getSystemClassLoader()
- ).readText()
+ ClassLoader.getSystemClassLoader().getResourceAsStream("version.txt")
+ ?.bufferedReader()
+ ?.use { it.readText() }!!
}
sealed interface ServerConfig {
diff --git a/common/src/main/kotlin/log.kt b/common/src/main/kotlin/log.kt
@@ -0,0 +1,105 @@
+/*
+ * This file is part of LibEuFin.
+ * Copyright (C) 2025 Taler Systems S.A.
+
+ * LibEuFin is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation; either version 3, or
+ * (at your option) any later version.
+
+ * LibEuFin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
+ * Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public
+ * License along with LibEuFin; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>
+ */
+
+package tech.libeufin.common
+
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
+import org.slf4j.event.Level;
+import org.slf4j.Logger;
+import org.slf4j.Marker;
+import org.slf4j.ILoggerFactory;
+import org.slf4j.IMarkerFactory;
+import org.slf4j.helpers.LegacyAbstractLogger;
+import org.slf4j.helpers.MessageFormatter;
+import org.slf4j.helpers.BasicMarkerFactory;
+import org.slf4j.helpers.BasicMDCAdapter;
+import org.slf4j.spi.MDCAdapter;
+import org.slf4j.spi.SLF4JServiceProvider;
+import java.time.LocalDateTime
+import java.time.format.DateTimeFormatter
+import java.io.PrintStream
+
+
+class TalerLogger(private val loggerName: String): LegacyAbstractLogger() {
+ private fun isLevelEnabled(level: Level): Boolean = level.toInt() >= TalerServiceProvider.currentLevel.toInt()
+ override fun isTraceEnabled(): Boolean = isLevelEnabled(Level.TRACE)
+ override fun isDebugEnabled(): Boolean = isLevelEnabled(Level.DEBUG)
+ override fun isInfoEnabled(): Boolean = isLevelEnabled(Level.INFO)
+ override fun isWarnEnabled(): Boolean = isLevelEnabled(Level.WARN)
+ override fun isErrorEnabled(): Boolean = isLevelEnabled(Level.ERROR)
+
+ override fun getFullyQualifiedCallerName(): String = loggerName
+
+ override fun handleNormalizedLoggingCall(level: Level, marker: Marker?, messagePattern: String?, arguments: Array<Any>?, throwable: Throwable?) {
+ val name = fullyQualifiedCallerName;
+ if (
+ !isLevelEnabled(level) ||
+ (name.startsWith("io.ktor") && level.toInt() < Level.WARN.toInt()) ||
+ name.startsWith("com.zaxxer.hikari")
+ ) return
+ val callId = org.slf4j.MDC.get("call-id")
+ val logEntry = buildString {
+ append(LocalDateTime.now().format(dateFormatter))
+ if (callId != null) {
+ append(' ')
+ append(callId)
+ }
+ append(' ')
+ append(level.name.padEnd(5))
+ append(' ')
+ append(name)
+ append(" - ")
+ append(MessageFormatter.basicArrayFormat(messagePattern, arguments))
+
+ /*throwable.let { t ->
+ append("\n")
+ append("${t.javaClass.simpleName}: ${t.message}")
+ t.stackTrace.take(10).forEach { stackElement ->
+ append("\n\tat $stackElement")
+ }
+ }*/
+ }
+
+ System.err.println(logEntry)
+ }
+
+ companion object {
+ private val dateFormatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy'T'HH:mm:ss.SSS")
+ }
+}
+
+class TalerServiceProvider: SLF4JServiceProvider {
+ private val markerFactory = BasicMarkerFactory()
+ private val mdcAdapter = BasicMDCAdapter()
+
+ override fun getLoggerFactory() = TalerServiceProvider
+ override fun getMarkerFactory() = markerFactory
+ override fun getMDCAdapter() = mdcAdapter
+ override fun getRequestedApiVersion() = "2.0.99"
+ override fun initialize() {}
+
+ companion object: ILoggerFactory {
+ var currentLevel = Level.TRACE
+ private val loggerMap: ConcurrentMap<String, TalerLogger> = ConcurrentHashMap()
+
+ override fun getLogger(name: String): Logger
+ = loggerMap.computeIfAbsent(name, ::TalerLogger)
+ }
+}
+\ No newline at end of file
diff --git a/common/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider b/common/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider
@@ -0,0 +1 @@
+tech.libeufin.common.TalerServiceProvider
+\ No newline at end of file
diff --git a/common/src/test/kotlin/ConfigTest.kt b/common/src/test/kotlin/ConfigTest.kt
@@ -39,12 +39,12 @@ class ConfigTest {
val secondPath = Path("tmp/test-second-conf.conf")
fun testErr(msg: String) {
- val prevOut = System.out
- val tmpOut = ByteArrayOutputStream()
- System.setOut(PrintStream(tmpOut))
+ val prevErr = System.err
+ val tmpErr = ByteArrayOutputStream()
+ System.setErr(PrintStream(tmpErr))
val result = cmd.test("dump -c $configPath")
- System.setOut(prevOut)
- val lastLog = tmpOut.asUtf8().substringAfterLast(" -- ").trimEnd('\n')
+ System.setErr(prevErr)
+ val lastLog = tmpErr.asUtf8().substringAfterLast(" - ").trimEnd('\n')
assertEquals(1, result.statusCode, lastLog)
assertEquals(msg, lastLog, lastLog)
}
diff --git a/nexus/build.gradle b/nexus/build.gradle
@@ -57,13 +57,11 @@ shadowJar {
exclude(dependency("io.ktor:ktor-serialization-kotlinx-json:.*"))
// Postgres unix socket driver
exclude(dependency("com.kohlschutter.junixsocket:junixsocket-core:.*"))
- // Logging
- exclude(dependency("ch.qos.logback:logback-classic:.*"))
// CIO engine
exclude(dependency("io.ktor:ktor-client-cio:.*"))
// Crypto
exclude(dependency("org.bouncycastle:.*"))
- // CLI
+ // CLI
exclude(dependency("com.github.ajalt.mordant:mordant:.*"))
}
}
\ No newline at end of file
diff --git a/nexus/src/main/resources/logback.xml b/nexus/src/main/resources/logback.xml
@@ -1,15 +0,0 @@
-<configuration>
- <appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender">
- <target>System.err</target>
- <encoder>
- <pattern>%d{dd-MMM-yyyy'T'HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern>
- </encoder>
- </appender>
-
- <logger name="ktor" level="WARN" />
- <logger name="com.zaxxer.hikari" level="OFF" />
-
- <root level="DEBUG">
- <appender-ref ref="STDERR" />
- </root>
-</configuration>
-\ No newline at end of file