Skip to content

Migration Guide - PANTHER Core Utilities¤

Overview¤

This guide helps you migrate existing Python logging setups to PANTHER's feature-aware logging system. The migration process is designed to be incremental, allowing you to adopt features gradually without breaking existing functionality.

Migration Strategies¤

Strategy 1: Drop-in Replacement (Minimal Changes)¤

Use case: Existing codebase with standard Python logging Effort: Low (1-2 hours) Benefits: Immediate colored output and consistent formatting

Before (Standard Python Logging)¤

import logging

# Old setup
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] - %(name)s - %(message)s'
)

logger = logging.getLogger(__name__)

class MyService:
    def __init__(self):
        self.logger = logging.getLogger(self.__class__.__name__)

    def process(self):
        self.logger.info("Processing started")
        self.logger.debug("Processing details")
        self.logger.info("Processing completed")

After (PANTHER Drop-in)¤

from panther.core.utils import LoggerFactory

# New setup - minimal changes
LoggerFactory.initialize({
    "level": "INFO",
    "format": "%(asctime)s [%(levelname)s] - %(name)s - %(message)s",
    "enable_colors": True  # New benefit!
})

# LoggerFactory patches logging.getLogger automatically
logger = logging.getLogger(__name__)  # Works as before!

class MyService:
    def __init__(self):
        # This now gets colored output and consistent formatting
        self.logger = logging.getLogger(self.__class__.__name__)

    def process(self):
        self.logger.info("Processing started")
        self.logger.debug("Processing details")
        self.logger.info("Processing completed")

Migration Steps: 1. Add LoggerFactory.initialize() call at application startup 2. Replace logging.basicConfig() with LoggerFactory.initialize() 3. Existing logging.getLogger() calls work unchanged 4. Test - you should see colored output immediately

Use case: Want to leverage feature-aware logging over time Effort: Medium (4-8 hours) Benefits: Feature-specific log levels, auto-detection, runtime configuration

Phase 1: Initialize with Feature Levels¤

from panther.core.utils import LoggerFactory

# Enhanced setup with feature-specific levels
LoggerFactory.initialize({
    "level": "INFO",
    "format": "%(asctime)s [%(levelname)s] - %(name)s - %(message)s",
    "enable_colors": True,
    "output_file": "/var/log/myapp.log"
})

# Define feature-specific levels
LoggerFactory.update_all_feature_levels({
    "database": "DEBUG",      # Database operations need detail
    "api": "INFO",            # API calls standard level
    "background": "WARNING",  # Background jobs only warnings
    "security": "DEBUG"       # Security events need detail
})

# Existing code works unchanged
logger = logging.getLogger(__name__)
logger.info("Application starting")  # Uses auto-detected feature level

Phase 2: Convert High-Value Components¤

from panther.core.utils import FeatureLoggerMixin

# Convert key components to use FeatureLoggerMixin
class DatabaseManager(FeatureLoggerMixin):  # Added mixin
    def __init__(self):
        self.__init_logger__()  # Added initialization
        # self.logger = logging.getLogger(...) <- Remove old logger setup

    def connect(self):
        # Old: self.logger.debug("Connecting to database")
        self.debug("Connecting to database")  # New: direct method

    def query(self, sql):
        # Old: self.logger.info(f"Executing: {sql}")
        self.info(f"Executing: {sql}")  # New: direct method

# Keep existing components unchanged during gradual migration
class LegacyService:
    def __init__(self):
        self.logger = logging.getLogger(self.__class__.__name__)  # Unchanged

    def process(self):
        self.logger.info("Legacy processing")  # Still works

Phase 3: Full Migration¤

from panther.core.utils import LoggerFactory, FeatureLoggerMixin

# All components use FeatureLoggerMixin
class APIHandler(FeatureLoggerMixin):
    def __init__(self):
        self.__init_logger__()
        self.info(f"API handler using feature: {self.get_effective_feature()}")

class SecurityValidator(FeatureLoggerMixin):
    def __init__(self):
        # Explicit feature assignment for components that don't auto-detect well
        self.__init_logger__(feature="security")

class BackgroundWorker(FeatureLoggerMixin):
    def __init__(self):
        self.__init_logger__()  # Auto-detects as "background" from class name

Strategy 3: Green Field Migration (New Components)¤

Use case: Building new features with PANTHER from the start Effort: Low (30 minutes per component) Benefits: Full feature set, optimal architecture

from panther.core.utils import LoggerFactory, FeatureLoggerMixin, feature_registry

# Application setup
LoggerFactory.initialize({
    "level": "INFO",
    "format": "%(asctime)s [%(levelname)s] - %(name)s - %(message)s",
    "enable_colors": True,
    "output_file": "/var/log/myapp.log"
})

# Register application-specific features
feature_registry.register_feature("order_processing", [
    "order", "checkout", "payment", "fulfillment"
])

feature_registry.register_feature("inventory_management", [
    "inventory", "stock", "warehouse", "supply"
])

# Set feature levels
LoggerFactory.update_all_feature_levels({
    "order_processing": "INFO",
    "inventory_management": "DEBUG",
    "unknown": "WARNING"
})

# Enable monitoring for new application
LoggerFactory.enable_statistics({
    "enabled": True,
    "track_performance": True
})

# New components built with PANTHER patterns
class OrderProcessor(FeatureLoggerMixin):
    def __init__(self):
        self.__init_logger__()  # Auto-detects order_processing
        self.info("Order processor initialized")

    def process_order(self, order_id):
        self.info(f"Processing order {order_id}")
        self.debug("Validating order details")  # Won't appear (INFO level)
        self.info("Order processed successfully")

class InventoryManager(FeatureLoggerMixin):
    def __init__(self):
        self.__init_logger__()  # Auto-detects inventory_management
        self.info("Inventory manager initialized")

    def update_stock(self, item_id, quantity):
        self.debug(f"Updating stock for {item_id}")  # Will appear (DEBUG level)
        self.info(f"Stock updated: {item_id} = {quantity}")

Common Migration Patterns¤

Pattern 1: Module-Level Logger Replacement¤

Before¤

# module.py
import logging

logger = logging.getLogger(__name__)

def process_data():
    logger.info("Processing data")

def validate_input():
    logger.debug("Validating input")

After (Option A: Minimal Change)¤

# module.py
from panther.core.utils import LoggerFactory

# Initialize somewhere in your application startup
logger = LoggerFactory.get_logger(__name__)

def process_data():
    logger.info("Processing data")

def validate_input():
    logger.debug("Validating input")

After (Option B: Feature-Aware)¤

# module.py
from panther.core.utils import LoggerFactory

# Get feature-specific logger
logger = LoggerFactory.get_feature_logger(__name__, "data_processing")

def process_data():
    logger.info("Processing data")

def validate_input():
    logger.debug("Validating input")

Pattern 2: Class-Based Logger Replacement¤

Before¤

class DataProcessor:
    def __init__(self):
        self.logger = logging.getLogger(self.__class__.__name__)

    def process(self):
        self.logger.info("Processing started")
        try:
            self._do_work()
            self.logger.info("Processing completed")
        except Exception as e:
            self.logger.error(f"Processing failed: {e}")

    def _do_work(self):
        self.logger.debug("Internal processing")

After¤

class DataProcessor(FeatureLoggerMixin):
    def __init__(self):
        self.__init_logger__()  # Replaces logger setup

    def process(self):
        self.info("Processing started")  # Direct method call
        try:
            self._do_work()
            self.info("Processing completed")
        except Exception as e:
            self.error(f"Processing failed: {e}", exc_info=True)  # Enhanced error logging

    def _do_work(self):
        self.debug("Internal processing")

Pattern 3: Configuration Migration¤

Before (logging.ini or dictConfig)¤

[loggers]
keys=root,myapp

[handlers]
keys=consoleHandler,fileHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_myapp]
level=INFO
handlers=consoleHandler,fileHandler
qualname=myapp
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[handler_fileHandler]
class=FileHandler
level=INFO
formatter=simpleFormatter
args=('/var/log/myapp.log',)

[formatter_simpleFormatter]
format=%(asctime)s [%(levelname)s] - %(name)s - %(message)s

After (Python Configuration)¤

from panther.core.utils import LoggerFactory

# Equivalent PANTHER configuration
LoggerFactory.initialize({
    "level": "INFO",  # Console level
    "format": "%(asctime)s [%(levelname)s] - %(name)s - %(message)s",
    "enable_colors": True,  # Added benefit
    "output_file": "/var/log/myapp.log",
    "debug_file_logging": True  # File gets DEBUG, console gets INFO
})

# Feature-specific levels (new capability)
LoggerFactory.update_all_feature_levels({
    "database": "DEBUG",
    "api": "INFO",
    "background": "WARNING"
})

Troubleshooting Migration Issues¤

Issue 1: Existing Loggers Not Using New Configuration¤

Problem: Loggers created before LoggerFactory.initialize() don't get new formatting.

Solution: Initialize LoggerFactory early in application startup, before creating any loggers.

# main.py or __init__.py
from panther.core.utils import LoggerFactory

# Initialize FIRST, before any other imports that create loggers
LoggerFactory.initialize({...})

# Now import modules that create loggers
from .services import MyService
from .handlers import APIHandler

Issue 2: Third-Party Libraries Not Using PANTHER Formatting¤

Problem: Third-party libraries still use default logging format.

Solution: PANTHER automatically patches logging.getLogger(), so this should work. If not:

# Force patch application
LoggerFactory.initialize({...})

# Get specific third-party logger and it will use PANTHER formatting
import requests
requests_logger = logging.getLogger("urllib3")  # Now uses PANTHER format

Issue 3: Performance Degradation After Migration¤

Problem: Application runs slower after enabling statistics.

Solution: Disable or optimize statistics collection:

# Disable statistics for production
LoggerFactory.disable_statistics()

# Or use buffered collection for better performance
LoggerFactory.enable_statistics({
    "enabled": True,
    "handler_type": "buffered",  # Better performance
    "buffer_size": 100,          # Smaller buffer
    "track_performance": False   # Disable expensive tracking
})

Issue 4: Feature Detection Not Working¤

Problem: Components not getting expected feature assignment.

Solution: Check and adjust feature detection:

from panther.core.utils import LoggerFactory

# Debug feature detection
component_name = "MyComponent"
detected = LoggerFactory._detect_feature_from_name(component_name)
print(f"'{component_name}' detected as: {detected}")

# Register custom patterns if needed
from panther.core.utils import feature_registry
feature_registry.register_feature("my_feature", ["MyComponent", "pattern"])

# Or use explicit assignment
class MyComponent(FeatureLoggerMixin):
    def __init__(self):
        self.__init_logger__(feature="explicit_feature")

Issue 5: File Logging Not Working¤

Problem: Log messages not appearing in specified file.

Solution: Check file permissions and paths:

import os
from pathlib import Path

log_file = Path("/var/log/myapp.log")

# Ensure directory exists
log_file.parent.mkdir(parents=True, exist_ok=True)

# Check permissions
if not os.access(log_file.parent, os.W_OK):
    print(f"No write permission to {log_file.parent}")
    # Use alternative location
    log_file = Path("/tmp/myapp.log")

LoggerFactory.initialize({
    "output_file": str(log_file),
    # ... other config
})

Migration Testing¤

Test 1: Verify Basic Functionality¤

def test_basic_migration():
    """Test that basic logging still works after migration."""
    from panther.core.utils import LoggerFactory

    LoggerFactory.initialize({"level": "DEBUG", "enable_colors": True})

    # Test standard logging still works
    import logging
    logger = logging.getLogger("test")

    logger.debug("Debug message")
    logger.info("Info message")
    logger.warning("Warning message")
    logger.error("Error message")

    print("✓ Basic logging functionality verified")

test_basic_migration()

Test 2: Verify Feature Detection¤

def test_feature_detection():
    """Test that feature detection works for your components."""
    from panther.core.utils import LoggerFactory, FeatureLoggerMixin

    LoggerFactory.initialize({"level": "DEBUG"})
    LoggerFactory.update_all_feature_levels({
        "database": "DEBUG",
        "api": "INFO"
    })

    class DatabaseService(FeatureLoggerMixin):
        def __init__(self):
            self.__init_logger__()
            feature = self.get_effective_feature()
            assert feature == "database", f"Expected 'database', got '{feature}'"
            self.info("Database service feature detection verified")

    class APIHandler(FeatureLoggerMixin):
        def __init__(self):
            self.__init_logger__()
            feature = self.get_effective_feature()
            print(f"API handler detected as feature: {feature}")

    db = DatabaseService()
    api = APIHandler()

    print("✓ Feature detection verified")

test_feature_detection()

Test 3: Performance Baseline¤

def test_performance_impact():
    """Measure performance impact of migration."""
    import time
    from panther.core.utils import LoggerFactory

    # Test with standard logging
    import logging
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger("perf_test")

    start = time.time()
    for i in range(1000):
        logger.info(f"Standard logging message {i}")
    standard_time = time.time() - start

    # Test with PANTHER
    LoggerFactory.initialize({"level": "INFO", "enable_colors": True})
    panther_logger = LoggerFactory.get_logger("perf_test_panther")

    start = time.time()
    for i in range(1000):
        panther_logger.info(f"PANTHER logging message {i}")
    panther_time = time.time() - start

    overhead = ((panther_time - standard_time) / standard_time) * 100
    print(f"Standard logging: {standard_time:.3f}s")
    print(f"PANTHER logging: {panther_time:.3f}s")
    print(f"Overhead: {overhead:.1f}%")

    assert overhead < 20, f"Performance overhead too high: {overhead:.1f}%"
    print("✓ Performance impact acceptable")

test_performance_impact()

Best Practices for Migration¤

1. Start Small¤

  • Begin with a single module or component
  • Verify functionality before expanding
  • Keep old and new logging side by side initially

2. Plan Feature Categories¤

  • Map your application's components to logical features
  • Start with obvious categories (database, api, background)
  • Refine based on actual usage patterns

3. Gradual Rollout¤

  • Week 1: Initialize LoggerFactory with basic config
  • Week 2: Add feature-specific levels
  • Week 3: Convert high-traffic components to FeatureLoggerMixin
  • Week 4: Enable statistics and monitoring

4. Monitor and Tune¤

  • Watch for performance impact
  • Adjust feature levels based on debugging needs
  • Use statistics to understand logging patterns

5. Document Changes¤

  • Update deployment documentation
  • Train team on new logging capabilities
  • Document feature categories and conventions

The migration to PANTHER's feature-aware logging is designed to be incremental and low-risk. Start with the minimal changes and gradually adopt more features as you become comfortable with the system.