What Logging Is Used for Non-TTY CloddsBot Runs: Pino-Based JSON Strategy
CloddsBot uses a structured Pino logger that automatically writes JSON-formatted logs to ~/.clodds/logs/clodds.log when running without a TTY, falling back from pretty-printed console output to ensure machine-readable records for daemons, CI pipelines, and Docker containers.
The alsk1992/CloddsBot repository implements a robust logging strategy specifically designed to handle non-interactive execution environments. When the process detects it is not attached to an interactive terminal, the system switches from human-readable formatting to structured JSON output, ensuring that log aggregation systems can parse entries without interference from ANSI color codes or multi-line formatting.
Core Logger Architecture in src/utils/logger.ts
The foundation of CloddsBot’s logging system resides in src/utils/logger.ts, where the root Pino logger is instantiated using pino({ level }, transport). The module exports a defaultLogger instance and a createLogger(name) factory function that returns child loggers inheriting the root configuration.
The transport configuration dynamically selects between pino-pretty for development environments and raw JSON streams for production or non-TTY scenarios. This automatic selection ensures that CI/CD pipelines and containerized deployments receive parseable structured data without manual configuration changes.
Environment-Driven Configuration
CloddsBot’s logger behavior is controlled through three primary environment variables:
-
LOG_LEVEL– Sets the minimum severity threshold (default:info). Accepts standard Pino levels:trace,debug,info,warn,error,fatal. -
LOG_FILE– Controls file output persistence. Set to"false"to disable file logging entirely; otherwise, logs write to the default path. -
LOG_JSON– Forces JSON output mode when set to"true", overriding the automatic TTY detection.
When LOG_FILE is not explicitly disabled, the logger maintains a persistent stream to ~/.clodds/logs/clodds.log, ensuring that log history survives container restarts and daemon detachments.
Non-TTY Detection and Output Modes
The system distinguishes between interactive and non-interactive execution through TTY detection logic embedded in the transport configuration. The behavior follows this decision tree:
- TTY attached +
LOG_JSON≠"true"– Usespino-prettytransport with colorized, indented formatting for human readability. - Non-TTY OR
LOG_JSON="true"– Falls back to raw JSON output with newline-delimited objects, suitable for log shippers like Fluentd, Logstash, or AWS CloudWatch.
This approach prevents pretty-printed logs from corrupting JSON-RPC communication streams and ensures that automated log parsers can extract fields like level, msg, time, and custom child properties without regex manipulation.
File Rotation and Retention Policies
For long-running non-TTY processes such as trading bots or persistent agents, src/utils/logger.ts implements automatic log rotation to prevent disk exhaustion:
maxFileSize– Rotates files when they exceed 10 MiB (default).maxFiles– Retains the 5 most recent rotated log files (default).
The rotation mechanism archives older logs with numeric suffixes (e.g., clodds.log.1, clodds.log.2) while maintaining the active write stream to the primary file. This configuration balances historical debugging capability against storage constraints in production environments.
Practical Implementation Examples
Import the default logger for standard operational logging:
import { defaultLogger as logger } from './utils/logger';
// Writes JSON to file and stdout when non-TTY
logger.info('Bot started', { pid: process.pid, version: '1.2.0' });
// Create component-specific child logger
const tradeLogger = logger.child({ name: 'trade-engine' });
tradeLogger.debug('Order execution initiated', {
symbol: 'BTC/USD',
size: 0.5,
orderId: 'abc123'
});
When executed without a TTY (e.g., docker run alsk1992/cloddsbot or systemd service), the output in ~/.clodds/logs/clodds.log appears as:
{"level":"info","msg":"Bot started","time":"2024-09-13T12:34:56.789Z","pid":12345,"version":"1.2.0"}
{"level":"debug","msg":"Order execution initiated","time":"2024-09-13T12:35:00.123Z","name":"trade-engine","symbol":"BTC/USD","size":0.5,"orderId":"abc123"}
For handler-specific logging, src/agents/handlers/predictfun.ts demonstrates the createLogger() pattern, while src/utils/config.ts shows configuration-time logger initialization.
Summary
- Non-TTY logging in CloddsBot relies on a Pino-based implementation in
src/utils/logger.tsthat automatically switches from pretty-printed to JSON output when stdout is not a terminal. - Structured JSON logs write to
~/.clodds/logs/clodds.logby default, with automatic rotation at 10 MiB and retention of 5 historical files. - Environment variables (
LOG_LEVEL,LOG_JSON,LOG_FILE) control severity filtering, output format coercion, and file persistence without code changes. - Child loggers created via
createLogger(name)inherit root configuration while adding component-specific context for traceability in distributed systems.
Frequently Asked Questions
How do I change the default log file location?
Set the LOG_FILE environment variable to your desired absolute path before starting the bot. If the directory does not exist, the logger will attempt to create it on initialization. Setting LOG_FILE="false" disables file output entirely, sending logs only to stdout.
Why does my Docker container output JSON instead of formatted text?
When Docker runs containers without a pseudo-TTY (docker run without -t flag), process.stdout.isTTY evaluates to undefined, triggering the non-TTY code path. To force pretty printing in Docker for debugging, either attach a TTY with docker run -t or set LOG_JSON="false" explicitly.
Can I disable log rotation or adjust the retention count?
The rotation settings (maxFileSize and maxFiles) are currently hardcoded to 10 MiB and 5 files respectively in src/utils/logger.ts. To customize these values, you must modify the Pino transport configuration in the source file and rebuild the application.
What is the difference between defaultLogger and createLogger()?
defaultLogger is the singleton root instance used for general application logging, while createLogger(name) is a factory function that returns a child logger with a bound name property. Child loggers automatically include their component name in every log entry, making them ideal for correlating logs across different subsystems like trade engines or prediction handlers.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →