Console Output, Input, and Logging
When you run a game server, you’re running a headless copy of your game. There is no screen showing what’s happening inside the world. The only way for humans and automated systems to know the state of the server is through its output.
Your console output is not just debug information. It is an API that hosting platforms, monitoring tools, and server admins consume in real time.
STDOUT vs. STDERR
Section titled “STDOUT vs. STDERR”Your server should write to two standard output streams:
- STDOUT: Normal operational output. Server startup, player events, state changes, informational messages.
- STDERR: Errors and warnings. Crashes, failed operations, configuration problems.
Every hosting platform and log aggregation tool distinguishes between these two streams. If you write everything to STDOUT, errors get buried. If you write everything to STDERR, normal operation looks like a stream of problems.
Server Events
Section titled “Server Events”Hosting platforms parse your console output to detect important events. These events drive UI updates, notifications, and automation. At minimum, your server should produce clear, parseable output for:
| Event | Description | Required Data |
|---|---|---|
| Server Online | Server has started and is accepting connections | (none) |
| Server Stopping | Server is shutting down, no longer accepting connections | (none) |
| Player Join | A player connected to the server | Platform ID (e.g., Steam ID), Username |
| Player Leave | A player disconnected from the server | Platform ID, Username |
Player Tracking
Section titled “Player Tracking”Player events must include both the platform ID (such as a Steam ID) and the username. The platform ID is the stable identifier; the username can change at any time.
If your game allows players to create characters with different names, log the character name alongside the platform ID so players can be tracked across all events. If events log only the character name without a platform ID, it becomes impossible for hosting platforms to correlate “ShadowKnight” with the Steam user who is actually playing.
Example output format:
[2026-01-15T14:32:01Z] [INFO] Server online. Listening on 0.0.0.0:27015[2026-01-15T14:33:15Z] [INFO] Player joined: steam:76561198012345678 "PlayerName"[2026-01-15T14:45:02Z] [INFO] Player left: steam:76561198012345678 "PlayerName"[2026-01-15T14:50:00Z] [INFO] Server stopping.Structured Log Format
Section titled “Structured Log Format”Consistent formatting makes your logs parseable. Every log line should include:
- Timestamp in ISO 8601 format (e.g.,
2026-01-15T14:32:01Z) - Log level (INFO, WARN, ERROR, DEBUG)
- Message with relevant context
Avoid unstructured output like bare print() statements that produce lines like player connected with no timestamp, no level, and no identifying information.
If your engine has its own logging format, that’s fine as a starting point. Just make sure it includes timestamps and log levels.
Log Levels and Verbosity
Section titled “Log Levels and Verbosity”Support at minimum these levels:
| Level | When to Use |
|---|---|
| ERROR | Something failed. Data may be lost, a player may be affected. |
| WARN | Something unexpected happened but the server recovered. |
| INFO | Normal operations. Server start, player join/leave, map changes. |
| DEBUG | Detailed diagnostic information. Disabled by default in production. |
Make the log level configurable via startup argument or config file (e.g., -loglevel info).
Production servers should run at INFO level. DEBUG output should be available for troubleshooting but off by default. A server that floods STDOUT with frame-by-frame debug data at 60 ticks per second makes the console unusable for admins and generates enormous log files.
File Logging
Section titled “File Logging”In addition to writing to STDOUT, your server should write logs to a file. Console output is ephemeral and gets lost when the terminal session ends. File logs persist for post-mortem analysis.
- Write to a predictable location (e.g.,
./logs/server.log) - Rotate logs by date or size so they don’t fill the disk
- Make the log file path configurable
STDOUT is the primary interface for hosting platforms. File logs are the backup for admins and developers investigating issues after the fact.
Console Input (STDIN)
Section titled “Console Input (STDIN)”A good server console is interactive. Server admins should be able to type commands into the console to control the server without restarting it.
Accept STDIN commands using the same syntax as in-game admin commands. This keeps the command language consistent between the console and in-game usage.
Standard Commands
Section titled “Standard Commands”Every server should support at minimum:
| Command | Purpose |
|---|---|
stop or quit or exit | Gracefully shut down the server |
save | Force a world save |
status | Print current server status (player count, uptime, map) |
list or players | List connected players with their platform IDs |
kick <player> | Disconnect a player |
ban <player> | Ban a player from the server |
say <message> | Send a message to all connected players |
Command Design
Section titled “Command Design”- Use consistent syntax. Don’t mix
/kick,!kick, andkick. - Return feedback for every command. Silence means the admin doesn’t know if it worked.
- Handle bad input gracefully. An invalid command should not crash the server.
- Support a
helpcommand that lists available commands.
RCON (Remote Console)
Section titled “RCON (Remote Console)”RCON allows admins to send console commands to the server over the network, without direct access to the server’s terminal.
If you want to support remote administration, implementing a basic RCON protocol gives hosting platforms the ability to build admin UIs that issue commands on behalf of server owners. Valve’s Source RCON protocol is widely supported by existing tools, but any TCP-based command protocol works.
RCON should be:
- Disabled by default
- Password-protected
- On a configurable port
- Rate-limited to prevent abuse
Avoid TTYs or Custom Console Implementations
Section titled “Avoid TTYs or Custom Console Implementations”Some engines provide a built-in console window or TTY interface. This can be useful for local development, but it is not suitable for production servers running in headless environments. If your engine has a built-in console, make sure it also supports writing to STDOUT and reading from STDIN so that hosting platforms can interact with it. Do not rely solely on a custom console implementation that may not work in all environments.
If you must use a custom console for development, consider adding a “batch mode” that bypasses the custom console and uses standard input/output instead via a -batchmode option or similar. This gives you the best of both worlds: a rich console experience during development and a compatible interface for hosting platforms in production. STDOUT/STDERR streams should not be littered with ANSI escapes or cursor control codes that make them unreadable by log parsers.