# How Lifetrace Configures the Loguru Logging Framework for Production

> Lifetrace configures the Loguru logging framework for production. Discover how it sets up structured console logs and daily sequenced files with error tracking for robust application monitoring.

- Repository: [FreeU-group/lifetrace](https://github.com/freeu-group/lifetrace)
- Tags: how-to-guide
- Published: 2026-03-02

---

**Lifetrace uses the Loguru logging framework, configured at runtime via Dynaconf settings in [`lifetrace/util/logging_config.py`](https://github.com/freeu-group/lifetrace/blob/main/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`](https://github.com/freeu-group/lifetrace/blob/main/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:

1. **Clears existing handlers** via `logger.remove()` to ensure no duplicate sinks.
2. **Builds a module filter** that silences logs from packages listed in the `quiet_modules` configuration (e.g., `uvicorn`, `sqlalchemy`).
3. **Adds a console sink** pointing to `sys.stderr` with a timestamp-rich format controlled by `console_level`.
4. **Creates daily-sequenced log files** when `log_path` ends with a trailing slash, generating files like `2024-01-15-1.log` and `2024-01-15-1.error.log`.

### Dynaconf Settings Schema in [`lifetrace/util/settings.py`](https://github.com/freeu-group/lifetrace/blob/main/lifetrace/util/settings.py)

Configuration defaults are declared using Dynaconf validators in [`lifetrace/util/settings.py`](https://github.com/freeu-group/lifetrace/blob/main/lifetrace/util/settings.py):

```python

# 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`](https://github.com/freeu-group/lifetrace/blob/main/lifetrace/server.py)

When the Lifetrace backend starts, the entry point in [`lifetrace/server.py`](https://github.com/freeu-group/lifetrace/blob/main/lifetrace/server.py) orchestrates the logging bootstrap:

```python

# 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`](https://github.com/freeu-group/lifetrace/blob/main/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 at `file_level` or higher
- `2024-01-15-1.error.log` – Only `ERROR` level 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:

1. **Main sink**: Captures everything from `DEBUG` (or configured `file_level`) up, retaining logs for **7 days**.
2. **Error sink**: Captures only `ERROR` and 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:

```python

# 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`](https://github.com/freeu-group/lifetrace/blob/main/config.yaml) in their user configuration directory:

```yaml

# ~/.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 `LoggerManager` in [`lifetrace/util/logging_config.py`](https://github.com/freeu-group/lifetrace/blob/main/lifetrace/util/logging_config.py).
- Configuration is driven by **Dynaconf** settings (`logging.level`, `log_path`, `quiet_modules`, etc.) defined in [`lifetrace/util/settings.py`](https://github.com/freeu-group/lifetrace/blob/main/lifetrace/util/settings.py).
- At runtime, [`lifetrace/server.py`](https://github.com/freeu-group/lifetrace/blob/main/lifetrace/server.py) copies settings, resolves the user data directory, and calls `setup_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.log` file for ERROR-level records.
- Developers retrieve the logger via `get_logger()` to ensure consistent