When something breaks while you're asleep, your logs are the only witness. Good logging turns "the server crashed, I have no idea why" into "the database timed out at 03:12, here's the line". This guide covers the discipline that makes logs useful, structured logging in Node and Python, and where every line ends up in the panel.
| At a glance | |
|---|---|
| You need | A Falix application server (Node.js or Python) |
| Plan | Any |
| Time | Twenty minutes |
Where your logs go on Falix
Everything your app prints to standard output shows up live on the Console page — that's your main window while a server runs. Two Falix facts shape how you print:
- Node flips your server's status to online when it sees a line containing
Listening, so a startup log likeListening on port 25565is both good practice and a status signal. - Python buffers output; use
print("...", flush=True), or theloggingmodule below, so your lines appear immediately instead of getting stuck.
The Logs page is a separate log-file browser with refresh/auto-refresh, download, share, and search (with optional regex). It shines for games that write log files; for application servers the Console is where the action is. See The Console.
console.log discipline (the free baseline)
console.log is fine — most bots and apps never need more. What matters is how you use it:
- Log events, not everything. Startup, key actions, and every error — not a line per loop iteration. Noise buries the one line you need.
- Include context.
console.log('user banned', userId, guildId)beatsconsole.log('done'). Log the values that let you reconstruct what happened. - Send errors to
console.error. It writes to stderr, which keeps errors distinguishable from normal output. - Never log secrets. Tokens, passwords and API keys don't belong in logs — logs get shared when you ask for help.
- Log the error object, not just a message.
console.error('login failed', err)prints the stack trace;console.error('login failed')throws away the one clue that matters.
Level up: structured logging in Node with pino
Once an app grows, you'll want to dial verbosity up and down without editing code everywhere. A logger with levels does that. pino is small, fast, and outputs one JSON object per line:
const pino = require('pino');
const log = pino({ level: process.env.LOG_LEVEL || 'info' });
log.info({ port: 25565 }, 'service starting');
log.warn('low on memory');
log.error(new Error('database connection failed'), 'request failed');
log.debug('you only see this when LOG_LEVEL=debug');
Add pino to your package.json and it installs on start. Each call prints a JSON line with a numeric level — info is 30, warn 40, error 50, debug 20 — so {"level":50,...,"msg":"request failed"} is unmistakably an error, and the error's stack is captured as a field. Because the level comes from LOG_LEVEL, you can set that variable to debug when hunting a bug and back to info after, no code change. The Console shows the JSON as-is; for a prettier local view, the pino-pretty formatter exists, but raw JSON is perfectly readable and ideal for searching.
🎯 Good to know:
winstonis the other popular Node logger and works the same way (levels, structured output) — pick either. The habit (levels + context + errors as objects) matters more than the library.
Structured logging in Python (no dependency)
Python's standard library logging module needs nothing installed. Configure it once at startup:
import logging, sys
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(name)s %(message)s",
stream=sys.stdout,
)
log = logging.getLogger("myapp")
log.info("service starting on port %s", 25565)
log.warning("low on memory")
try:
risky()
except Exception:
log.exception("unhandled error while handling request")
That prints timestamped, levelled lines like 2026-07-18 04:03:39 INFO myapp service starting on port 25565, and log.exception(...) inside an except block logs your message plus the full traceback automatically — exactly what you want at 3am. Streaming to sys.stdout means the lines land on the Console immediately, sidestepping the buffering that plagues bare print.
Log levels: which to use when
| Level | Use it for |
|---|---|
| error | Something failed and needs attention — a crash, a failed request, a rejected login |
| warn | Something's off but survivable — a retry, a missing optional config, high memory |
| info | Normal milestones — startup, a command run, a scheduled job fired |
| debug | Detailed tracing you only want while investigating — off in normal running |
The 3am checklist
Whatever tool you use, always log these:
- A startup line with the port (for Node, it also flips your status online).
- Every unhandled error, with its stack trace.
- External calls that can fail — database, another API, a webhook — on both success and failure.
- Never tokens, passwords, or personal data.
Reading and sharing logs
When you need help, don't retype errors — the panel shares them for you. Use the Share button on the Console (or the Logs page) to hand someone a link to the exact output, and the Logs page's regex search to find one error string in a wall of lines. See Getting help.
Troubleshooting
- My logs don't appear — Python buffering; use
logging(streamed to stdout) orprint(..., flush=True). - The Console is too noisy to read — raise your log level (set
LOG_LEVEL=infoorwarn) sodebuglines drop out. - An error shows no cause — you logged a message but not the error object. Log the exception itself (
console.error(msg, err)/log.exception(msg)) to capture the stack. - A secret leaked into a shared log — rotate it immediately, then stop logging it. See Keep secrets out of Git.
Next steps
- The Console
- Health checks that actually tell you something
- Logger docs: pino at getpino.io and Python's logging HOWTO at docs.python.org