#2 Logging mit kotlin-logging implementiert

This commit is contained in:
Hartmut Bauer committed 2026-02-06 23:50:15 +01:00
1 parent 698490a6f8
commit 4d827948f8
15 files changed
+92 -51

No files matched your search

+4 -5
View File
@@ -32,7 +32,7 @@ kotlin {
implementation(libs.ktor.client) implementation(libs.ktor.client)
implementation(libs.ktor.clientCio) implementation(libs.ktor.clientCio)
implementation(libs.ktor.clientAuth) implementation(libs.ktor.clientAuth)
implementation(libs.ktor.clientLogging) // implementation(libs.ktor.clientLogging)
implementation(libs.ktor.clientContentNegotiation) implementation(libs.ktor.clientContentNegotiation)
implementation(libs.ktor.serializationJson) implementation(libs.ktor.serializationJson)
implementation(libs.ktor.serverCore) implementation(libs.ktor.serverCore)
@@ -43,6 +43,8 @@ kotlin {
implementation(libs.koin.compose) implementation(libs.koin.compose)
implementation(libs.koin.compose.viewmodel) implementation(libs.koin.compose.viewmodel)
// implementation(libs.koin.compose.viewmodel.navigation) // implementation(libs.koin.compose.viewmodel.navigation)
implementation(libs.kotlin.logging)
implementation(libs.logback)
implementation(projects.shared) implementation(projects.shared)
} }
// commonTest.dependencies { // commonTest.dependencies {
@@ -51,10 +53,7 @@ kotlin {
jvmMain.dependencies { jvmMain.dependencies {
implementation(compose.desktop.currentOs) implementation(compose.desktop.currentOs)
implementation(libs.kotlinx.coroutinesSwing) implementation(libs.kotlinx.coroutinesSwing)
implementation(libs.ktor.clientJava) // ?? // implementation(libs.ktor.clientJava) // ??
// implementation(libs.ktor.serverCio)
// implementation(libs.ktor.serverCore)
// implementation(libs.ktor.serverNetty)
implementation(libs.ktor.serverAuthJwt) // nicht f. js, wasmJs implementation(libs.ktor.serverAuthJwt) // nicht f. js, wasmJs
} }
// wasmJsMain.dependencies { // wasmJsMain.dependencies {
+2 -2
View File
@@ -1,3 +1,3 @@
#auto generated #auto generated
#Fri Feb 06 16:36:58 CET 2026 #Fri Feb 06 23:31:36 CET 2026
build.number=212 build.number=221
@@ -14,6 +14,7 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import io.github.oshai.kotlinlogging.KotlinLogging
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.client.statement.* import io.ktor.client.statement.*
@@ -27,6 +28,8 @@ import org.jetbrains.compose.resources.painterResource
import org.koin.compose.koinInject import org.koin.compose.koinInject
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
private val logger = KotlinLogging.logger {}
@Composable @Composable
@Preview @Preview
fun App() { fun App() {
@@ -52,7 +55,7 @@ fun App() {
} }
Button(onClick = { Button(onClick = {
CoroutineScope(Dispatchers.Default).launch { CoroutineScope(Dispatchers.Default).launch {
println("requesting '/test'") logger.debug { "requesting '/test'" }
test.request("/test") test.request("/test")
} }
}) { }) {
@@ -77,10 +80,11 @@ class TestAuth(val httpClient: HttpClient) {
private val srv = "https://kas.keinekekse.net/" private val srv = "https://kas.keinekekse.net/"
suspend fun request(url: String) { suspend fun request(url: String) {
println("connecting") logger.debug { "connecting" }
val response = httpClient.get(srv + url) val response = httpClient.get(srv + url)
println("call to $url") val body = response.bodyAsText()
println(response.status) logger.debug { "call to $url" }
println(response.bodyAsText()) logger.debug { response.status }
logger.debug { body }
} }
} }
@@ -4,13 +4,13 @@ import net.keinekekse.version.Version
object VERSION: Version { object VERSION: Version {
override val version = "1.0.0" override val version = "1.0.0"
override val fullVersion = "1.0.0+build.212" override val fullVersion = "1.0.0+build.221"
override val major = 1 override val major = 1
override val minor = 0 override val minor = 0
override val patch = 0 override val patch = 0
override val preRelease = "" override val preRelease = ""
override val buildMetadata = "build.212" override val buildMetadata = "build.221"
override val buildDate = "2026-02-06T15:36:58.060625Z" override val buildDate = "2026-02-06T22:31:36.863331Z"
override val displayName = "Books App" override val displayName = "Books App"
override val copyright = "Copyright (c) 2026 Hartmut Bauer" override val copyright = "Copyright (c) 2026 Hartmut Bauer"
override val name = "books" override val name = "books"
@@ -0,0 +1,12 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT"/>
</root>
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.netty" level="INFO"/>
</configuration>
@@ -1,5 +1,6 @@
package net.keinekekse.app.auth package net.keinekekse.app.auth
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CompletableDeferred
import net.keinekekse.app.config.ConfigProperties import net.keinekekse.app.config.ConfigProperties
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
@@ -7,6 +8,8 @@ import java.security.MessageDigest
import java.security.SecureRandom import java.security.SecureRandom
import java.util.* import java.util.*
private val logger = KotlinLogging.logger {}
sealed interface AuthResult { sealed interface AuthResult {
class Success : AuthResult class Success : AuthResult
@@ -1,6 +1,7 @@
package net.keinekekse.app.auth package net.keinekekse.app.auth
import com.auth0.jwt.JWT import com.auth0.jwt.JWT
import io.github.oshai.kotlinlogging.KotlinLogging
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.call.* import io.ktor.client.call.*
import io.ktor.client.plugins.* import io.ktor.client.plugins.*
@@ -27,6 +28,8 @@ import java.net.Socket
import java.util.* import java.util.*
import kotlin.use import kotlin.use
private val logger = KotlinLogging.logger {}
@Single @Single
class Authenticator( class Authenticator(
val tokenService: TokenService, val tokenService: TokenService,
@@ -54,11 +57,11 @@ class Authenticator(
val access = JWT.decode(tokens.accessToken) val access = JWT.decode(tokens.accessToken)
val refresh = JWT.decode(tokens.refreshToken) val refresh = JWT.decode(tokens.refreshToken)
val now = Date() val now = Date()
println("Now: $now") logger.debug { "Now: $now" }
println("Token expires at: ${access.expiresAt}") logger.debug { "Token expires at: ${access.expiresAt}" }
println("Token is expired: ${access.expiresAt.before(Date())}") logger.debug { "Token is expired: ${access.expiresAt.before(Date())}" }
println("Refresh token expires at: ${refresh.expiresAt}") logger.debug { "Refresh token expires at: ${refresh.expiresAt}" }
println("Refresh token is expired: ${refresh.expiresAt.before(Date())}") logger.debug { "Refresh token is expired: ${refresh.expiresAt.before(Date())}" }
if (access.expiresAt.before(now)) { if (access.expiresAt.before(now)) {
// access_token ungültig // access_token ungültig
if (refresh.expiresAt.before(now)) { if (refresh.expiresAt.before(now)) {
@@ -66,10 +69,10 @@ class Authenticator(
return doAuthentication() return doAuthentication()
} else { } else {
val result = requestRefreshToken(tokens) val result = requestRefreshToken(tokens)
if (result != null) { return if (result != null) {
return AuthResult.Success() AuthResult.Success()
} else { } else {
return doAuthentication() doAuthentication()
} }
} }
} else { } else {
@@ -145,32 +148,33 @@ class Authenticator(
else -> { else -> {
tokenService.setTokens(null) tokenService.setTokens(null)
println("Error refreshing tokens: ") logger.debug { "Error refreshing tokens: " }
var error = "" var error = ""
var errorDetails = "" var errorDetails = ""
when (val err = result.first) { when (val err = result.first) {
is AuthResult.Fatal -> { is AuthResult.Fatal -> {
println(err.code) logger.debug { err.code }
error = err.message ?: "" error = err.message ?: ""
errorDetails = err.details ?: "" errorDetails = err.details ?: ""
} }
is AuthResult.Error -> { is AuthResult.Error -> {
println(err.code) logger.debug { err.code }
error = err.message ?: "" error = err.message ?: ""
errorDetails = err.details ?: "" errorDetails = err.details ?: ""
} }
is AuthResult.Success -> println("Success ???") is AuthResult.Success -> println("Success ???")
} }
println("Status: ${response.status}") logger.debug { "Status: ${response.status}" }
println("Error: $error") logger.debug { "Error: #$error#" }
println("Description: $errorDetails") logger.debug { "Description: $errorDetails" }
if (error == "invalid_grant") { return if (error == "invalid_grant") {
// session expired || token is invalid // session expired || token is invalid
return reAuthenticate() logger.debug { "Session expired or token invalid. Re-authenticating..." }
reAuthenticate()
} else { } else {
return null null
} }
} }
} }
@@ -182,7 +186,7 @@ class Authenticator(
Socket(KeycloakConfig.LOCALHOST, port).use { true } Socket(KeycloakConfig.LOCALHOST, port).use { true }
} catch (e: Exception) { } catch (e: Exception) {
// Port is closed or unreachable // Port is closed or unreachable
println("${e.javaClass.simpleName}: ${e.message}") logger.debug { "${e.javaClass.simpleName}: ${e.message}" }
false false
} }
@@ -341,7 +345,6 @@ class Authenticator(
data class TokenErrorResponse( data class TokenErrorResponse(
@SerialName("error") val error: String, @SerialName("error") val error: String,
@SerialName("error_description") val errorDescription: String?, @SerialName("error_description") val errorDescription: String?,
@SerialName("session_state") val sessionState: String?,
) )
private suspend fun requestTokens(config: AuthRun, authCode: String?): AuthResult { private suspend fun requestTokens(config: AuthRun, authCode: String?): AuthResult {
@@ -3,9 +3,12 @@ package net.keinekekse.app
//import org.koin.plugin.module.dsl.startKoin //import org.koin.plugin.module.dsl.startKoin
import androidx.compose.ui.window.Window import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application import androidx.compose.ui.window.application
import io.github.oshai.kotlinlogging.KotlinLogging
import net.keinekekse.app.books.di.KoinApp import net.keinekekse.app.books.di.KoinApp
import org.koin.plugin.module.dsl.startKoin import org.koin.plugin.module.dsl.startKoin
private val logger = KotlinLogging.logger {}
//@PropertyValue("kc_server") //@PropertyValue("kc_server")
//var keycloakServer: String = "" //var keycloakServer: String = ""
// //
@@ -18,7 +21,8 @@ import org.koin.plugin.module.dsl.startKoin
fun main() = application { fun main() = application {
startKoin<KoinApp>() startKoin<KoinApp>()
logger.info { "Koin started" }
logger.debug { "Debugging Koin start" }
// val config: ConfigProperties = koinInject() // val config: ConfigProperties = koinInject()
// val settings: Settings = koinInject() // val settings: Settings = koinInject()
@@ -1,6 +1,7 @@
package net.keinekekse.app.service package net.keinekekse.app.service
import com.auth0.jwt.JWT import com.auth0.jwt.JWT
import io.github.oshai.kotlinlogging.KotlinLogging
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.plugins.* import io.ktor.client.plugins.*
import io.ktor.client.plugins.auth.* import io.ktor.client.plugins.auth.*
@@ -13,6 +14,8 @@ import net.keinekekse.app.auth.Authenticator
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
import java.util.* import java.util.*
private val logger = KotlinLogging.logger {}
@OptIn(ExperimentalSerializationApi::class) @OptIn(ExperimentalSerializationApi::class)
@Single @Single
fun createHttpClient(authenticator: Authenticator): HttpClient { fun createHttpClient(authenticator: Authenticator): HttpClient {
@@ -20,32 +23,32 @@ fun createHttpClient(authenticator: Authenticator): HttpClient {
install(Auth) { install(Auth) {
bearer { bearer {
loadTokens { loadTokens {
println("HTTPClient: loadTokens") logger.debug { "HTTPClient: loadTokens" }
// tokenService.getTokens() // tokenService.getTokens()
authenticator.tokenService.getTokens() authenticator.tokenService.getTokens()
} }
refreshTokens { refreshTokens {
println("HTTPClient: refreshTokens") logger.debug { "HTTPClient: refreshTokens" }
// val tokens = authenticator.tokenService.getTokens() // val tokens = authenticator.tokenService.getTokens()
if (oldTokens != null) { if (oldTokens != null) {
val access = JWT.decode(oldTokens?.accessToken) val access = JWT.decode(oldTokens?.accessToken)
val refresh = JWT.decode(oldTokens?.refreshToken) val refresh = JWT.decode(oldTokens?.refreshToken)
val now = Date() val now = Date()
println("Now: $now") logger.debug { "Now: $now" }
println("Token expires at: ${access.expiresAt}") logger.debug { "Token expires at: ${access.expiresAt}" }
println("Token is expired: ${access.expiresAt.before(Date())}") logger.debug { "Token is expired: ${access.expiresAt.before(Date())}" }
println("Refresh token expires at: ${refresh.expiresAt}") logger.debug { "Refresh token expires at: ${refresh.expiresAt}" }
println("Refresh token is expired: ${refresh.expiresAt.before(Date())}") logger.debug { "Refresh token is expired: ${refresh.expiresAt.before(Date())}" }
if (refresh.expiresAt.before(now)) { if (refresh.expiresAt.before(now)) {
println("refresh token expired - HOW TO authenticate") logger.debug { "refresh token expired - HOW TO authenticate" }
authenticator.reAuthenticate() authenticator.reAuthenticate()
} else { } else {
println("refresh token valid - request new tokens") logger.debug { "refresh token valid - request new tokens" }
authenticator.requestRefreshToken(oldTokens) authenticator.requestRefreshToken(oldTokens)
} }
} else { } else {
println("no tokens - authenticate") logger.debug { "no tokens - authenticate" }
authenticator.reAuthenticate() authenticator.reAuthenticate()
} }
} }
@@ -68,9 +71,9 @@ fun createHttpClient(authenticator: Authenticator): HttpClient {
} }
client.plugin(HttpSend).intercept { request -> client.plugin(HttpSend).intercept { request ->
println("HTTPClient: intercept ${request.url}") logger.debug { "HTTPClient: intercept ${request.url}" }
val originalCall = execute(request) val originalCall = execute(request)
println("HTTPClient: response status ${originalCall.response.status}") logger.debug { "HTTPClient: response status ${originalCall.response.status}" }
originalCall originalCall
// val originalCall = execute(request) // val originalCall = execute(request)
// if (originalCall.response.status.value !in 100..399) { // if (originalCall.response.status.value !in 100..399) {
@@ -1,8 +1,11 @@
package net.keinekekse.app.service package net.keinekekse.app.service
import io.github.oshai.kotlinlogging.KotlinLogging
import io.ktor.client.plugins.auth.providers.* import io.ktor.client.plugins.auth.providers.*
import org.koin.core.annotation.Single import org.koin.core.annotation.Single
private val logger = KotlinLogging.logger {}
@Single @Single
class TokenService { class TokenService {
private var tokens: BearerTokens? = null private var tokens: BearerTokens? = null
@@ -11,9 +14,9 @@ class TokenService {
fun setTokens(tokens: BearerTokens? = null) { fun setTokens(tokens: BearerTokens? = null) {
this.tokens = tokens this.tokens = tokens
println("set tokens") logger.debug { "set tokens" }
println("Access Token: ${tokens?.accessToken}") logger.debug { "Access Token: ${tokens?.accessToken}" }
println("Refresh Token: ${tokens?.refreshToken}") logger.debug { "Refresh Token: ${tokens?.refreshToken}" }
} }
} }
+2
View File
@@ -17,6 +17,7 @@ koin-bom = "4.2.0-RC1"
koin-plugin = "0.3.0" koin-plugin = "0.3.0"
ktor = "3.4.0" ktor = "3.4.0"
logback = "1.5.26" logback = "1.5.26"
kotlinLogging = "7.0.14"
material3 = "1.10.0-alpha05" material3 = "1.10.0-alpha05"
settings = "1.3.0" settings = "1.3.0"
dataStore = "1.2.0" dataStore = "1.2.0"
@@ -41,6 +42,7 @@ kotlinx-coroutinesSwing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-s
kotlinx-html = { module = "org.jetbrains.kotlinx:kotlinx-html", version.ref = "kotlinx-html" } kotlinx-html = { module = "org.jetbrains.kotlinx:kotlinx-html", version.ref = "kotlinx-html" }
kotlin-wrappersCss = { module = "org.jetbrains.kotlin-wrappers:kotlin-css-jvm", version.ref = "kotlin-wrappers-css" } kotlin-wrappersCss = { module = "org.jetbrains.kotlin-wrappers:kotlin-css-jvm", version.ref = "kotlin-wrappers-css" }
logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
kotlin-logging = { module = "io.github.oshai:kotlin-logging", version.ref = "kotlinLogging" }
ktor-serverCore = { module = "io.ktor:ktor-server-core", version.ref = "ktor" } ktor-serverCore = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }
ktor-serverNetty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" } ktor-serverNetty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" }
ktor-serverCio = { module = "io.ktor:ktor-server-cio", version.ref = "ktor" } ktor-serverCio = { module = "io.ktor:ktor-server-cio", version.ref = "ktor" }
+1
View File
@@ -23,6 +23,7 @@ dependencies {
implementation(masterLibs.utilLibrary) implementation(masterLibs.utilLibrary)
// implementation(projects.shared) // err wg. doppelter jar ?? // implementation(projects.shared) // err wg. doppelter jar ??
implementation(libs.logback) implementation(libs.logback)
implementation(libs.kotlin.logging)
implementation(libs.ktor.serverCore) implementation(libs.ktor.serverCore)
implementation(libs.ktor.serverNetty) implementation(libs.ktor.serverNetty)
implementation(libs.ktor.serverAuth) implementation(libs.ktor.serverAuth)
@@ -1,14 +1,17 @@
package net.keinekekse.app package net.keinekekse.app
import io.github.oshai.kotlinlogging.KotlinLogging
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.engine.* import io.ktor.server.engine.*
import io.ktor.server.netty.* import io.ktor.server.netty.*
import net.keinekekse.app.server.VERSION import net.keinekekse.app.server.VERSION
import net.keinekekse.app.server.config.configureSecurity import net.keinekekse.app.server.config.configureSecurity
private val logger = KotlinLogging.logger {}
fun main() { fun main() {
// embeddedServer(Netty, port = SERVER_PORT, host = "0.0.0.0", module = Application::module) // embeddedServer(Netty, port = SERVER_PORT, host = "0.0.0.0", module = Application::module)
println("**\n** Starting ${VERSION.displayName} - ${VERSION.description} ${VERSION.fullVersion}\n**") logger.debug { "**\n** Starting ${VERSION.displayName} - ${VERSION.description} ${VERSION.fullVersion}\n**" }
embeddedServer(Netty, port = 7770, host = "0.0.0.0", module = Application::module) embeddedServer(Netty, port = 7770, host = "0.0.0.0", module = Application::module)
.start(wait = true) .start(wait = true)
} }
@@ -1,6 +1,7 @@
package net.keinekekse.app.server.config package net.keinekekse.app.server.config
import com.auth0.jwk.UrlJwkProvider import com.auth0.jwk.UrlJwkProvider
import io.github.oshai.kotlinlogging.KotlinLogging
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.application.* import io.ktor.server.application.*
import io.ktor.server.auth.* import io.ktor.server.auth.*
@@ -10,6 +11,8 @@ import io.ktor.server.routing.*
import net.keinekekse.app.server.VERSION import net.keinekekse.app.server.VERSION
import java.net.URI import java.net.URI
private val logger = KotlinLogging.logger {}
fun Application.configureSecurity() { fun Application.configureSecurity() {
// TODO die Werte müssen aus der Config kommen // TODO die Werte müssen aus der Config kommen
+1
View File
@@ -26,6 +26,7 @@ kotlin {
commonMain.dependencies { commonMain.dependencies {
implementation(libs.bundles.kotlin.compose) implementation(libs.bundles.kotlin.compose)
implementation(libs.ktor.serializationJson) implementation(libs.ktor.serializationJson)
implementation(libs.ktor.clientLogging)
} }
jvmMain.dependencies { jvmMain.dependencies {