Skip to content

Latest commit

 

History

History
728 lines (535 loc) · 16.2 KB

File metadata and controls

728 lines (535 loc) · 16.2 KB

Logging Framework Documentation

Version: 2.0.0-alpha.6 Added: Phase 2 completion Purpose: Structured logging with console and file output


Overview

The AI CLI Preparation tool now includes a comprehensive logging framework that replaces the previous print-based vlog() system with structured Python logging. This provides better control over log output, supports multiple output destinations, and enables production-grade debugging capabilities.

Key Features

  • Multiple log levels: DEBUG, INFO, WARNING, ERROR, CRITICAL
  • Console output with colors: Visual distinction between log levels
  • File logging: Persistent logs for production debugging
  • Backward compatible: Existing vlog() calls automatically use new system
  • Configurable: Control verbosity, output destinations, and formatting
  • Testing support: Full integration with pytest's caplog fixture

Quick Start

Basic Usage

from cli_audit import setup_logging, get_logger

# Setup logging
setup_logging(verbose=True)

# Get logger instance
logger = get_logger()

# Log messages
logger.info("Starting operation")
logger.warning("Potential issue detected")
logger.error("Operation failed")

Using Convenience Functions

from cli_audit.logging_config import info, warning, error

# Simple logging with verbose flag
info("Processing complete", verbose=True)
warning("Deprecated feature used", verbose=True)
error("Configuration invalid", verbose=True)

Backward Compatible vlog()

from cli_audit.common import vlog

# Existing code continues to work
vlog("Detecting installations...", verbose=True)
# Now uses structured logging internally

Configuration

setup_logging() Parameters

def setup_logging(
    level: str = "INFO",
    log_file: Optional[str] = None,
    verbose: bool = False,
    quiet: bool = False,
    propagate: bool = False,
) -> logging.Logger

Parameters:

  • level (str): Base log level - "DEBUG", "INFO", "WARNING", "ERROR", or "CRITICAL"

    • Default: "INFO"
    • Example: setup_logging(level="DEBUG")
  • log_file (str | None): Path to log file for persistent logging

    • Default: None (console only)
    • Creates parent directories if needed
    • Example: setup_logging(log_file="~/.cli-audit/logs/audit.log")
  • verbose (bool): Enable verbose (DEBUG) output

    • Default: False
    • Overrides level parameter when True
    • Example: setup_logging(verbose=True)
  • quiet (bool): Suppress console output (file logging only)

    • Default: False
    • Useful for background processes
    • Example: setup_logging(quiet=True, log_file="/var/log/cli-audit.log")
  • propagate (bool): Allow log propagation to root logger

    • Default: False
    • Set to True for pytest integration
    • Example: setup_logging(propagate=True) # For testing

Returns: Configured logging.Logger instance


Log Levels

Level Hierarchy

From most to least verbose:

  1. DEBUG: Detailed diagnostic information

    • File operations, API calls, algorithm steps
    • Example: "Checking PATH directory: /home/user/.cargo/bin"
  2. INFO: General informational messages

    • Operation progress, successful completions
    • Example: "Detected 3 installations of ripgrep"
  3. WARNING: Potential issues that don't prevent operation

    • Configuration warnings, deprecations
    • Example: "PATH ordering issue: /usr/bin before ~/.cargo/bin"
  4. ERROR: Operation failures that need attention

    • Installation failures, network errors
    • Example: "Failed to install tool: network timeout"
  5. CRITICAL: Severe errors requiring immediate action

    • System-level failures, data corruption
    • Example: "Cannot access package manager: permission denied"

Console Output

Log levels are visually distinguished with colors and symbols:

🔍 DEBUG: Cyan text
✓ INFO: Green text
⚠️  WARNING: Yellow text
✗ ERROR: Red text
🚨 CRITICAL: Bold red text

Usage Examples

Example 1: Basic Console Logging

from cli_audit import setup_logging, get_logger

# Setup with default INFO level
setup_logging()

logger = get_logger()
logger.info("Application started")
logger.warning("Using default configuration")
logger.error("Configuration file not found")

Output:

✓ INFO Application started
⚠️  WARNING Using default configuration
✗ ERROR Configuration file not found

Example 2: Verbose Debug Logging

from cli_audit import setup_logging, get_logger

# Enable verbose (DEBUG) output
setup_logging(verbose=True)

logger = get_logger()
logger.debug("Scanning PATH directories")
logger.debug("Found candidate: /home/user/.cargo/bin/rg")
logger.info("Detection complete")

Output:

🔍 DEBUG Scanning PATH directories
🔍 DEBUG Found candidate: /home/user/.cargo/bin/rg
✓ INFO Detection complete

Example 3: File Logging for Production

from cli_audit import setup_logging, get_logger

# Log to file with auto-directory creation
setup_logging(
    level="INFO",
    log_file="~/.cli-audit/logs/production.log"
)

logger = get_logger()
logger.info("Production deployment started")
logger.error("Failed to connect to package registry")

File content (~/.cli-audit/logs/production.log):

2025-10-09 14:23:45 [INFO] cli_audit: Production deployment started
2025-10-09 14:23:46 [ERROR] cli_audit: Failed to connect to package registry

Example 4: Quiet Mode (File Only)

from cli_audit import setup_logging, get_logger

# Suppress console, log to file only
setup_logging(
    quiet=True,
    log_file="/var/log/cli-audit-daemon.log"
)

logger = get_logger()
logger.info("Daemon started")  # Only in file, not console

Example 5: Using Convenience Functions

from cli_audit.logging_config import info, warning, error, debug

# Simple logging with verbose control
debug("Detailed diagnostic info", verbose=True)
info("Operation progress update", verbose=True)
warning("Non-critical issue detected", verbose=True)
error("Operation failed", verbose=True)

Example 6: Backward Compatible vlog()

from cli_audit.common import vlog

# Existing code works without modification
vlog("Detecting installations...", verbose=True)
vlog("Found 3 installations", verbose=True)

# Internally uses new logging system
# Output: ✓ INFO Detecting installations...
#         ✓ INFO Found 3 installations

Integration with Existing Code

Phase 2.1-2.5 Modules

All existing modules automatically use the new logging system through vlog():

# cli_audit/reconcile.py
from .common import vlog

def detect_installations(tool_name: str, verbose: bool = False):
    vlog(f"Detecting installations of {tool_name}...", verbose)
    # vlog now uses structured logging internally

No Code Changes Required

Existing code using vlog() continues to work:

  • cli_audit/environment.py - 12 vlog calls
  • cli_audit/config.py - 8 vlog calls
  • cli_audit/installer.py - 23 vlog calls
  • cli_audit/bulk.py - 15 vlog calls
  • cli_audit/upgrade.py - 18 vlog calls
  • cli_audit/reconcile.py - 34 vlog calls

Total: 110+ vlog calls automatically upgraded to structured logging


Testing

Unit Tests

The logging framework includes 19 comprehensive tests:

pytest tests/test_logging.py -v

Test Coverage:

  • ✅ Setup with different levels (DEBUG, INFO, WARNING, etc.)
  • ✅ Verbose and quiet modes
  • ✅ File logging with directory creation
  • ✅ Colored formatter with/without colors
  • ✅ Convenience functions (debug, info, warning, error, critical)
  • ✅ Backward compatibility with vlog()

Integration with pytest

For testing code that uses logging:

def test_my_function(caplog):
    """Test function with logging."""
    from cli_audit import setup_logging, get_logger

    # Enable propagate for pytest integration
    setup_logging(level="INFO", propagate=True)

    # Test your code
    with caplog.at_level(logging.INFO, logger="cli_audit"):
        my_function()
        assert "Expected message" in caplog.text

Configuration in CLI

Environment Variables

Control logging via environment variables:

# Enable debug output
export CLI_AUDIT_DEBUG=1

# Specify log file
export CLI_AUDIT_LOG_FILE=~/.cli-audit/debug.log

# Set log level
export CLI_AUDIT_LOG_LEVEL=WARNING

CLI Flags (Future)

Planned CLI integration:

# Verbose output
cli_audit audit --verbose

# Quiet mode
cli_audit install --quiet --log-file /tmp/install.log

# Debug level
cli_audit reconcile --log-level DEBUG

Best Practices

1. Choose Appropriate Log Levels

# DEBUG: Detailed diagnostic information
logger.debug(f"Checking PATH directory: {path_dir}")

# INFO: Successful operations and progress
logger.info(f"Detected {count} installations")

# WARNING: Potential issues
logger.warning(f"PATH ordering issue: {issue}")

# ERROR: Operation failures
logger.error(f"Failed to install {tool}: {error}")

# CRITICAL: System-level failures
logger.critical(f"Cannot access package manager")

2. Use File Logging for Production

# Development: Console only
setup_logging(verbose=True)

# Production: Console + File
setup_logging(
    level="INFO",
    log_file="/var/log/cli-audit.log"
)

# Background daemon: File only
setup_logging(
    quiet=True,
    log_file="/var/log/cli-audit-daemon.log"
)

3. Include Context in Log Messages

# Good: Includes context
logger.error(f"Failed to install {tool_name} via {method}: {error_message}")

# Bad: Missing context
logger.error("Installation failed")

4. Use Structured Logging

# Log structured data
logger.info(
    f"Installation complete: tool={tool_name}, "
    f"version={version}, method={method}, duration={duration}s"
)

5. Handle Exceptions with Logging

try:
    result = install_tool(tool_name)
except Exception as e:
    logger.error(f"Installation failed: {e}", exc_info=True)
    # exc_info=True includes stack trace in file logs

Performance Considerations

Log Level Filtering

Logging framework only evaluates log messages at or above the configured level:

# With level=INFO
logger.debug("Expensive operation")  # ← Never evaluated (fast)
logger.info("Regular operation")     # ← Evaluated and logged

File I/O

File logging is buffered and asynchronous-safe:

  • Console output: Immediate (for user feedback)
  • File output: Buffered (for performance)

Memory Usage

  • Logger instance: ~1KB (singleton)
  • Each handler: ~2KB
  • Log records: Garbage collected after processing

Troubleshooting

Issue: Logs Not Appearing

Symptom: No console output despite logging calls

Solution:

# Check log level
logger = get_logger()
print(logger.level)  # Should be <= logging.INFO

# Ensure verbose=True for vlog
vlog("Message", verbose=True)  # Not False!

# Verify logger is configured
from cli_audit import setup_logging
setup_logging(verbose=True)  # Call before logging

Issue: File Not Created

Symptom: Log file not created despite log_file parameter

Solution:

# Check file path
import os
log_path = os.path.expanduser("~/.cli-audit/logs/audit.log")
print(f"Log file: {log_path}")

# Verify permissions
print(f"Can write: {os.access(os.path.dirname(log_path), os.W_OK)}")

# Create directory manually if needed
os.makedirs(os.path.dirname(log_path), exist_ok=True)

Issue: Colors Not Showing

Symptom: ANSI color codes visible instead of colored text

Solution:

# Colors require TTY
import sys
print(f"TTY: {sys.stdout.isatty()}")  # Should be True

# Force colors in ColoredFormatter
formatter = ColoredFormatter(fmt, use_colors=True)

Issue: Duplicate Log Messages

Symptom: Each log message appears multiple times

Solution:

# Clear handlers before setup
logger = logging.getLogger("cli_audit")
logger.handlers.clear()  # Remove old handlers

# Then setup fresh
setup_logging(verbose=True)

Migration Guide

From Old vlog() System

Old Code:

from cli_audit.common import vlog

def my_function(verbose=False):
    vlog("Starting operation", verbose)
    # ... code ...
    vlog("Operation complete", verbose)

New Code (Option 1 - No Changes):

from cli_audit.common import vlog

# Existing code works unchanged
def my_function(verbose=False):
    vlog("Starting operation", verbose)
    vlog("Operation complete", verbose)
# vlog now uses structured logging internally

New Code (Option 2 - Direct Logging):

from cli_audit import get_logger

def my_function():
    logger = get_logger()
    logger.info("Starting operation")
    # ... code ...
    logger.info("Operation complete")

From print() Statements

Old Code:

def install_tool(tool_name):
    print(f"Installing {tool_name}...")
    # ... code ...
    print(f"✓ Installed {tool_name}")

New Code:

from cli_audit import get_logger

def install_tool(tool_name):
    logger = get_logger()
    logger.info(f"Installing {tool_name}...")
    # ... code ...
    logger.info(f"Installed {tool_name}")

API Reference

Functions

setup_logging()

def setup_logging(
    level: str = "INFO",
    log_file: Optional[str] = None,
    verbose: bool = False,
    quiet: bool = False,
    propagate: bool = False,
) -> logging.Logger

Configure logging for the application. See Configuration section for details.

get_logger()

def get_logger() -> logging.Logger

Get the configured logger instance. Initializes with defaults if not yet set up.

debug()

def debug(msg: str, verbose: bool = True) -> None

Log debug message (only if verbose=True).

info()

def info(msg: str, verbose: bool = True) -> None

Log info message.

warning()

def warning(msg: str, verbose: bool = True) -> None

Log warning message.

error()

def error(msg: str, verbose: bool = True) -> None

Log error message.

critical()

def critical(msg: str, verbose: bool = True) -> None

Log critical message.

Classes

ColoredFormatter

class ColoredFormatter(logging.Formatter):
    """Formatter with colored output for different log levels."""

    def __init__(self, fmt: str, use_colors: bool = True):
        """Initialize with format string and color flag."""

Color Mapping:

  • DEBUG: Cyan (🔍)
  • INFO: Green (✓)
  • WARNING: Yellow (⚠️)
  • ERROR: Red (✗)
  • CRITICAL: Bold Red (🚨)

Changelog

Version 2.0.0-alpha.6 (2025-10-09)

Added:

  • Structured logging framework with Python logging module
  • Console output with colored formatting
  • File logging with automatic directory creation
  • Multiple log levels (DEBUG, INFO, WARNING, ERROR, CRITICAL)
  • Backward compatible vlog() integration
  • Convenience functions: debug(), info(), warning(), error(), critical()
  • 19 comprehensive unit tests
  • Full pytest integration with caplog support

Changed:

  • vlog() now uses structured logging internally (backward compatible)
  • All print-based logging converted to proper log levels

Fixed:

  • Log output now respects configured levels
  • File logging creates parent directories automatically
  • Colors disabled for non-TTY output (e.g., piped output)

Future Enhancements

Planned Features

  1. Structured Log Fields

    logger.info("Installation complete", extra={
        "tool": tool_name,
        "version": version,
        "duration": duration
    })
  2. JSON Log Format

    setup_logging(format="json", log_file="audit.jsonl")
    # Output: {"timestamp":"2025-10-09T14:23:45","level":"INFO","message":"..."}
  3. Log Rotation

    setup_logging(
        log_file="audit.log",
        max_bytes=10*1024*1024,  # 10MB
        backup_count=5
    )
  4. Remote Logging

    setup_logging(
        syslog_host="logs.example.com",
        syslog_port=514
    )
  5. Performance Metrics

    logger.metric("install_duration", duration, tool=tool_name)

Document Version: 1.0 Last Updated: 2025-10-09 Test Coverage: 19 tests, 100% passing Integration Status: ✅ Fully integrated with Phases 2.1-2.5