Compare commits

...

1 Commits

Author SHA1 Message Date
ec41111054 Start Logging
Some checks failed
SteamWarCI Build failed
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-01-28 20:44:05 +01:00
9 changed files with 159 additions and 65 deletions

View File

@@ -24,4 +24,5 @@ plugins {
dependencies { dependencies {
testImplementation(libs.junit) testImplementation(libs.junit)
testImplementation(libs.hamcrest) testImplementation(libs.hamcrest)
compileOnly(project(":CommonCore:SQL"))
} }

View File

@@ -0,0 +1,22 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar
data class ServerInfo(val name: String, val version: Int, val checkpointed: Boolean)

View File

@@ -0,0 +1,71 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.logger
import de.steamwar.sql.AuditLog
import de.steamwar.sql.AuditLogTable
import de.steamwar.sql.SQLWrapper
import de.steamwar.sql.SteamwarUser
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.jdbc.insertIgnore
class LogEntry(val type: AuditLog.Type, val user: SteamwarUser) {
private val start = System.currentTimeMillis()
var text: String = ""
var arguments: String = ""
private var exceptionType: String? = null
private var exceptionText: String? = null
private var exceptionStacktrace: String? = null
private var owner: SteamwarUser? = null
fun addException(e: Throwable) {
exceptionType = e.javaClass.name
exceptionText = e.message
exceptionStacktrace = e.stackTraceToString()
}
fun addServerOwner(owner: SteamwarUser) {
this.owner = owner
}
fun finish() {
val end = System.currentTimeMillis()
val info = SQLWrapper.impl.serverInfo
useDb {
AuditLogTable.insertIgnore {
it[this.action] = type
it[this.actor] = user.getId()
it[this.actionText] = text
it[this.actionArguments] = arguments
it[this.duration] = end - start
it[this.errorType] = exceptionType
it[this.errorText] = exceptionText
it[this.stackTrace] = exceptionStacktrace
it[this.server] = info.name
it[this.checkpointed] = info.checkpointed
it[this.duration] = end - start
it[this.serverOwner] = owner?.getId()
}
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.logger
import de.steamwar.sql.AuditLog
import de.steamwar.sql.SteamwarUser
object SWLogger {
fun startCommand(user: SteamwarUser) = LogEntry(AuditLog.Type.COMMAND, user)
}

View File

@@ -19,94 +19,47 @@
package de.steamwar.sql package de.steamwar.sql
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.dao.id.EntityID import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
import org.jetbrains.exposed.v1.dao.IntEntity import org.jetbrains.exposed.v1.dao.IntEntity
import org.jetbrains.exposed.v1.dao.IntEntityClass import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.javatime.timestamp import org.jetbrains.exposed.v1.javatime.timestamp
import java.time.Instant
object AuditLogTable: IntIdTable("AuditLog", "AuditLogId") { object AuditLogTable: IntIdTable("AuditLog", "AuditLogId") {
val time = timestamp("Time") val time = timestamp("Time")
val server = varchar("ServerName", 255) val server = varchar("ServerName", 255)
val serverOwner = reference("ServerOwner", SteamwarUserTable).nullable() val serverType = varchar("ServerType", 255)
val serverOwner = optReference("ServerOwner", SteamwarUserTable)
val checkpointed = bool("Checkpointed")
val actor = reference("Actor", SteamwarUserTable) val actor = reference("Actor", SteamwarUserTable)
val action = enumerationByName("ActionType", 255, AuditLog.Type::class) val action = enumerationByName("ActionType", 255, AuditLog.Type::class)
val actionText = text("ActionText") val actionText = text("ActionText")
val actionArguments = text("ActionArguments")
val duration = long("Duration")
val errorType = text("ErrorType").nullable()
val errorText = text("ErrorText").nullable()
val stackTrace = text("StackTrace").nullable()
} }
class AuditLog(id: EntityID<Int>): IntEntity(id) { class AuditLog(id: EntityID<Int>): IntEntity(id) {
companion object: IntEntityClass<AuditLog>(AuditLogTable) { companion object: IntEntityClass<AuditLog>(AuditLogTable) {
const val SERVER_NAME_VELOCITY: String = "Velocity" @JvmField
val SERVER_NAME_VELOCITY: String = "Velocity"
private fun create(
serverName: String,
serverOwner: SteamwarUser?,
actor: SteamwarUser,
actionType: Type,
text: String = ""
) = useDb {
new {
this.time = Instant.now()
this.server = serverName
this.serverOwner = serverOwner?.id
this.actor = actor.id
this.action = actionType
this.actionText = text
}
}
@JvmStatic
fun createJoin(jointServerName: String, serverOwner: SteamwarUser?, joinedPlayer: SteamwarUser) = create(jointServerName, serverOwner, joinedPlayer, Type.JOIN)
@JvmStatic
fun createLeave(leftServerName: String, serverOwner: SteamwarUser?, joinedPlayer: SteamwarUser) = create(leftServerName, serverOwner, joinedPlayer, Type.LEAVE)
@JvmStatic
fun createCommand(serverName: String, serverOwner: SteamwarUser?, player: SteamwarUser?, command: String) = player?.let { create(serverName, serverOwner, it, Type.COMMAND, command) }
@JvmStatic
fun createSensitiveCommand(
serverName: String,
serverOwner: SteamwarUser?,
player: SteamwarUser?,
command: String
) = player?.let { create(serverName, serverOwner, it, Type.SENSITIVE_COMMAND, command) }
@JvmStatic
fun createChat(serverName: String, serverOwner: SteamwarUser?, chatter: SteamwarUser, chat: String) = create(serverName, serverOwner, chatter, Type.CHAT, chat)
@JvmStatic
fun createGuiOpen(serverName: String, serverOwner: SteamwarUser?, player: SteamwarUser, guiName: String) = create(serverName, serverOwner, player, Type.GUI_OPEN, guiName)
@JvmStatic
fun createGuiClick(
serverName: String,
serverOwner: SteamwarUser?,
player: SteamwarUser,
guiName: String,
clickType: String,
slot: Int,
itemName: String
) = create(
serverName,
serverOwner,
player,
Type.GUI_CLICK,
"Gui: $guiName\nSlot: $slot\nClickType: $clickType\nItemName: $itemName"
)
@JvmStatic
fun createGuiClose(serverName: String, serverOwner: SteamwarUser?, player: SteamwarUser, guiName: String) = create(serverName, serverOwner, player, Type.GUI_CLOSE, guiName)
} }
var time by AuditLogTable.time var time by AuditLogTable.time
var server by AuditLogTable.server var server by AuditLogTable.server
val serverType by AuditLogTable.serverType
var serverOwner by AuditLogTable.serverOwner var serverOwner by AuditLogTable.serverOwner
var actor by AuditLogTable.actor var actor by AuditLogTable.actor
var checkpointed by AuditLogTable.checkpointed
var action by AuditLogTable.action var action by AuditLogTable.action
var actionText by AuditLogTable.actionText var actionText by AuditLogTable.actionText
var actionArguments by AuditLogTable.actionArguments
var duration by AuditLogTable.duration
var errorType by AuditLogTable.errorType
var errorText by AuditLogTable.errorText
var stackTrace by AuditLogTable.stackTrace
enum class Type { enum class Type {
JOIN, JOIN,

View File

@@ -20,6 +20,7 @@
package de.steamwar.sql; package de.steamwar.sql;
import de.steamwar.ImplementationProvider; import de.steamwar.ImplementationProvider;
import de.steamwar.ServerInfo;
import java.io.File; import java.io.File;
import java.util.Collections; import java.util.Collections;
@@ -40,4 +41,6 @@ public interface SQLWrapper<M> {
} }
void additionalExceptionMetadata(StringBuilder builder); void additionalExceptionMetadata(StringBuilder builder);
ServerInfo getServerInfo();
} }

View File

@@ -36,6 +36,8 @@ object ExceptionTable: IntIdTable("Exception") {
class SWException { class SWException {
companion object { companion object {
private val exceptionCache = HashSet<String>()
val cwd = System.getProperty("user.dir") val cwd = System.getProperty("user.dir")
val serverName = File(cwd).name val serverName = File(cwd).name
@@ -43,7 +45,9 @@ class SWException {
fun init() = Unit fun init() = Unit
@JvmStatic @JvmStatic
fun log(message: String, stacktrace: String) = useDb { fun log(message: String, stacktrace: String) = if (exceptionCache.contains(stacktrace)) Unit else useDb {
exceptionCache.add(stacktrace)
ExceptionTable.insert { ExceptionTable.insert {
it[ExceptionTable.server] = serverName it[ExceptionTable.server] = serverName
it[ExceptionTable.message] = generateMessage(message) it[ExceptionTable.message] = generateMessage(message)

View File

@@ -19,9 +19,14 @@
package de.steamwar.core; package de.steamwar.core;
import lombok.Getter;
public class CheckpointUtils { public class CheckpointUtils {
private CheckpointUtils() {} private CheckpointUtils() {}
@Getter
private static boolean restored = false;
public static void signalHandler() { public static void signalHandler() {
try { try {
CheckpointUtilsJ9.signalHandler(); CheckpointUtilsJ9.signalHandler();
@@ -33,6 +38,7 @@ public class CheckpointUtils {
public static void freeze() { public static void freeze() {
try { try {
CheckpointUtilsJ9.freeze(); CheckpointUtilsJ9.freeze();
restored = true;
} catch (NoClassDefFoundError e) { } catch (NoClassDefFoundError e) {
//ignore //ignore
} }

View File

@@ -19,6 +19,8 @@
package de.steamwar.sql; package de.steamwar.sql;
import de.steamwar.ServerInfo;
import de.steamwar.core.CheckpointUtils;
import de.steamwar.core.Core; import de.steamwar.core.Core;
import de.steamwar.data.GameModeConfigUtils; import de.steamwar.data.GameModeConfigUtils;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@@ -68,4 +70,9 @@ public class SQLWrapperImpl implements SQLWrapper<Material> {
builder.append(world.getName()).append(" "); builder.append(world.getName()).append(" ");
builder.append("\nServer: ").append(SERVER_VERSION); builder.append("\nServer: ").append(SERVER_VERSION);
} }
@Override
public ServerInfo getServerInfo() {
return new ServerInfo(Core.getServerName(), Core.getVersion(), CheckpointUtils.isRestored());
}
} }