Claude Code for colorlog: Colored Python Logging — Claude Skills 360 Blog
Blog / AI / Claude Code for colorlog: Colored Python Logging
AI

Claude Code for colorlog: Colored Python Logging

Published: March 28, 2028
Read time: 5 min read
By: Claude Skills 360

colorlog adds ANSI color to Python’s standard logging output. pip install colorlog. Quick: import colorlog; colorlog.basicConfig(level="DEBUG"); logger = colorlog.getLogger(__name__); logger.info("Hello"). Full: handler = colorlog.StreamHandler(); handler.setFormatter(colorlog.ColoredFormatter("%(log_color)s%(levelname)s%(reset)s:%(name)s:%(message)s")); logger.addHandler(handler). Colors: log_colors={"DEBUG":"cyan","INFO":"green","WARNING":"yellow","ERROR":"red","CRITICAL":"red,bg_white"}. Custom level: colorlog.addLevelName(25, "SUCCESS"); logger.log(25, "Done"). Secondary colors: secondary_log_colors={"message":{"ERROR":"red","CRITICAL":"red"}}. %(log_color)s, %(reset)s, %(message_log_color)s format tokens. Style escape: %(bold_cyan)s, %(bg_white)s, %(white)s. Color names: black white red green yellow blue purple cyan bold_* bg_*. Reset: reset=True appends ANSI reset at line end — default True. File + console: add logging.FileHandler (without ColoredFormatter) alongside StreamHandler. logging.config.dictConfig with () key for custom formatter class. Logger factory: colorlog.getLogger(name). TTY check: colorlog.escape_codes.escape_codes — strip if not TTY. colorlog.TTYColoredFormatter — auto-disables in non-TTY. Claude Code generates colorlog setups for development servers, CLI tools, and debug logging pipelines.

CLAUDE.md for colorlog

## colorlog Stack
- Version: colorlog >= 6.8 | pip install colorlog
- Quick: colorlog.basicConfig(level="DEBUG", format="%(log_color)s%(levelname)s:%(name)s:%(message)s")
- Handler: ColoredFormatter("%(log_color)s%(levelname)-8s%(reset)s %(message)s")
- Colors: log_colors={"DEBUG":"cyan","INFO":"green","WARNING":"yellow","ERROR":"red","CRITICAL":"bold_red"}
- TTY-safe: TTYColoredFormatter — falls back to plain text in non-TTY
- File+color: ColoredFormatter on StreamHandler; plain Formatter on FileHandler

colorlog Colored Logging Pipeline

# app/logging_config.py — colorlog setup, dict config, and dual-output logging
from __future__ import annotations

import logging
import logging.config
import sys
from pathlib import Path
from typing import Any

import colorlog


# ─────────────────────────────────────────────────────────────────────────────
# 1. Colour map
# ─────────────────────────────────────────────────────────────────────────────

DEFAULT_LOG_COLORS: dict[str, str] = {
    "DEBUG":    "cyan",
    "INFO":     "green",
    "WARNING":  "yellow",
    "ERROR":    "red",
    "CRITICAL": "bold_red,bg_white",
}

DEFAULT_SECONDARY_COLORS: dict[str, dict[str, str]] = {
    "message": {
        "WARNING":  "yellow",
        "ERROR":    "red",
        "CRITICAL": "red",
    },
}


# ─────────────────────────────────────────────────────────────────────────────
# 2. Formatter factories
# ─────────────────────────────────────────────────────────────────────────────

def make_color_formatter(
    fmt: str = "%(log_color)s%(levelname)-8s%(reset)s %(name)s: %(message)s",
    datefmt: str | None = "%H:%M:%S",
    log_colors: dict[str, str] | None = None,
    secondary_log_colors: dict[str, dict[str, str]] | None = None,
    reset: bool = True,
    style: str = "%",
) -> colorlog.ColoredFormatter:
    """
    Create a colorlog formatter with sane defaults.
    Use %(asctime)s in fmt to include timestamp.
    """
    return colorlog.ColoredFormatter(
        fmt=fmt,
        datefmt=datefmt,
        reset=reset,
        log_colors=log_colors or DEFAULT_LOG_COLORS,
        secondary_log_colors=secondary_log_colors or DEFAULT_SECONDARY_COLORS,
        style=style,
    )


def make_plain_formatter(
    fmt: str = "%(asctime)s %(levelname)-8s %(name)s: %(message)s",
    datefmt: str = "%Y-%m-%d %H:%M:%S",
) -> logging.Formatter:
    """Plain (no-color) formatter — for file output or non-TTY."""
    return logging.Formatter(fmt=fmt, datefmt=datefmt)


def make_tty_formatter(
    fmt: str = "%(log_color)s%(levelname)-8s%(reset)s %(name)s: %(message)s",
    log_colors: dict[str, str] | None = None,
) -> colorlog.TTYColoredFormatter:
    """
    TTY-aware formatter — colors in terminals, plain text in pipes/files.
    Automatically detects sys.stderr.isatty().
    """
    return colorlog.TTYColoredFormatter(
        fmt=fmt,
        log_colors=log_colors or DEFAULT_LOG_COLORS,
        reset=True,
        stream=sys.stderr,
    )


# ─────────────────────────────────────────────────────────────────────────────
# 3. Handler setup
# ─────────────────────────────────────────────────────────────────────────────

def make_console_handler(
    level: int = logging.DEBUG,
    formatter: logging.Formatter | None = None,
    stream=sys.stderr,
) -> logging.StreamHandler:
    """Colored StreamHandler for stderr (or stdout)."""
    handler = colorlog.StreamHandler(stream)
    handler.setLevel(level)
    handler.setFormatter(formatter or make_color_formatter())
    return handler


def make_file_handler(
    path: str | Path,
    level: int = logging.DEBUG,
    formatter: logging.Formatter | None = None,
    mode: str = "a",
    encoding: str = "utf-8",
) -> logging.FileHandler:
    """Plain-text FileHandler (no ANSI codes in log files)."""
    handler = logging.FileHandler(str(path), mode=mode, encoding=encoding)
    handler.setLevel(level)
    handler.setFormatter(formatter or make_plain_formatter())
    return handler


# ─────────────────────────────────────────────────────────────────────────────
# 4. Logger factory
# ─────────────────────────────────────────────────────────────────────────────

def get_logger(
    name: str,
    level: int | str = logging.DEBUG,
    log_file: str | Path | None = None,
    propagate: bool = False,
) -> logging.Logger:
    """
    Create (or get) a named logger with a colored console handler
    and optional plain-text file handler.

    Usage:
        logger = get_logger(__name__)
        logger.info("Starting up")
    """
    logger = logging.getLogger(name)
    if isinstance(level, str):
        level = getattr(logging, level.upper(), logging.DEBUG)
    logger.setLevel(level)
    logger.propagate = propagate

    if not logger.handlers:
        logger.addHandler(make_console_handler(level))
        if log_file:
            logger.addHandler(make_file_handler(log_file, level))

    return logger


def setup_root_logger(
    level: int | str = logging.INFO,
    log_file: str | Path | None = None,
    include_thread: bool = False,
) -> None:
    """
    Configure the root logger with a colored handler.
    Call once at application startup.
    """
    if isinstance(level, str):
        level = getattr(logging, level.upper(), logging.INFO)

    fmt = (
        "%(log_color)s%(levelname)-8s%(reset)s "
        + ("%(threadName)-10s " if include_thread else "")
        + "%(name)s: %(message)s"
    )

    root = logging.getLogger()
    root.setLevel(level)

    if not root.handlers:
        root.addHandler(make_console_handler(level, make_color_formatter(fmt=fmt)))
        if log_file:
            root.addHandler(make_file_handler(log_file, level))


# ─────────────────────────────────────────────────────────────────────────────
# 5. dictConfig integration
# ─────────────────────────────────────────────────────────────────────────────

def make_dict_config(
    app_name: str = "app",
    level: str = "DEBUG",
    log_file: str | None = None,
) -> dict[str, Any]:
    """
    Build a logging.config.dictConfig dict with colorlog.
    Pass to logging.config.dictConfig(make_dict_config(...)).
    """
    handlers = {
        "console": {
            "()":          "colorlog.StreamHandler",
            "formatter":   "colored",
            "stream":      "ext://sys.stderr",
            "level":       level,
        },
    }
    if log_file:
        handlers["file"] = {
            "class":     "logging.FileHandler",
            "formatter": "plain",
            "filename":  log_file,
            "mode":      "a",
            "encoding":  "utf-8",
            "level":     level,
        }

    config = {
        "version":                  1,
        "disable_existing_loggers": False,
        "formatters": {
            "colored": {
                "()":                "colorlog.ColoredFormatter",
                "format":            "%(log_color)s%(levelname)-8s%(reset)s %(name)s: %(message)s",
                "datefmt":           "%H:%M:%S",
                "reset":             True,
                "log_colors":        DEFAULT_LOG_COLORS,
                "secondary_log_colors": DEFAULT_SECONDARY_COLORS,
            },
            "plain": {
                "format":  "%(asctime)s %(levelname)-8s %(name)s: %(message)s",
                "datefmt": "%Y-%m-%d %H:%M:%S",
            },
        },
        "handlers": handlers,
        "loggers": {
            app_name: {
                "handlers":  list(handlers.keys()),
                "level":     level,
                "propagate": False,
            },
        },
        "root": {
            "handlers": ["console"],
            "level":    "WARNING",
        },
    }
    return config


def configure_from_dict(app_name: str = "app", level: str = "DEBUG", log_file: str | None = None) -> None:
    """Apply the dictConfig immediately."""
    logging.config.dictConfig(make_dict_config(app_name, level, log_file))


# ─────────────────────────────────────────────────────────────────────────────
# 6. Per-level format (LevelFormatter)
# ─────────────────────────────────────────────────────────────────────────────

def make_level_formatter() -> colorlog.LevelFormatter:
    """
    Different format strings per log level.
    DEBUG: show function and line; ERROR: show exc_info flags.
    """
    return colorlog.LevelFormatter(
        fmt={
            "DEBUG":    "%(log_color)s%(levelname)-8s%(reset)s %(name)s:%(funcName)s:%(lineno)d%(message)s",
            "INFO":     "%(log_color)s%(levelname)-8s%(reset)s %(name)s%(message)s",
            "WARNING":  "%(log_color)s%(levelname)-8s%(reset)s %(name)s%(message)s",
            "ERROR":    "%(log_color)s%(levelname)-8s%(reset)s %(name)s:%(lineno)d%(message)s",
            "CRITICAL": "%(log_color)s%(levelname)-8s%(reset)s %(name)s:%(lineno)d%(message)s",
        },
        log_colors=DEFAULT_LOG_COLORS,
    )


# ─────────────────────────────────────────────────────────────────────────────
# 7. FastAPI / Flask / Django snippets
# ─────────────────────────────────────────────────────────────────────────────

def setup_fastapi_logging(level: str = "DEBUG") -> None:
    """
    Configure colorized logging for a FastAPI app.
    Call before creating the FastAPI() instance.
    """
    setup_root_logger(level=level)
    # Quieten noisy uvicorn access log but keep error log colored
    logging.getLogger("uvicorn.access").setLevel(logging.WARNING)
    logging.getLogger("uvicorn.error").setLevel(logging.INFO)


def setup_django_logging() -> dict[str, Any]:
    """
    Return a LOGGING dict for Django settings.py.
    Assign: LOGGING = setup_django_logging()
    """
    return {
        "version": 1,
        "disable_existing_loggers": False,
        "formatters": {
            "colored": {
                "()":       "colorlog.ColoredFormatter",
                "format":   "%(log_color)s%(levelname)-8s%(reset)s %(name)s: %(message)s",
                "log_colors": DEFAULT_LOG_COLORS,
            },
        },
        "handlers": {
            "console": {
                "class":     "colorlog.StreamHandler",
                "formatter": "colored",
            },
        },
        "root": {
            "handlers": ["console"],
            "level":    "DEBUG",
        },
    }


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("=== Root logger setup ===")
    setup_root_logger(level=logging.DEBUG)
    root_log = logging.getLogger("demo")
    root_log.debug("Debug message — cyan")
    root_log.info("Info message — green")
    root_log.warning("Warning message — yellow")
    root_log.error("Error message — red")
    try:
        raise ValueError("something broke")
    except Exception:
        root_log.exception("Exception with traceback — red")

    print("\n=== get_logger helper ===")
    log = get_logger("myapp.service", level=logging.DEBUG)
    log.debug("Service starting up")
    log.info("Connected to database")
    log.warning("Deprecated endpoint called")
    log.error("Failed to process request id=42")

    print("\n=== TTY formatter (auto-strips in pipes) ===")
    tty_logger = logging.getLogger("tty_demo")
    tty_logger.setLevel(logging.DEBUG)
    tty_handler = colorlog.StreamHandler()
    tty_handler.setFormatter(make_tty_formatter())
    tty_logger.addHandler(tty_handler)
    tty_logger.info("This uses TTYColoredFormatter")
    tty_logger.warning("Only colored in a real terminal")

    print("\n=== Per-level formatter ===")
    lf_logger = logging.getLogger("levelformat")
    lf_logger.setLevel(logging.DEBUG)
    lf_handler = colorlog.StreamHandler()
    lf_handler.setFormatter(make_level_formatter())
    lf_logger.addHandler(lf_handler)
    lf_logger.debug("debug shows function+line")
    lf_logger.info("info is compact")
    lf_logger.error("error shows line number")

    print("\n=== dictConfig ===")
    configure_from_dict("myapp", level="DEBUG")
    cfg_log = logging.getLogger("myapp")
    cfg_log.info("Configured via dictConfig")
    cfg_log.critical("Critical alert!")

For the loguru alternative — loguru provides a modern drop-in replacement for the stdlib logging module with colored output built-in, structured records, and zero-config setup (from loguru import logger; logger.info("hi")); colorlog is for teams that must stay on stdlib logging (because of third-party libraries that call logging.getLogger) and want to add color without replacing the logging infrastructure — use colorlog to color an existing stdlib-logging setup, use loguru for new projects where you control all logging calls. For the rich.logging alternative — rich.logging.RichHandler renders log lines with full rich markup, clickable tracebacks, and colored level badges that look better than raw ANSI; colorlog integrates with the plain stdlib logging.config.dictConfig / fileConfig ecosystem without pulling rich in — the right choice when you need a standards-compatible logging setup. The Claude Skills 360 bundle includes colorlog skill sets covering ColoredFormatter with log_colors dict, secondary_log_colors for message coloring, TTYColoredFormatter auto-TTY detection, LevelFormatter per-level format strings, make_color_formatter()/make_plain_formatter() factories, get_logger() named logger factory, setup_root_logger() application startup, make_dict_config() for logging.config.dictConfig, file handler combo (color stdout + plain file), setup_fastapi_logging(), and setup_django_logging() LOGGING dict. Start with the free tier to try colored logging code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free