How Lifetrace Configures the Loguru Logging Framework for Production
Lifetrace uses the Loguru logging framework, configured at runtime via Dynaconf settings in lifetrace/util/logging_config.py to output structured console logs and daily-sequenced files with separate error tracking.
Lifetrace is an open-source life-tracking application that relies on a centralized, highly configurable logging subsystem. Instead of Python’s standard library logging, the codebase adopts Loguru to provide a singleton logger interface with automatic structured formatting and simplified configuration management.
Why Lifetrace Uses Loguru Instead of Standard Logging
Standard Python logging requires boilerplate setup for handlers, formatters, and filter chains. Loguru collapses this complexity into a single importable logger object while retaining full control over sinks (output targets). In Lifetrace, this choice enables dynamic reconfiguration at server startup without restarting the process, and provides built-in support for colorized console output and file rotation that the standard library lacks without third-party packages.
Core Logging Configuration Architecture
The LoggerManager Class in lifetrace/util/logging_config.py
The heart of the subsystem is the LoggerManager class, instantiated by the setup_logging() function. This class encapsulates all Loguru configuration logic:
- Clears existing handlers via
logger.remove()to ensure no duplicate sinks. - Builds a module filter that silences logs from packages listed in the
quiet_modulesconfiguration (e.g.,uvicorn,sqlalchemy). - Adds a console sink pointing to
sys.stderrwith a timestamp-rich format controlled byconsole_level. - Creates daily-sequenced log files when
log_pathends with a trailing slash, generating files like2024-01-15-1.logand2024-01-15-1.error.log.
Dynaconf Settings Schema in lifetrace/util/settings.py
Configuration defaults are declared using Dynaconf validators in lifetrace/util/settings.py:
# lifetrace/util/settings.py (excerpt)
Validator("logging.level", default="INFO")
Validator("logging.log_path", default="logs/")
Validator("logging.console_level", default="INFO")
Validator("logging.file_level", default="INFO")
Validator("logging.quiet_modules", default=[], is_type_of=list)
These keys allow users to override behavior via YAML configuration files or environment variables without touching source code.
How Logging Is Initialized at Runtime
Server Startup Sequence in lifetrace/server.py
When the Lifetrace backend starts, the entry point in lifetrace/server.py orchestrates the logging bootstrap:
# lifetrace/server.py
logging_config = settings.get("logging").copy()
logging_config["log_path"] = str(get_user_logs_dir()) + "/"
setup_logging(logging_config) # ← applies Loguru configuration
logger = get_logger()
This pattern ensures the log directory is relocated to the user’s data folder (platform-specific) before sinks are created.
Dynamic Log Path Resolution
The _generate_log_file_path helper inside logging_config.py scans the target directory for existing files matching the current date pattern. It extracts the highest sequence number (the N in YYYY-MM-DD-N.log) and increments it, ensuring each process start writes to a fresh file rather than appending to an existing session.
Daily Log Rotation and Error Separation
Sequenced File Naming Convention
Unlike time-based rotation, Lifetrace uses launch-based sequencing. Each time the application starts, it creates a new pair of files:
2024-01-15-1.log– All logs atfile_levelor higher2024-01-15-1.error.log– OnlyERRORlevel and above
If the application restarts three times on the same day, you will see ...-1.log, ...-2.log, and ...-3.log, preventing log corruption and making debugging specific sessions trivial.
Separate Error Log Handling
The LoggerManager adds two distinct file sinks:
- Main sink: Captures everything from
DEBUG(or configuredfile_level) up, retaining logs for 7 days. - Error sink: Captures only
ERRORand above, retaining logs for 30 days to ensure long-term visibility of critical issues.
Both sinks use a pipe-separated format (|) for easy parsing by log aggregation tools.
Usage Examples for Developers
Getting a Logger Instance
Any module within Lifetrace should import the configured logger via the utility function:
# Example: lifetrace/util/utils.py
from lifetrace.util.logging_config import get_logger
log = get_logger()
log.info("Utility module loaded")
log.debug("Configuration details: %s", config_data)
This ensures all modules share the same Loguru instance with consistent formatting and filtering.
Customizing Configuration via YAML
End users can override defaults by creating a config.yaml in their user configuration directory:
# ~/.config/lifetrace/config.yaml
logging:
level: "DEBUG"
console_level: "DEBUG"
file_level: "INFO"
log_path: "custom_logs/"
quiet_modules:
- "uvicorn"
- "sqlalchemy"
- "asyncio"
After restarting the server, Loguru will reinitialize with these settings, suppressing noisy third-party libraries and writing to the custom directory.
Summary
- Lifetrace uses Loguru as its logging framework, wrapped by a custom
LoggerManagerinlifetrace/util/logging_config.py. - Configuration is driven by Dynaconf settings (
logging.level,log_path,quiet_modules, etc.) defined inlifetrace/util/settings.py. - At runtime,
lifetrace/server.pycopies settings, resolves the user data directory, and callssetup_logging()to initialize sinks. - The system creates daily-sequenced log files (e.g.,
2024-01-15-1.log) for each process start, plus a separate.error.logfile for ERROR-level records. - Developers retrieve the logger via
get_logger()to ensure consistent
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 →