feat: configure logging and add OpenTelemetry support (#49)
Build & Test (NowChessSystems) TeamCity build failed

Reviewed-on: #49
This commit was merged in pull request #49.
This commit is contained in:
2026-05-10 20:31:48 +02:00
parent 989ac312d9
commit d57c488661
13 changed files with 344 additions and 98 deletions
@@ -3,6 +3,7 @@ package de.nowchess.store.service
import de.nowchess.api.dto.GameWritebackEventDto
import de.nowchess.store.domain.GameRecord
import de.nowchess.store.repository.GameRecordRepository
import io.micrometer.core.instrument.{Counter, MeterRegistry, Timer}
import jakarta.enterprise.context.ApplicationScoped
import jakarta.inject.Inject
import jakarta.transaction.Transactional
@@ -12,64 +13,84 @@ import java.time.Instant
@ApplicationScoped
class GameWritebackService:
@Inject
// scalafix:off DisableSyntax.var
@Inject
var repository: GameRecordRepository = uninitialized
// scalafix:on
@Inject
var meterRegistry: MeterRegistry = uninitialized
// scalafix:on DisableSyntax.var
private lazy val writebackTimer: Timer =
meterRegistry.timer("nowchess.store.writeback.duration")
private lazy val writebackSkipped: Counter =
meterRegistry.counter("nowchess.store.writeback.skipped")
private def gamesWrittenCounter(operation: String): Counter =
meterRegistry.counter("nowchess.store.games.written", "operation", operation)
@Transactional
def writeBack(event: GameWritebackEventDto): Unit =
writebackTimer.record(() => doWriteBack(event))
private def doWriteBack(event: GameWritebackEventDto): Unit =
repository.findByGameId(event.gameId) match
case None =>
val record = new GameRecord
record.gameId = event.gameId
record.fen = event.fen
record.pgn = event.pgn
record.moveCount = event.moveCount
record.whiteId = event.whiteId
record.whiteName = event.whiteName
record.blackId = event.blackId
record.blackName = event.blackName
record.mode = event.mode
record.resigned = event.resigned
record.limitSeconds = event.limitSeconds.map(java.lang.Integer.valueOf).orNull
record.incrementSeconds = event.incrementSeconds.map(java.lang.Integer.valueOf).orNull
record.daysPerMove = event.daysPerMove.map(java.lang.Integer.valueOf).orNull
record.whiteRemainingMs = event.whiteRemainingMs.map(java.lang.Long.valueOf).orNull
record.blackRemainingMs = event.blackRemainingMs.map(java.lang.Long.valueOf).orNull
record.incrementMs = event.incrementMs.map(java.lang.Long.valueOf).orNull
record.clockLastTickAt = event.clockLastTickAt.map(java.lang.Long.valueOf).orNull
record.clockMoveDeadline = event.clockMoveDeadline.map(java.lang.Long.valueOf).orNull
record.clockActiveColor = event.clockActiveColor.orNull
record.pendingDrawOffer = event.pendingDrawOffer.orNull
record.result = event.result.orNull
record.terminationReason = event.terminationReason.orNull
record.createdAt = Instant.now()
record.updatedAt = Instant.now()
repository.persist(record)
createRecord(event)
gamesWrittenCounter("create").increment()
case Some(r) if event.moveCount > r.moveCount || event.pgn != r.pgn =>
r.fen = event.fen
r.pgn = event.pgn
r.moveCount = event.moveCount
r.whiteId = event.whiteId
r.whiteName = event.whiteName
r.blackId = event.blackId
r.blackName = event.blackName
r.mode = event.mode
r.resigned = event.resigned
r.limitSeconds = event.limitSeconds.map(java.lang.Integer.valueOf).orNull
r.incrementSeconds = event.incrementSeconds.map(java.lang.Integer.valueOf).orNull
r.daysPerMove = event.daysPerMove.map(java.lang.Integer.valueOf).orNull
r.whiteRemainingMs = event.whiteRemainingMs.map(java.lang.Long.valueOf).orNull
r.blackRemainingMs = event.blackRemainingMs.map(java.lang.Long.valueOf).orNull
r.incrementMs = event.incrementMs.map(java.lang.Long.valueOf).orNull
r.clockLastTickAt = event.clockLastTickAt.map(java.lang.Long.valueOf).orNull
r.clockMoveDeadline = event.clockMoveDeadline.map(java.lang.Long.valueOf).orNull
r.clockActiveColor = event.clockActiveColor.orNull
r.pendingDrawOffer = event.pendingDrawOffer.orNull
r.pendingTakebackOffer = event.pendingTakebackRequest.orNull
r.result = event.result.orNull
r.terminationReason = event.terminationReason.orNull
r.updatedAt = Instant.now()
repository.merge(r)
case _ => ()
updateRecord(r, event)
gamesWrittenCounter("update").increment()
case _ =>
writebackSkipped.increment()
private def createRecord(event: GameWritebackEventDto): Unit =
val record = new GameRecord
record.gameId = event.gameId
record.fen = event.fen
record.pgn = event.pgn
record.moveCount = event.moveCount
record.whiteId = event.whiteId
record.whiteName = event.whiteName
record.blackId = event.blackId
record.blackName = event.blackName
record.mode = event.mode
record.resigned = event.resigned
applyClockFields(record, event)
record.result = event.result.orNull
record.terminationReason = event.terminationReason.orNull
record.createdAt = Instant.now()
record.updatedAt = Instant.now()
repository.persist(record)
private def updateRecord(r: GameRecord, event: GameWritebackEventDto): Unit =
r.fen = event.fen
r.pgn = event.pgn
r.moveCount = event.moveCount
r.whiteId = event.whiteId
r.whiteName = event.whiteName
r.blackId = event.blackId
r.blackName = event.blackName
r.mode = event.mode
r.resigned = event.resigned
applyClockFields(r, event)
r.pendingDrawOffer = event.pendingDrawOffer.orNull
r.pendingTakebackOffer = event.pendingTakebackRequest.orNull
r.result = event.result.orNull
r.terminationReason = event.terminationReason.orNull
r.updatedAt = Instant.now()
repository.merge(r)
private def applyClockFields(r: GameRecord, event: GameWritebackEventDto): Unit =
r.limitSeconds = event.limitSeconds.map(java.lang.Integer.valueOf).orNull
r.incrementSeconds = event.incrementSeconds.map(java.lang.Integer.valueOf).orNull
r.daysPerMove = event.daysPerMove.map(java.lang.Integer.valueOf).orNull
r.whiteRemainingMs = event.whiteRemainingMs.map(java.lang.Long.valueOf).orNull
r.blackRemainingMs = event.blackRemainingMs.map(java.lang.Long.valueOf).orNull
r.incrementMs = event.incrementMs.map(java.lang.Long.valueOf).orNull
r.clockLastTickAt = event.clockLastTickAt.map(java.lang.Long.valueOf).orNull
r.clockMoveDeadline = event.clockMoveDeadline.map(java.lang.Long.valueOf).orNull
r.clockActiveColor = event.clockActiveColor.orNull
r.pendingDrawOffer = event.pendingDrawOffer.orNull