Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
afa848e
feat: implement error logging system for player connection failures
TheBjoRedCraft Feb 8, 2026
51841df
feat: update error logging to use dynamic error code in PlayerConnect…
TheBjoRedCraft Feb 8, 2026
38cd40e
feat: add core error command for viewing player errors and error codes
TheBjoRedCraft Feb 8, 2026
a0504ed
feat: enhance error logging and disconnect message handling in authen…
TheBjoRedCraft Feb 8, 2026
de1eb94
feat: refactor disconnect message handling to include error codes in …
TheBjoRedCraft Feb 8, 2026
ded7a43
feat: add error logging for failed authentication attempts in Authent…
TheBjoRedCraft Feb 8, 2026
a4f943d
feat: rename AuthentificationService to AuthenticationService and upd…
TheBjoRedCraft Feb 8, 2026
ef2f04f
feat: fix typo in logError method parameter name in SurfCoreApi
TheBjoRedCraft Feb 8, 2026
14208a3
feat: add player UUID to error logging and enhance error details in S…
TheBjoRedCraft Feb 8, 2026
eb50f72
Initial plan
Copilot Feb 8, 2026
505a898
feat: enhance error logging with stacktrace, location, and deduplication
Copilot Feb 8, 2026
68beaf7
feat: implement separate system error logging infrastructure
Copilot Feb 8, 2026
5ed9264
fix: address code review feedback - lifecycle management and code cle…
Copilot Feb 8, 2026
bc984a3
docs: add comprehensive implementation summary
Copilot Feb 8, 2026
582d5d8
feat: add MCCoroutine exception event listeners for proper coroutine …
Copilot Feb 8, 2026
ee7d519
docs: remove IMPLEMENTATION_SUMMARY.md and SYSTEM_ERROR_LOGGING.md
Copilot Feb 8, 2026
a14d9bc
feat: implement error logging structure with improved server name han…
TheBjoRedCraft Feb 8, 2026
21b9eab
Merge remote-tracking branch 'origin/copilot/implement-error-logging-…
TheBjoRedCraft Feb 8, 2026
d22f196
feat: add launch command to trigger exceptions for testing error hand…
TheBjoRedCraft Feb 8, 2026
043b624
fix: correct /testerror coroutine to use suspend executor and add plu…
Copilot Feb 8, 2026
2fa1c5a
feat: log error ID to console when errors are captured
Copilot Feb 8, 2026
dfdf5a6
fix: use upsert for atomic deduplication across restarts
Copilot Feb 8, 2026
d8516b6
refactor: remove documentation comments from code
Copilot Feb 8, 2026
c8267fe
refactor: rename SystemError to SurfCoreSystemError throughout codebase
Copilot Feb 8, 2026
7f218cb
refactor: rename SystemError to SurfCoreSystemError in command and se…
TheBjoRedCraft Feb 8, 2026
3e817de
Merge pull request #10 from SLNE-Development/copilot/implement-error-…
TheBjoRedCraft Feb 8, 2026
cd3cdb7
refactor: remove unnecessary plugin check from MCCoroutineExceptionLi…
TheBjoRedCraft Feb 8, 2026
a1fa50b
feat: update SurfCoreSystemError to use UUID and enhance error handli…
TheBjoRedCraft Feb 8, 2026
e495314
feat: enhance error logging with UUID and improve exception handling
TheBjoRedCraft Feb 8, 2026
a89726e
feat: improve error logging in MCCoroutineExceptionListener with stru…
TheBjoRedCraft Feb 8, 2026
2c18455
feat: enhance error logging in MCCoroutineExceptionListener to includ…
TheBjoRedCraft Feb 8, 2026
3f44235
feat: enhance error logging in SurfCoreSystemErrorCommand with hover …
TheBjoRedCraft Feb 8, 2026
cf3a1a3
feat: refine getLocationClassName method to improve class name extrac…
TheBjoRedCraft Feb 8, 2026
bf86af2
feat: enhance core error command with player and system error handling
TheBjoRedCraft Feb 8, 2026
d6462c5
Merge branch 'version/1.21.11' into feat/error-logging-system
TheBjoRedCraft Feb 8, 2026
953e999
Initial plan
Copilot Feb 8, 2026
ad64038
Add 10-character error code system for system errors
Copilot Feb 8, 2026
4945931
Add unique constraint to error code and implement retry mechanism
Copilot Feb 8, 2026
fc258fb
Remove ineffective retry mechanism, rely on database uniqueness
Copilot Feb 8, 2026
102b407
Use error codes in command and logs for system errors
Copilot Feb 8, 2026
5afb70a
Merge pull request #12 from SLNE-Development/copilot/add-error-code-f…
TheBjoRedCraft Feb 8, 2026
a392fdd
feat: enhance global error handling with detailed logging of exceptions
TheBjoRedCraft Feb 8, 2026
4e58870
Initial plan
Copilot Feb 8, 2026
fdc7647
Add filtering support to error list commands
Copilot Feb 8, 2026
f693fd8
Fix query building to use proper where clause construction
Copilot Feb 8, 2026
40023b2
Improve code quality: Extract common functions and reduce duplication
Copilot Feb 8, 2026
69a2844
Extract andCondition helper to shared utility file
Copilot Feb 8, 2026
aec80dc
Merge pull request #13 from SLNE-Development/copilot/enhance-error-li…
TheBjoRedCraft Feb 8, 2026
d49107d
feat: rename QueryUtils.kt to query-util.kt and streamline error filt…
TheBjoRedCraft Feb 8, 2026
cfb02b5
feat: streamline error handling logic in CoreErrorCommand and SurfCor…
TheBjoRedCraft Feb 8, 2026
5607948
feat: add error retrieval functionality for player and system errors …
TheBjoRedCraft Feb 8, 2026
62169e0
feat: notify players of background system restart during proxy shutdown
TheBjoRedCraft Feb 22, 2026
d273a1f
Merge branch 'version/1.21.11' into feat/proxy-shutdown-broadcast-and…
TheBjoRedCraft Feb 22, 2026
cb78017
Merge remote-tracking branch 'origin/feat/proxy-shutdown-broadcast-an…
TheBjoRedCraft Feb 22, 2026
dccb0b9
feat: improve error logging and fix variable naming inconsistencies
TheBjoRedCraft Feb 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.slne.surf.core.api.common

import dev.slne.surf.core.api.common.error.SurfCoreError
import dev.slne.surf.core.api.common.event.SurfEvent
import dev.slne.surf.core.api.common.player.SurfPlayer
import dev.slne.surf.core.api.common.server.CommonSurfServer
Expand Down Expand Up @@ -39,6 +40,19 @@ interface SurfCoreApi {
fun fireEvent(event: SurfEvent)
fun subscribe(eventClass: KClass<out SurfEvent>, handler: (SurfEvent) -> Unit)

fun logError(playerUuid: UUID, code: String, message: String)
fun logError(playerUuid: UUID, message: String): String

suspend fun logErrorAwaiting(
playerUuid: UUID,
code: String,
message: String,
server: String
): SurfCoreError

suspend fun logErrorAwaiting(playerUuid: UUID, message: String, server: String): SurfCoreError
fun generateCode(): String

fun sendPlayer(player: SurfPlayer, server: CommonSurfServer)

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dev.slne.surf.core.api.common.error

import java.time.OffsetDateTime
import java.util.*

data class SurfCoreError(
val playerUuid: UUID,
val code: String,
val message: String,
val server: String,
val timestamp: OffsetDateTime
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package dev.slne.surf.core.api.common.error

import java.time.OffsetDateTime
import java.util.*

data class SurfCoreErrorFilter(
val playerUuid: UUID? = null,
val code: String? = null,
val messageLike: String? = null,
val server: String? = null,
val timestampAfter: OffsetDateTime? = null,
val timestampBefore: OffsetDateTime? = null,
val limit: Int = 50
) {
companion object {
fun empty() = SurfCoreErrorFilter()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package dev.slne.surf.core.api.common.error

import java.time.OffsetDateTime
import java.util.*

data class SurfCoreSystemError(
val uuid: UUID,
val errorCode: String,
val errorMessage: String,
val stacktrace: String,
val location: String,
val server: String,
val firstOccurred: OffsetDateTime,
val lastOccurred: OffsetDateTime,
val occurrenceCount: Int
) {
fun getLocationClassName(): String =
location
.substringBefore(':')
.substringBeforeLast('.')
.substringAfterLast('.')
.substringBefore('$')


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package dev.slne.surf.core.api.common.error

import java.time.OffsetDateTime
import java.util.*

data class SurfCoreSystemErrorFilter(
val uuid: UUID? = null,
val errorCode: String? = null,
val messageLike: String? = null,
val locationLike: String? = null,
val server: String? = null,
val firstOccurredAfter: OffsetDateTime? = null,
val firstOccurredBefore: OffsetDateTime? = null,
val lastOccurredAfter: OffsetDateTime? = null,
val lastOccurredBefore: OffsetDateTime? = null,
val minOccurrenceCount: Int? = null,
val limit: Int = 50
) {
companion object {
fun empty() = SurfCoreSystemErrorFilter()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ package dev.slne.surf.core.fallback

import com.google.auto.service.AutoService
import dev.slne.surf.core.core.common.database.DatabaseLoader
import dev.slne.surf.core.fallback.table.SurfPlayerIpAddressHistoryTable
import dev.slne.surf.core.fallback.table.SurfPlayerNameHistoryTable
import dev.slne.surf.core.fallback.table.SurfPlayerTable
import dev.slne.surf.core.fallback.table.SurfPlayerTexturesHistoryTable
import dev.slne.surf.core.fallback.table.*
import dev.slne.surf.database.DatabaseApi
import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.SchemaUtils
import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.transactions.suspendTransaction
Expand All @@ -23,7 +20,9 @@ class DatabaseLoaderImpl : DatabaseLoader, Services.Fallback {
SurfPlayerTable,
SurfPlayerNameHistoryTable,
SurfPlayerIpAddressHistoryTable,
SurfPlayerTexturesHistoryTable
SurfPlayerTexturesHistoryTable,
SurfCoreErrorLogsTable,
SurfCoreSystemErrorTable
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package dev.slne.surf.core.fallback.repository

import dev.slne.surf.core.api.common.error.SurfCoreError
import dev.slne.surf.core.api.common.error.SurfCoreErrorFilter
import dev.slne.surf.core.fallback.table.SurfCoreErrorLogsTable
import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.core.*
import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.insert
import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.selectAll
import dev.slne.surf.database.libs.org.jetbrains.exposed.v1.r2dbc.transactions.suspendTransaction
import dev.slne.surf.surfapi.core.api.util.toObjectList
import it.unimi.dsi.fastutil.objects.ObjectList
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList
import java.time.OffsetDateTime
import java.util.*

val surfCoreErrorLoggingRepository = SurfCoreErrorLoggingRepository()

class SurfCoreErrorLoggingRepository {
suspend fun logError(
playerUuid: UUID,
code: String,
message: String,
server: String,
): SurfCoreError = suspendTransaction {
val timestamp = OffsetDateTime.now()
SurfCoreErrorLogsTable.insert {
it[SurfCoreErrorLogsTable.playerUuid] = playerUuid
it[SurfCoreErrorLogsTable.errorCode] = code
it[SurfCoreErrorLogsTable.errorMessage] = message
it[SurfCoreErrorLogsTable.server] = server
it[SurfCoreErrorLogsTable.timestamp] = timestamp
}

SurfCoreError(playerUuid, code, message, server, timestamp)
}

suspend fun getErrors(playerUuid: UUID): ObjectList<SurfCoreError> = suspendTransaction {
SurfCoreErrorLogsTable.selectAll().where(SurfCoreErrorLogsTable.playerUuid eq playerUuid)
.map {
SurfCoreError(
playerUuid = it[SurfCoreErrorLogsTable.playerUuid],
code = it[SurfCoreErrorLogsTable.errorCode],
message = it[SurfCoreErrorLogsTable.errorMessage],
server = it[SurfCoreErrorLogsTable.server],
timestamp = it[SurfCoreErrorLogsTable.timestamp]
)
}.toList().toObjectList()
}

suspend fun getErrors(filter: SurfCoreErrorFilter): ObjectList<SurfCoreError> =
suspendTransaction {
var query = SurfCoreErrorLogsTable.selectAll()

var whereCondition: Op<Boolean>? =
filter.playerUuid?.let { SurfCoreErrorLogsTable.playerUuid eq it }

filter.code?.let {
whereCondition = whereCondition.andCondition(SurfCoreErrorLogsTable.errorCode eq it)
}

filter.messageLike?.let {
whereCondition =
whereCondition.andCondition(SurfCoreErrorLogsTable.errorMessage like "%$it%")
}

filter.server?.let {
whereCondition = whereCondition.andCondition(SurfCoreErrorLogsTable.server eq it)
}

filter.timestampAfter?.let {
whereCondition =
whereCondition.andCondition(SurfCoreErrorLogsTable.timestamp greaterEq it)
}

filter.timestampBefore?.let {
whereCondition =
whereCondition.andCondition(SurfCoreErrorLogsTable.timestamp lessEq it)
}

whereCondition?.let { query = query.where(it) }

query
.limit(filter.limit)
.map {
SurfCoreError(
playerUuid = it[SurfCoreErrorLogsTable.playerUuid],
code = it[SurfCoreErrorLogsTable.errorCode],
message = it[SurfCoreErrorLogsTable.errorMessage],
server = it[SurfCoreErrorLogsTable.server],
timestamp = it[SurfCoreErrorLogsTable.timestamp]
)
}.toList().toObjectList()
}

suspend fun getError(code: String): SurfCoreError? = suspendTransaction {
SurfCoreErrorLogsTable.selectAll().where(SurfCoreErrorLogsTable.errorCode eq code).map {
SurfCoreError(
playerUuid = it[SurfCoreErrorLogsTable.playerUuid],
code = it[SurfCoreErrorLogsTable.errorCode],
message = it[SurfCoreErrorLogsTable.errorMessage],
server = it[SurfCoreErrorLogsTable.server],
timestamp = it[SurfCoreErrorLogsTable.timestamp]
)
}.firstOrNull()
Comment on lines +97 to +106
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getError(code) assumes error_code uniquely identifies a single player error log, but the table schema does not enforce uniqueness on error_code. Even if collisions are rare, this can return the wrong player's error. Either enforce uniqueness at the DB level (and retry on conflict when generating codes) or change the lookup API to include a disambiguator (e.g., player UUID + code, or code + timestamp).

Suggested change
suspend fun getError(code: String): SurfCoreError? = suspendTransaction {
SurfCoreErrorLogsTable.selectAll().where(SurfCoreErrorLogsTable.errorCode eq code).map {
SurfCoreError(
playerUuid = it[SurfCoreErrorLogsTable.playerUuid],
code = it[SurfCoreErrorLogsTable.errorCode],
message = it[SurfCoreErrorLogsTable.errorMessage],
server = it[SurfCoreErrorLogsTable.server],
timestamp = it[SurfCoreErrorLogsTable.timestamp]
)
}.firstOrNull()
suspend fun getError(playerUuid: UUID, code: String): SurfCoreError? = suspendTransaction {
SurfCoreErrorLogsTable
.selectAll()
.where(
SqlExpressionBuilder.run {
(SurfCoreErrorLogsTable.playerUuid eq playerUuid) and
(SurfCoreErrorLogsTable.errorCode eq code)
}
)
.map {
SurfCoreError(
playerUuid = it[SurfCoreErrorLogsTable.playerUuid],
code = it[SurfCoreErrorLogsTable.errorCode],
message = it[SurfCoreErrorLogsTable.errorMessage],
server = it[SurfCoreErrorLogsTable.server],
timestamp = it[SurfCoreErrorLogsTable.timestamp]
)
}
.firstOrNull()

Copilot uses AI. Check for mistakes.
}
}
Loading