Skip to content
Edit this page

Colored Logging

This example demonstrates Logly's colored logging features, including default color mapping and custom color configuration.

Default Colored Levels (NEW in v0.1.5)

Logly automatically applies colors to log levels when color=True:

from logly import logger

# Colors are automatic! No need to configure
logger.trace("Entering function")           # Cyan
logger.debug("Variable x = 42")             # Blue
logger.info("Server started on port 8000")  # White
logger.success("User created successfully") # Green
logger.warning("Rate limit approaching")    # Yellow
logger.error("Database connection failed")  # Red
logger.critical("System crash imminent")    # Bright Red
logger.fail("Payment authorization failed") # Magenta (NEW)

Default Color Mapping

Level Color ANSI Code Use Case
TRACE Cyan 36 Most verbose debugging
DEBUG Blue 34 Development debugging
INFO White 37 General information
SUCCESS Green 32 Successful operations
WARNING Yellow 33 Warning messages
ERROR Red 31 Error conditions
CRITICAL Bright Red 91 Critical failures
FAIL Magenta 35 Operation failures

FAIL Level - Operation Failures

The FAIL level is designed for expected operation failures (vs unexpected errors):

from logly import logger

# Authentication failures
logger.fail("Login failed", 
    user="alice", 
    attempts=3, 
    reason="invalid_password"
)

# Payment failures
logger.fail("Payment declined", 
    transaction_id="txn_123", 
    card_last4="1234",
    reason="insufficient_funds"
)

# Validation failures
logger.fail("Validation failed",
    field="email",
    value="invalid@",
    rule="email_format"
)

# API/Authorization failures
logger.fail("API request denied",
    endpoint="/api/admin",
    status=403,
    reason="insufficient_permissions"
)

# Timeout failures
logger.fail("Operation timeout",
    operation="database_query",
    timeout_seconds=30
)

FAIL vs ERROR

When to use FAIL: - Authentication failures (login, token validation) - Authorization denials (permission checks) - Validation failures (form validation, data validation) - Payment/transaction failures - Business rule violations - Operation timeouts - Expected failure conditions

When to use ERROR: - Unexpected exceptions - System errors - Bugs in code - Infrastructure failures - Unhandled error conditions

# FAIL - Expected operation failure (user mistake)
logger.fail("Login failed", reason="wrong_password")

# ERROR - Unexpected technical error (system issue)
logger.error("Login system crashed", exception=str(e))

Custom Colors

Override default colors with your own color scheme:

from logly import logger

# Define custom colors using color names
logger.configure(
    color=True,
    level_colors={
        "TRACE": "BRIGHT_CYAN",
        "DEBUG": "BRIGHT_BLUE",
        "INFO": "BRIGHT_WHITE",
        "SUCCESS": "BRIGHT_GREEN",
        "WARNING": "BRIGHT_YELLOW",
        "ERROR": "BRIGHT_RED",
        "CRITICAL": "BRIGHT_MAGENTA",
        "FAIL": "MAGENTA"
    }
)

# Or use ANSI color codes directly
logger.configure(
    color=True,
    level_colors={
        "TRACE": "96",   # Bright Cyan
        "DEBUG": "94",   # Bright Blue
        "INFO": "97",    # Bright White
        "SUCCESS": "92", # Bright Green
        "WARNING": "93", # Bright Yellow
        "ERROR": "91",   # Bright Red
        "CRITICAL": "95",# Bright Magenta
        "FAIL": "35"     # Magenta
    }
)

logger.info("Custom colors applied!")

Available Colors

Standard Colors

Color Name Code Bright Version Code
"BLACK" 30 "BRIGHT_BLACK" / "GRAY" 90
"RED" 31 "BRIGHT_RED" 91
"GREEN" 32 "BRIGHT_GREEN" 92
"YELLOW" 33 "BRIGHT_YELLOW" 93
"BLUE" 34 "BRIGHT_BLUE" 94
"MAGENTA" 35 "BRIGHT_MAGENTA" 95
"CYAN" 36 "BRIGHT_CYAN" 96
"WHITE" 37 "BRIGHT_WHITE" 97

Disabling Colors

Disable colors for plain text output:

from logly import logger

# Disable all colors
logger.configure(color=False)

logger.info("No colors")
logger.error("Plain text only")

Per-Level Color Control

Enable colors for specific levels only:

from logly import logger

logger.configure(
    color=True,
    color_levels={
        "INFO": False,    # No color for INFO
        "DEBUG": False,   # No color for DEBUG
        "ERROR": True,    # Color for ERROR
        "CRITICAL": True, # Color for CRITICAL
        "FAIL": True      # Color for FAIL
    }
)

# Only errors and critical messages are colored
logger.info("Plain text")       # No color
logger.error("Colored error")   # Red
logger.fail("Colored failure")  # Magenta

Advanced: Color Callbacks

Color callbacks give you complete control over log message coloring and formatting. When a color_callback is provided, it takes full precedence over built-in colors and level_colors configuration.

Color Callback vs Built-in Colors

Feature Built-in Colors Color Callback
Setup Automatic (v0.1.5+) or level_colors dict Custom function
Flexibility Predefined ANSI colors Any color library or custom ANSI codes
Control Per-level color mapping Full control over text formatting
Precedence Lower priority Highest priority (overrides everything)
Use Case Simple color schemes Rich terminal features (bold, underline, gradients)
Libraries None needed Rich, colorama, termcolor, etc.

When to Use Color Callbacks

Use built-in colors (level_colors) when: - You want simple, clean color mapping - ANSI colors are sufficient for your needs - You prefer configuration over code - You want minimal dependencies

Use color callbacks when: - You need advanced formatting (bold, underline, dim, etc.) - You want to integrate with Rich, colorama, or other libraries - You need conditional coloring based on message content - You want dynamic colors based on runtime state - You need gradient effects or custom styling

Basic Color Callback Example

from logly import logger

def custom_color(level: str, text: str) -> str:
    """Custom color callback using ANSI codes."""
    colors = {
        "TRACE": "\033[36m",      # Cyan
        "DEBUG": "\033[34m",      # Blue
        "INFO": "\033[37m",       # White
        "SUCCESS": "\033[1;32m",  # Bold Green
        "WARNING": "\033[1;33m",  # Bold Yellow
        "ERROR": "\033[1;31m",    # Bold Red
        "CRITICAL": "\033[1;91m", # Bold Bright Red
        "FAIL": "\033[1;35m",     # Bold Magenta
    }
    reset = "\033[0m"

    color = colors.get(level, "")
    return f"{color}{text}{reset}" if color else text

logger.configure(
    color=True,
    color_callback=custom_color  # Takes full control!
)

logger.success("Bold green!")
logger.fail("Bold magenta!")
logger.error("Bold red!")

Integration with Rich Library

Use Rich for advanced terminal formatting:

from logly import logger
from rich.console import Console

console = Console()

def rich_color(level: str, text: str) -> str:
    """Color callback using Rich library."""
    styles = {
        "TRACE": "dim cyan",
        "DEBUG": "blue",
        "INFO": "white",
        "SUCCESS": "bold green",
        "WARNING": "bold yellow",
        "ERROR": "bold red",
        "CRITICAL": "bold white on red",
        "FAIL": "bold magenta",
    }

    style = styles.get(level, "white")

    # Rich provides advanced formatting
    from io import StringIO
    buffer = StringIO()
    temp_console = Console(file=buffer, force_terminal=True)
    temp_console.print(text, style=style, end="")
    return buffer.getvalue()

logger.configure(
    color=True,
    color_callback=rich_color
)

logger.info("Standard info")
logger.success("✓ Operation completed")
logger.critical("⚠ CRITICAL ALERT")
logger.fail("✗ Payment failed")

Integration with Colorama

Use colorama for cross-platform color support:

from logly import logger
from colorama import Fore, Style, init

# Initialize colorama (important for Windows)
init(autoreset=True)

def colorama_color(level: str, text: str) -> str:
    """Color callback using colorama."""
    colors = {
        "TRACE": Fore.CYAN,
        "DEBUG": Fore.BLUE,
        "INFO": Fore.WHITE,
        "SUCCESS": Fore.GREEN + Style.BRIGHT,
        "WARNING": Fore.YELLOW + Style.BRIGHT,
        "ERROR": Fore.RED + Style.BRIGHT,
        "CRITICAL": Fore.RED + Style.BRIGHT,
        "FAIL": Fore.MAGENTA + Style.BRIGHT,
    }

    color = colors.get(level, "")
    return f"{color}{text}{Style.RESET_ALL}"

logger.configure(
    color=True,
    color_callback=colorama_color
)

logger.success("Works perfectly on Windows!")
logger.fail("Cross-platform colors")

Integration with Termcolor

Use termcolor for quick color formatting:

from logly import logger
from termcolor import colored

def termcolor_callback(level: str, text: str) -> str:
    """Color callback using termcolor."""
    color_map = {
        "TRACE": ("cyan", None),
        "DEBUG": ("blue", None),
        "INFO": ("white", None),
        "SUCCESS": ("green", ["bold"]),
        "WARNING": ("yellow", ["bold"]),
        "ERROR": ("red", ["bold"]),
        "CRITICAL": ("white", ["bold"], "on_red"),
        "FAIL": ("magenta", ["bold"]),
    }

    config = color_map.get(level, ("white", None))
    color = config[0]
    attrs = config[1] if len(config) > 1 else None
    on_color = config[2] if len(config) > 2 else None

    return colored(text, color, on_color, attrs=attrs)

logger.configure(
    color=True,
    color_callback=termcolor_callback
)

logger.info("Using termcolor")
logger.critical("Critical with background")

Advanced: Conditional Coloring

Apply different colors based on message content:

from logly import logger

def smart_color(level: str, text: str) -> str:
    """Conditional coloring based on content."""

    # Base color from level
    base_colors = {
        "ERROR": "\033[31m",     # Red
        "FAIL": "\033[35m",      # Magenta
        "WARNING": "\033[33m",   # Yellow
        "SUCCESS": "\033[32m",   # Green
    }

    # Enhanced colors for specific keywords
    if "database" in text.lower():
        color = "\033[1;31m"  # Bold red for database issues
    elif "payment" in text.lower() or "transaction" in text.lower():
        color = "\033[1;35m"  # Bold magenta for payment issues
    elif "timeout" in text.lower():
        color = "\033[1;33m"  # Bold yellow for timeouts
    else:
        color = base_colors.get(level, "\033[37m")

    return f"{color}{text}\033[0m"

logger.configure(
    color=True,
    color_callback=smart_color
)

logger.error("Database connection failed")  # Bold red
logger.fail("Payment timeout occurred")     # Bold magenta
logger.warning("API timeout")               # Bold yellow

Color Callback Precedence

Important: Color callbacks have highest precedence:

from logly import logger

def my_callback(level: str, text: str) -> str:
    return f"\033[35m{text}\033[0m"  # Always magenta

logger.configure(
    color=True,
    level_colors={
        "INFO": "BRIGHT_CYAN"  # This will be IGNORED
    },
    color_callback=my_callback  # This takes full control
)

logger.info("This will be MAGENTA, not cyan")  # Callback wins!

Precedence order: 1. color_callback (highest - if provided, overrides everything) 2. level_colors (medium - custom color mapping) 3. Default colors (lowest - automatic in v0.1.5+)

Best Practices

✅ DO: - Use callbacks for advanced formatting needs - Use callbacks when integrating external libraries - Use level_colors for simple color schemes - Keep callbacks fast and efficient - Handle exceptions in your callback (return plain text on error)

❌ DON'T: - Use callbacks for simple color changes (use level_colors instead) - Perform heavy computation in callbacks (called for every log message) - Block or wait in callbacks (performance impact) - Forget to reset ANSI codes (causes color bleeding)

Complete Example

from logly import logger

# Configure with custom colors
logger.configure(
    level="TRACE",
    color=True,
    level_colors={
        "TRACE": "CYAN",
        "DEBUG": "BLUE",
        "INFO": "WHITE",
        "SUCCESS": "BRIGHT_GREEN",
        "WARNING": "BRIGHT_YELLOW",
        "ERROR": "BRIGHT_RED",
        "CRITICAL": "BRIGHT_MAGENTA",
        "FAIL": "MAGENTA"
    }
)

# Application startup
logger.info("Starting application", version="1.0.0")
logger.success("Configuration loaded")

# Simulated operations
logger.trace("Entering authentication flow")
logger.debug("Checking credentials", user="alice")

# Success case
logger.success("User authenticated", user="alice", session_id="sess_123")

# Warning case
logger.warning("Rate limit at 80%", current=80, limit=100)

# Failure case (expected failure)
logger.fail("Payment declined", 
    transaction_id="txn_456",
    reason="insufficient_funds",
    amount=150.00,
    available=75.50
)

# Error case (unexpected error)
try:
    1 / 0
except Exception as e:
    logger.error("Unexpected error", exception=str(e))

# Critical case
logger.critical("Database connection pool exhausted", 
    active=100, 
    max=100,
    queued=50
)

logger.complete()

Expected Output

2025-01-15T14:30:00.123456+00:00 [INFO] Starting application | version=1.0.0
2025-01-15T14:30:00.124567+00:00 [SUCCESS] Configuration loaded
2025-01-15T14:30:00.125678+00:00 [TRACE] Entering authentication flow
2025-01-15T14:30:00.126789+00:00 [DEBUG] Checking credentials | user=alice
2025-01-15T14:30:00.127890+00:00 [SUCCESS] User authenticated | user=alice | session_id=sess_123
2025-01-15T14:30:00.128901+00:00 [WARN] Rate limit at 80% | current=80 | limit=100
2025-01-15T14:30:00.129012+00:00 [FAIL] Payment declined | transaction_id=txn_456 | reason=insufficient_funds | amount=150.0 | available=75.5
2025-01-15T14:30:00.130123+00:00 [ERROR] Unexpected error | exception=division by zero
2025-01-15T14:30:00.131234+00:00 [CRITICAL] Database connection pool exhausted | active=100 | max=100 | queued=50

Each level will appear in its configured color in your terminal!

Key Features Demonstrated

  • Default Colors: Automatic color mapping for all 8 levels
  • FAIL Level: New level for operation failures (v0.1.5)
  • Custom Colors: Override defaults with ANSI codes or color names
  • Color Control: Per-level enable/disable
  • Color Callbacks: Integration with external color libraries
  • Semantic Logging: FAIL for expected failures, ERROR for unexpected errors