How Debug Mode and Logging Work in Superfile: A Complete Technical Guide
Debug mode and logging in superfile are controlled by a boolean debug configuration flag that switches the global slog logger between Info and Debug levels, exposing detailed diagnostic output while maintaining clean normal operation.
Superfile, the modern terminal file manager written in Go, implements a unified debugging and logging system built on the standard library's slog package. Understanding how debug mode and logging in superfile work is essential for troubleshooting file operations and understanding internal state transitions. The system propagates a single configuration value from user settings through CLI arguments to the root logger initializer, ensuring consistent log levels across the entire application lifecycle.
Configuring Debug Mode in Superfile
Superfile offers two mechanisms for enabling debug output: persistent configuration via the config file, or temporary activation through command-line flags.
Persistent Configuration via config.toml
The debug setting lives in the ConfigType struct defined in src/internal/common/config_type.go. This struct includes a debug boolean field marked with the comment "Whether to enable debug mode." Users can persistently enable debugging by setting debug = true in their ~/.config/superfile/config.toml file:
# ~/.config/superfile/config.toml
debug = true
When the application loads its configuration, this value determines the initial logging behavior before any CLI flags are processed.
Temporary Override with CLI Flags
For one-off debugging sessions, Superfile exposes a --debug (or -d) flag in src/cmd/main.go. This flag overrides the configuration file setting for the current execution:
# Enable debug mode for a single session
superfile --debug
# Or use the short form
superfile -d
The CLI parser stores this value and passes it to configure_logging(), which forwards the boolean to the logger initialization logic.
How the Logging System Initializes
The core logging infrastructure resides in src/pkg/utils/log_utils.go, where the function SetRootLoggerToStdout(debug bool) creates and configures the global logger instance.
Root Logger Setup in log_utils.go
When Superfile starts, it calls utils.SetRootLoggerToStdout(debug) to establish the logging infrastructure. This function creates a new slog logger using a text handler attached to os.Stdout:
// src/pkg/utils/log_utils.go
func SetRootLoggerToStdout(debug bool) {
level := slog.LevelInfo
if debug {
level = slog.LevelDebug // ← Debug mode boosts log level
}
slog.SetDefault(slog.New(
slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: level}),
))
}
The function immediately sets this logger as the default via slog.SetDefault(), ensuring all subsequent logging calls throughout the codebase use this configured handler.
Log Level Switching (Info vs Debug)
The critical mechanism lies in the HandlerOptions.Level assignment. When the debug parameter is false, the logger uses slog.LevelInfo, filtering out Debug-level messages. When debug is true, the level shifts to slog.LevelDebug, causing all logger.Debug() calls to become visible.
This design allows developers to sprinkle detailed diagnostic calls throughout the codebase without impacting performance or cluttering output during normal usage:
// Example usage inside Superfile code
logger := slog.Default()
logger.Debug("Opening file panel", "panel", panelID) // Visible only with debug mode
logger.Info("File saved", "path", filePath) // Always visible
Where Log Output Goes
Superfile implements a dual-phase logging strategy that captures bootstrap information differently from runtime logs.
Startup Logging to Stdout
During application initialization—before the configuration file is fully parsed and the log file is opened—the logger writes to os.Stdout as configured in SetRootLoggerToStdout. This ensures that early-stage errors and configuration parsing issues are visible immediately, even if the eventual log file destination is unavailable or misconfigured.
File Destination Configuration
After the initial bootstrap, the logging destination transitions based on the log_file entry in config.toml. The application updates slog.SetDefault() to redirect output to the specified file path, ensuring that detailed logs persist across sessions for post-mortem analysis. This architecture guarantees that both interactive debugging (via stdout) and background logging (via file) operate through the same unified pipeline.
Writing Debug Messages in the Codebase
Throughout Superfile's source code, developers access the logger via slog.Default() (often aliased to logger). The codebase uses structured logging with key-value pairs:
logger.Debug("Navigation event", "from", currentPath, "to", targetPath)
logger.Debug("Render operation", "panel_count", len(panels), "duration_ms", elapsed)
When debug mode is disabled, these calls return immediately with minimal overhead because the slog handler filters them before processing. When enabled, the text handler formats these as structured key-value pairs to the output destination.
The debug-info Diagnostic Command
Beyond the standard logging system, Superfile provides a separate diagnostic tool via the debug-info command implemented in src/cmd/debug_info.go. Running superfile --debug-info prints internal state including environment variables, dependency versions, and configuration values.
This command operates independently of the standard logger initialization but respects the same debug flag semantics, offering a snapshot of system state useful for bug reports and environment troubleshooting.
# Print diagnostic information
superfile --debug-info
Summary
- Configuration: The
debugfield inConfigType(src/internal/common/config_type.go) controls logging behavior, settable viaconfig.tomlor the-d/--debugCLI flag. - Initialization:
SetRootLoggerToStdout()insrc/pkg/utils/log_utils.gocreates the root logger with eitherslog.LevelInfoorslog.LevelDebug. - Output: Logs write to stdout during startup, then transition to the file specified by
log_filein configuration. - Usage: Code calls
slog.Default().Debug()for diagnostic data, which only appears when debug mode is active. - Diagnostics: The
--debug-infoflag provides instant system state reporting without entering the full TUI.
Frequently Asked Questions
How do I enable debug mode temporarily without editing config files?
Use the -d or --debug command-line flag when launching Superfile. This passes true to configure_logging in src/cmd/main.go, overriding the config file setting for that session only. This is ideal for troubleshooting specific operations without persisting verbose logs to disk.
What is the difference between --debug and --debug-info?
The --debug flag enables the debug logging level, causing logger.Debug() calls throughout the codebase to print to your log destination. The --debug-info command (implemented in src/cmd/debug_info.go) is a standalone diagnostic tool that prints system information and exits immediately, without launching the file manager interface.
Where are log files stored when debug mode is enabled?
Superfile initially logs to stdout during bootstrap. Once configuration loads, logs write to the path specified by the log_file entry in your config.toml. If no log file is configured, output may remain on stdout depending on your specific build and initialization sequence.
Why don't I see Debug level messages even with debug enabled?
Ensure the SetRootLoggerToStdout function in src/pkg/utils/log_utils.go is receiving true for its debug parameter. Debug messages only appear when the slog.HandlerOptions.Level is set to slog.LevelDebug. If using the CLI flag, verify it is being parsed correctly in src/cmd/main.go and passed through to the logging configuration function.
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 →