Improved logging, especially for proxy servers.

This commit is contained in:
Daan Vanden Bosch 2021-08-05 15:54:05 +02:00
parent bc3c3c19b2
commit 21e9ebaaee
6 changed files with 67 additions and 34 deletions

View File

@ -22,7 +22,8 @@ proxy: {
# One server configuration per address/port pair that needs to be proxied. # One server configuration per address/port pair that needs to be proxied.
servers: [ servers: [
{ {
# Name used for e.g. the logs. # Name used for e.g. the logs. Should contain only alpha-numeric characters, minus (-) or
# underscore (_).
name: patch_proxy name: patch_proxy
# PC or BB, determines the message format encryption cipher used. # PC or BB, determines the message format encryption cipher used.
version: PC version: PC

View File

@ -114,7 +114,7 @@ private fun initialize(config: Config): PhantasmalServer {
config.blocks.filter { it.run }.associate { blockCfg -> config.blocks.filter { it.run }.associate { blockCfg ->
val block = BlockInfo( val block = BlockInfo(
name = blockCfg.name ?: "block_$blockI", name = validateName("Block", blockCfg.name) ?: "block_$blockI",
uiName = blockCfg.uiName ?: "BLOCK${blockI.toString(2).padStart(2, '0')}", uiName = blockCfg.uiName ?: "BLOCK${blockI.toString(2).padStart(2, '0')}",
bindPair = Inet4Pair( bindPair = Inet4Pair(
blockCfg.address?.let(::inet4Address) ?: defaultAddress, blockCfg.address?.let(::inet4Address) ?: defaultAddress,
@ -132,7 +132,7 @@ private fun initialize(config: Config): PhantasmalServer {
shipsToRun.map { shipCfg -> shipsToRun.map { shipCfg ->
val ship = ShipInfo( val ship = ShipInfo(
name = shipCfg.name ?: "ship_$shipI", name = validateName("Ship", shipCfg.name) ?: "ship_$shipI",
uiName = shipCfg.uiName ?: "Ship $shipI", uiName = shipCfg.uiName ?: "Ship $shipI",
bindPair = Inet4Pair( bindPair = Inet4Pair(
shipCfg.address?.let(::inet4Address) ?: defaultAddress, shipCfg.address?.let(::inet4Address) ?: defaultAddress,
@ -256,7 +256,7 @@ private fun initializeProxy(config: ProxyConfig): List<ProxyServer> {
continue continue
} }
val name = psc.name ?: "proxy_${nameI++}" val name = validateName("Proxy server", psc.name) ?: "proxy_${nameI++}"
val bindPair = Inet4Pair( val bindPair = Inet4Pair(
psc.bindAddress?.let(::inet4Address) ?: defaultBindAddress, psc.bindAddress?.let(::inet4Address) ?: defaultBindAddress,
psc.bindPort, psc.bindPort,
@ -299,3 +299,13 @@ private fun initializeProxy(config: ProxyConfig): List<ProxyServer> {
return proxyServers return proxyServers
} }
private fun validateName(whichName: String, name: String?): String? =
if (name == null) {
null
} else {
check(Regex("[a-zA-Z0-9_-]+").matches(name)) {
"""$whichName name "$name" should contain only alpha-numeric characters, minus (-) or underscore (_)."""
}
name
}

View File

@ -24,8 +24,9 @@ abstract class GameServer<MessageType : Message>(
override fun clientConnected(clientSocket: Socket) { override fun clientConnected(clientSocket: Socket) {
// Handle each client connection in its own thread. // Handle each client connection in its own thread.
val thread = Thread { GameClientHandler(clientSocket).listen() } val client = "${name}_client_${connectionCounter++}"
thread.name = "${name}_client_${connectionCounter++}" val thread = Thread { GameClientHandler(client, clientSocket).listen() }
thread.name = client
thread.start() thread.start()
} }
@ -42,8 +43,8 @@ abstract class GameServer<MessageType : Message>(
return true return true
} }
private inner class GameClientHandler(socket: Socket) : private inner class GameClientHandler(client: String, socket: Socket) :
SocketHandler<MessageType>(logger, socket) { SocketHandler<MessageType>(client, socket) {
private val serverCipher = createCipher() private val serverCipher = createCipher()
private val clientCipher = createCipher() private val clientCipher = createCipher()

View File

@ -2,10 +2,7 @@ package world.phantasmal.psoserv.servers
import world.phantasmal.psolib.buffer.Buffer import world.phantasmal.psolib.buffer.Buffer
import world.phantasmal.psoserv.encryption.Cipher import world.phantasmal.psoserv.encryption.Cipher
import world.phantasmal.psoserv.messages.InitEncryptionMessage import world.phantasmal.psoserv.messages.*
import world.phantasmal.psoserv.messages.Message
import world.phantasmal.psoserv.messages.MessageDescriptor
import world.phantasmal.psoserv.messages.RedirectMessage
import java.net.Socket import java.net.Socket
class ProxyServer( class ProxyServer(
@ -31,12 +28,10 @@ class ProxyServer(
private inner class ServerHandler( private inner class ServerHandler(
serverSocket: Socket, serverSocket: Socket,
private val clientSocket: Socket, private val clientSocket: Socket,
) : SocketHandler<Message>(logger, serverSocket) { ) : ProxySocketHandler("${name}_server", serverSocket) {
private var clientHandler: ClientHandler? = null private var clientHandler: ClientHandler? = null
override val messageDescriptor = this@ProxyServer.messageDescriptor
// The first message sent by the server is always unencrypted and initializes the // The first message sent by the server is always unencrypted and initializes the
// encryption. We don't start listening to the client until the encryption is // encryption. We don't start listening to the client until the encryption is
// initialized. // initialized.
@ -66,7 +61,7 @@ class ProxyServer(
) )
this.clientHandler = clientListener this.clientHandler = clientListener
val thread = Thread(clientListener::listen) val thread = Thread(clientListener::listen)
thread.name = "$name client" thread.name = "${name}_client"
thread.start() thread.start()
} }
@ -104,9 +99,8 @@ class ProxyServer(
private val serverHandler: ServerHandler, private val serverHandler: ServerHandler,
override val readDecryptCipher: Cipher, override val readDecryptCipher: Cipher,
override val readEncryptCipher: Cipher, override val readEncryptCipher: Cipher,
) : SocketHandler<Message>(logger, clientSocket) { ) : ProxySocketHandler("${name}_client", clientSocket) {
override val messageDescriptor = this@ProxyServer.messageDescriptor
override val writeEncryptCipher: Cipher? = null override val writeEncryptCipher: Cipher? = null
override fun processMessage(message: Message): ProcessResult = ProcessResult.Ok override fun processMessage(message: Message): ProcessResult = ProcessResult.Ok
@ -119,4 +113,21 @@ class ProxyServer(
serverHandler.stop() serverHandler.stop()
} }
} }
private abstract inner class ProxySocketHandler(name: String, socket: Socket) :
SocketHandler<Message>(name, socket) {
override val messageDescriptor = this@ProxyServer.messageDescriptor
override fun logMessageTooLarge(code: Int, size: Int) {
logger.warn {
val message = messageString(code, size)
"Sending $message with size ${size}B. Skipping because it's too large."
}
}
override fun logMessageReceived(message: Message) {
logger.trace { "Sent $message." }
}
}
} }

View File

@ -1,19 +1,20 @@
package world.phantasmal.psoserv.servers package world.phantasmal.psoserv.servers
import mu.KLogger import mu.KLogger
import mu.KotlinLogging
import world.phantasmal.psolib.Endianness import world.phantasmal.psolib.Endianness
import world.phantasmal.psolib.buffer.Buffer import world.phantasmal.psolib.buffer.Buffer
import world.phantasmal.psoserv.alignToWidth
import world.phantasmal.psoserv.encryption.Cipher import world.phantasmal.psoserv.encryption.Cipher
import world.phantasmal.psoserv.messages.Message import world.phantasmal.psoserv.messages.Message
import world.phantasmal.psoserv.messages.MessageDescriptor import world.phantasmal.psoserv.messages.MessageDescriptor
import world.phantasmal.psoserv.messages.messageString import world.phantasmal.psoserv.messages.messageString
import world.phantasmal.psoserv.alignToWidth
import java.net.Socket import java.net.Socket
import java.net.SocketException import java.net.SocketException
import kotlin.math.min import kotlin.math.min
abstract class SocketHandler<MessageType : Message>( abstract class SocketHandler<MessageType : Message>(
protected val logger: KLogger, protected val name: String,
private val socket: Socket, private val socket: Socket,
) { ) {
private val sockName: String = "${socket.remoteSocketAddress}" private val sockName: String = "${socket.remoteSocketAddress}"
@ -22,6 +23,7 @@ abstract class SocketHandler<MessageType : Message>(
@Volatile @Volatile
private var running = false private var running = false
protected val logger: KLogger = KotlinLogging.logger(name)
protected abstract val messageDescriptor: MessageDescriptor<MessageType> protected abstract val messageDescriptor: MessageDescriptor<MessageType>
protected abstract val readDecryptCipher: Cipher? protected abstract val readDecryptCipher: Cipher?
@ -33,7 +35,7 @@ abstract class SocketHandler<MessageType : Message>(
protected abstract val writeEncryptCipher: Cipher? protected abstract val writeEncryptCipher: Cipher?
fun listen() { fun listen() {
logger.info { "Listening to $sockName." } logger.info { "Listening to $name ($sockName)." }
running = true running = true
try { try {
@ -76,10 +78,7 @@ abstract class SocketHandler<MessageType : Message>(
when { when {
// Don't parse the message when it's too large. // Don't parse the message when it's too large.
encryptedSize > BUFFER_CAPACITY -> { encryptedSize > BUFFER_CAPACITY -> {
logger.warn { logMessageTooLarge(code, size)
val message = messageString(code, size)
"Receiving $message, too large: ${size}B. Skipping."
}
bytesToSkip = encryptedSize - available bytesToSkip = encryptedSize - available
@ -111,7 +110,7 @@ abstract class SocketHandler<MessageType : Message>(
try { try {
val message = messageDescriptor.readMessage(messageBuffer) val message = messageDescriptor.readMessage(messageBuffer)
logger.trace { "Received $message." } logMessageReceived(message)
when (processMessage(message)) { when (processMessage(message)) {
ProcessResult.Ok -> { ProcessResult.Ok -> {
@ -139,7 +138,7 @@ abstract class SocketHandler<MessageType : Message>(
} }
} }
} catch (e: Throwable) { } catch (e: Throwable) {
logger.error(e) { "Error while processing message from $sockName." } logger.error(e) { "Error while processing message." }
} }
offset += encryptedSize offset += encryptedSize
@ -161,7 +160,7 @@ abstract class SocketHandler<MessageType : Message>(
if (read == -1) { if (read == -1) {
logger.warn { logger.warn {
"Expected to skip $bytesToSkip more bytes, but $sockName stopped sending." "Expected to skip $bytesToSkip more bytes, but $name stopped sending."
} }
// Close the connection. // Close the connection.
@ -189,23 +188,23 @@ abstract class SocketHandler<MessageType : Message>(
} }
} }
} catch (e: InterruptedException) { } catch (e: InterruptedException) {
logger.error(e) { "Interrupted while listening to $sockName, closing connection." } logger.error(e) { "Interrupted while listening to $name ($sockName), closing connection." }
} catch (e: SocketException) { } catch (e: SocketException) {
// Don't log if we're not running anymore because that means this exception was probably // Don't log if we're not running anymore because that means this exception was probably
// generated by a socket.close() call. // generated by a socket.close() call.
if (running) { if (running) {
logger.error(e) { "Error while listening to $sockName, closing connection." } logger.error(e) { "Error while listening to $name ($sockName), closing connection." }
} }
} catch (e: Throwable) { } catch (e: Throwable) {
logger.error(e) { "Error while listening to $sockName, closing connection." } logger.error(e) { "Error while listening to $name ($sockName), closing connection." }
} finally { } finally {
running = false running = false
try { try {
if (socket.isClosed) { if (socket.isClosed) {
logger.info { "Connection to $sockName was closed." } logger.info { "Connection to $name ($sockName) was closed." }
} else { } else {
logger.info { "Closing connection to $sockName." } logger.info { "Closing connection to $name ($sockName)." }
socket.close() socket.close()
} }
} finally { } finally {
@ -262,6 +261,17 @@ abstract class SocketHandler<MessageType : Message>(
// Do nothing. // Do nothing.
} }
protected open fun logMessageTooLarge(code: Int, size: Int) {
logger.warn {
val message = messageString(code, size)
"Receiving $message with size ${size}B. Skipping because it's too large."
}
}
protected open fun logMessageReceived(message: Message) {
logger.trace { "Received $message." }
}
protected enum class ProcessResult { protected enum class ProcessResult {
Ok, Changed, Done Ok, Changed, Done
} }

View File

@ -2,7 +2,7 @@
<Configuration status="WARN"> <Configuration status="WARN">
<Appenders> <Appenders>
<Console name="Console" target="SYSTEM_OUT"> <Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger{36} [%t] - %msg%n"/> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%-5level] %logger{36} - %msg%n"/>
</Console> </Console>
</Appenders> </Appenders>
<Loggers> <Loggers>