Skip to content

Unfazed Logging

Unfazed configures logging through Python's standard logging.config.dictConfig. You provide a LOGGING dictionary in your settings, and Unfazed merges it with sensible defaults so that framework-internal loggers (unfazed, uvicorn, tortoise) work out of the box. If you don't provide any logging config, the defaults are used as-is.

Quick Start

Add a LOGGING key to your settings to configure application-level logging:

# settings.py
UNFAZED_SETTINGS = {
    "PROJECT_NAME": "myproject",
    "LOGGING": {
        "version": 1,
        "formatters": {
            "verbose": {
                "format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s",
            },
        },
        "handlers": {
            "console": {
                "class": "logging.StreamHandler",
                "level": "INFO",
                "formatter": "verbose",
                "stream": "ext://sys.stdout",
            },
        },
        "loggers": {
            "myapp": {
                "handlers": ["console"],
                "level": "DEBUG",
                "propagate": False,
            },
        },
    },
}

Then use standard Python logging in your code:

import logging

logger = logging.getLogger("myapp")

async def my_endpoint(request):
    logger.info("Request received")
    ...

Configuration

The LOGGING setting follows Python's dictConfig schema:

"LOGGING": {
    "version": 1,                          # required, always 1
    "disable_existing_loggers": False,      # optional
    "formatters": {
        "<name>": {
            "format": "...",
            "datefmt": "...",
        },
    },
    "filters": {
        "<name>": {
            "()": "dotted.path.to.FilterClass",
        },
    },
    "handlers": {
        "<name>": {
            "class": "logging.StreamHandler",
            "level": "INFO",
            "formatter": "<formatter_name>",
            "filters": ["<filter_name>"],
            # ... handler-specific options
        },
    },
    "loggers": {
        "<name>": {
            "level": "DEBUG",
            "handlers": ["<handler_name>"],
            "filters": ["<filter_name>"],
            "propagate": True,
        },
    },
    "root": {
        "level": "WARNING",
        "handlers": ["<handler_name>"],
    },
}

How merging works

Your config is merged with Unfazed's defaults — it does not replace them. The merge rules are:

  • formatters, handlers, filters, loggers — your entries are added alongside the defaults. If you define a key that already exists in the defaults, your value wins.
  • Other top-level keys (e.g. version, root, disable_existing_loggers) — the defaults are kept; your values for these keys are ignored. This ensures the framework's internal logging always works.

This means you never need to re-declare the unfazed or tortoise loggers unless you want to change their configuration.

Default Configuration

When no LOGGING setting is provided (or it is empty), Unfazed uses these defaults:

{
    "version": 1,
    "formatters": {
        "_simple": {
            "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
            "datefmt": "%Y-%m-%d %H:%M:%S",
        },
    },
    "handlers": {
        "_console": {
            "class": "logging.StreamHandler",
            "level": "DEBUG",
            "formatter": "_simple",
        },
    },
    "loggers": {
        "unfazed": {
            "level": "DEBUG",
            "handlers": ["_console"],
        },
        "uvicorn": {
            "level": "INFO",
            "handlers": ["_console"],
        },
        "tortoise": {
            "level": "INFO",
            "handlers": ["_console"],
        },
    },
    "filters": {},
}

Default handler and formatter names are prefixed with _ to avoid collisions with your own config.

API Reference

LogCenter

class LogCenter:
    def __init__(self, unfazed: Unfazed, dictconfig: Dict[str, Any]) -> None

Central logging configuration manager. Merges user config with defaults and applies it via logging.config.dictConfig.

Methods:

  • setup() -> None: Apply the merged logging configuration.
  • merge_default(dictconfig: Dict) -> Dict: Merge user config with the default config and return the result.

Attributes:

  • config: Dict — The final merged configuration.
  • raw_dictconfig: Dict — The original user-provided configuration.

LogConfig

class LogConfig(BaseModel):
    version: int = 1
    formatters: Dict[str, Formatter] | None = None
    filters: Dict[str, Filter] | None = None
    handlers: Dict[str, Handler] | None = None
    loggers: Dict[str, Logger] | None = None
    root: RootLogger | None = None
    incremental: bool | None = None
    disable_existing_loggers: bool | None = None

Pydantic model used to validate the LOGGING setting before it is passed to LogCenter.