Diagnostics - InjectQ Documentation
Author: InjectQ Contributors
Description: Modern Python dependency injection library

Skip to content

# Diagnostics

Diagnostics provide comprehensive tools for debugging, monitoring, and troubleshooting dependency injection issues in your InjectQ applications.

## 🔍 Diagnostic Tools

### Container Inspection

    from injectq import InjectQ
    from injectq.diagnostics import ContainerInspector

    # Create container with diagnostics
    container = InjectQ()
    inspector = ContainerInspector(container)

    # Inspect container state
    container_info = inspector.inspect_container()
    print("Container Information:")
    print(f"- Total bindings: {container_info['total_bindings']}")
    print(f"- Active scopes: {container_info['active_scopes']}")
    print(f"- Memory usage: {container_info['memory_usage']} bytes")

    # Inspect specific bindings
    binding_info = inspector.inspect_binding(SomeService)
    print(f"Binding for SomeService: {binding_info}")

    # List all bindings
    all_bindings = inspector.list_bindings()
    for binding_type, binding_info in all_bindings.items():
        print(f"{binding_type.__name__}: {binding_info['scope']} scope")

### Dependency Graph Analysis

    from injectq.diagnostics import DependencyGraphAnalyzer

    analyzer = DependencyGraphAnalyzer(container)

    # Analyze dependency graph
    graph_info = analyzer.analyze_graph()
    print("Dependency Graph Analysis:")
    print(f"- Total nodes: {graph_info['total_nodes']}")
    print(f"- Total edges: {graph_info['total_edges']}")
    print(f"- Circular dependencies: {graph_info['circular_dependencies']}")

    # Find dependency chains
    chains = analyzer.find_dependency_chains(SomeService)
    for chain in chains:
        print(f"Dependency chain: {' -> '.join(cls.__name__ for cls in chain)}")

    # Detect circular dependencies
    circular_deps = analyzer.detect_circular_dependencies()
    if circular_deps:
        print("Circular dependencies found:")
        for dep in circular_deps:
            print(f"- {' -> '.join(cls.__name__ for cls in dep)}")

### Performance Diagnostics

    from injectq.diagnostics import PerformanceMonitor

    monitor = PerformanceMonitor(container)

    # Monitor resolution performance
    with monitor.monitor_resolution():
        service = container.get(SomeService)

    # Get performance report
    performance_report = monitor.get_performance_report()
    print("Performance Report:")
    print(f"- Total resolutions: {performance_report['total_resolutions']}")
    print(f"- Average resolution time: {performance_report['avg_resolution_time']}ms")
    print(f"- Slowest resolution: {performance_report['slowest_resolution']}ms")

    # Monitor memory usage
    memory_report = monitor.get_memory_report()
    print("Memory Report:")
    print(f"- Peak memory usage: {memory_report['peak_memory']} bytes")
    print(f"- Current memory usage: {memory_report['current_memory']} bytes")

## 🐛 Debugging Tools

### Resolution Tracing

    from injectq.diagnostics import ResolutionTracer

    tracer = ResolutionTracer(container)

    # Trace dependency resolution
    with tracer.trace_resolution(SomeService) as trace:
        service = container.get(SomeService)

    # Print resolution trace
    print("Resolution Trace:")
    for step in trace.steps:
        print(f"  {step['step']}: {step['description']}")
        if 'duration' in step:
            print(f"    Duration: {step['duration']}ms")

    # Trace with detailed output
    tracer.enable_detailed_tracing()
    with tracer.trace_resolution(ComplexService) as trace:
        complex_service = container.get(ComplexService)

    tracer.print_trace(trace)

### Error Diagnostics

    from injectq.diagnostics import ErrorAnalyzer

    analyzer = ErrorAnalyzer(container)

    # Analyze resolution errors
    try:
        service = container.get(SomeService)
    except Exception as e:
        error_analysis = analyzer.analyze_error(e)
        print("Error Analysis:")
        print(f"- Error type: {error_analysis['error_type']}")
        print(f"- Root cause: {error_analysis['root_cause']}")
        print(f"- Suggested fixes: {error_analysis['suggested_fixes']}")

        # Print detailed error context
        analyzer.print_error_context(error_analysis)

### Scope Debugging

    from injectq.diagnostics import ScopeDebugger

    debugger = ScopeDebugger(container)

    # Debug scope issues
    scope_info = debugger.debug_scopes()
    print("Scope Debug Information:")
    for scope_name, scope_data in scope_info.items():
        print(f"- {scope_name}: {scope_data['instance_count']} instances")
        print(f"  Active instances: {len(scope_data['active_instances'])}")

    # Check for scope leaks
    leaks = debugger.detect_scope_leaks()
    if leaks:
        print("Scope leaks detected:")
        for leak in leaks:
            print(f"- {leak['scope']}: {leak['leaked_instances']} instances")

    # Debug singleton scope
    singleton_debug = debugger.debug_singleton_scope()
    print("Singleton Debug:")
    for binding_type, info in singleton_debug.items():
        print(f"- {binding_type.__name__}: {info['instance_count']} instances")

## 📊 Monitoring and Metrics

### Container Metrics

    from injectq.diagnostics import ContainerMetrics

    metrics = ContainerMetrics(container)

    # Collect metrics
    container_metrics = metrics.collect_metrics()
    print("Container Metrics:")
    print(f"- Bindings count: {container_metrics['bindings_count']}")
    print(f"- Resolutions count: {container_metrics['resolutions_count']}")
    print(f"- Cache hit rate: {container_metrics['cache_hit_rate']}%")
    print(f"- Memory usage: {container_metrics['memory_usage']} bytes")

    # Monitor over time
    metrics.start_monitoring(interval_seconds=60)

    # Later...
    time_series_data = metrics.get_time_series_data()
    for timestamp, data in time_series_data.items():
        print(f"{timestamp}: {data['resolutions_per_minute']} resolutions/min")

### Performance Profiling

    from injectq.diagnostics import PerformanceProfiler

    profiler = PerformanceProfiler(container)

    # Profile dependency resolution
    with profiler.profile_resolution(SomeService) as profile:
        service = container.get(SomeService)

    # Analyze profile
    profile_analysis = profiler.analyze_profile(profile)
    print("Profile Analysis:")
    print(f"- Total time: {profile_analysis['total_time']}ms")
    print(f"- Slowest dependency: {profile_analysis['slowest_dependency']}")
    print(f"- Bottlenecks: {profile_analysis['bottlenecks']}")

    # Profile memory usage
    memory_profile = profiler.profile_memory_usage()
    print("Memory Profile:")
    for binding_type, usage in memory_profile.items():
        print(f"- {binding_type.__name__}: {usage['memory_usage']} bytes")

### Health Checks

    from injectq.diagnostics import HealthChecker

    health_checker = HealthChecker(container)

    # Perform health check
    health_status = await health_checker.check_health()
    print("Health Status:")
    print(f"- Overall health: {health_status['overall_health']}")
    print(f"- Issues found: {len(health_status['issues'])}")

    for issue in health_status['issues']:
        print(f"- {issue['severity']}: {issue['description']}")
        if 'suggestion' in issue:
            print(f"  Suggestion: {issue['suggestion']}")

    # Check specific components
    binding_health = health_checker.check_binding_health(SomeService)
    print(f"Binding health for SomeService: {binding_health}")

    scope_health = health_checker.check_scope_health()
    print(f"Scope health: {scope_health}")

## 🔧 Diagnostic Commands

### Command Line Diagnostics

    # Inspect container state
    injectq inspect container

    # Analyze dependency graph
    injectq analyze graph --output graph.dot

    # Check for circular dependencies
    injectq check circular

    # Monitor performance
    injectq monitor performance --duration 60

    # Generate diagnostic report
    injectq report --output diagnostics.json

### Programmatic Diagnostics

    from injectq.diagnostics import DiagnosticRunner

    runner = DiagnosticRunner(container)

    # Run all diagnostics
    diagnostic_report = runner.run_all_diagnostics()
    print("Diagnostic Report:")
    for check_name, result in diagnostic_report.items():
        print(f"- {check_name}: {result['status']}")
        if result['status'] == 'failed':
            print(f"  Error: {result['error']}")

    # Run specific diagnostic
    circular_check = runner.run_diagnostic('circular_dependencies')
    if circular_check['status'] == 'failed':
        print("Circular dependencies found:")
        for dep in circular_check['details']:
            print(f"- {' -> '.join(cls.__name__ for cls in dep)}")

    # Export diagnostics
    runner.export_diagnostics('diagnostics_report.json')

## 🎯 Common Diagnostic Scenarios

### Debugging Resolution Failures

    # Scenario: Service resolution fails
    try:
        service = container.get(SomeService)
    except Exception as e:
        # Use error analyzer
        analyzer = ErrorAnalyzer(container)
        analysis = analyzer.analyze_error(e)

        print("Resolution Failure Analysis:")
        print(f"Error: {analysis['error']}")
        print(f"Root cause: {analysis['root_cause']}")

        # Check if binding exists
        if 'missing_binding' in analysis:
            print(f"Missing binding for: {analysis['missing_binding']}")
            print("Suggestion: Add binding with container.bind()")

        # Check for circular dependencies
        if 'circular_dependency' in analysis:
            print("Circular dependency detected in chain:")
            for cls in analysis['circular_dependency']:
                print(f"- {cls.__name__}")

        # Get suggested fixes
        for fix in analysis['suggested_fixes']:
            print(f"Suggestion: {fix}")

### Performance Issues

    # Scenario: Slow dependency resolution
    monitor = PerformanceMonitor(container)

    # Profile the resolution
    with monitor.monitor_resolution() as monitoring:
        service = container.get(SomeService)

    performance_data = monitoring.get_data()

    if performance_data['total_time'] > 1000:  # More than 1 second
        print("Performance issue detected!")

        # Find bottlenecks
        for step in performance_data['steps']:
            if step['duration'] > 500:  # More than 500ms
                print(f"Bottleneck: {step['description']} ({step['duration']}ms)")

        # Check for optimization opportunities
        if performance_data['cache_misses'] > performance_data['cache_hits']:
            print("Suggestion: Consider using singleton scope for frequently used dependencies")

        if performance_data['memory_usage'] > 100 * 1024 * 1024:  # 100MB
            print("Suggestion: Check for memory leaks in dependencies")

### Memory Leaks

    # Scenario: Suspected memory leak
    from injectq.diagnostics import MemoryLeakDetector

    detector = MemoryLeakDetector(container)

    # Monitor memory usage over time
    detector.start_monitoring()

    # Perform operations that might leak
    for i in range(1000):
        service = container.get(SomeService)
        # Use service...

    # Check for leaks
    leak_report = detector.check_for_leaks()
    print("Memory Leak Analysis:")
    print(f"- Memory growth: {leak_report['memory_growth']} bytes")
    print(f"- Potential leaks: {len(leak_report['potential_leaks'])}")

    for leak in leak_report['potential_leaks']:
        print(f"- {leak['type'].__name__}: {leak['instances']} instances")

    # Get recommendations
    recommendations = detector.get_recommendations()
    for rec in recommendations:
        print(f"Recommendation: {rec}")

### Scope Issues

    # Scenario: Scope-related problems
    debugger = ScopeDebugger(container)

    # Check scope state
    scope_status = debugger.get_scope_status()
    print("Scope Status:")
    for scope_name, status in scope_status.items():
        print(f"- {scope_name}: {status['active_instances']} active, {status['total_created']} total")

    # Detect scope leaks
    leaks = debugger.detect_scope_leaks()
    if leaks:
        print("Scope Leaks Detected:")
        for leak in leaks:
            print(f"- {leak['scope']}: {leak['count']} leaked instances")
            print(f"  Instances: {[str(inst) for inst in leak['instances']]}")

    # Check for scope conflicts
    conflicts = debugger.detect_scope_conflicts()
    if conflicts:
        print("Scope Conflicts Detected:")
        for conflict in conflicts:
            print(f"- {conflict['binding']}: requested in {conflict['requested_scope']}, bound in {conflict['bound_scope']}")

## 📋 Diagnostic Best Practices

### ✅ Good Diagnostic Practices

#### 1. Regular Health Monitoring

    class ApplicationMonitor:
        """Monitor application health continuously."""

        def __init__(self, container: InjectQ):
            self.container = container
            self.health_checker = HealthChecker(container)
            self.performance_monitor = PerformanceMonitor(container)
            self.last_check = None

        async def run_health_check(self):
            """Run comprehensive health check."""
            health_status = await self.health_checker.check_health()

            if health_status['overall_health'] != 'healthy':
                await self.handle_health_issues(health_status['issues'])

            self.last_check = datetime.now()
            return health_status

        async def monitor_performance(self):
            """Monitor performance metrics."""
            performance_data = self.performance_monitor.get_performance_report()

            # Check for performance degradation
            if performance_data['avg_resolution_time'] > 100:  # 100ms threshold
                await self.handle_performance_issue(performance_data)

            return performance_data

        async def handle_health_issues(self, issues):
            """Handle health check failures."""
            for issue in issues:
                if issue['severity'] == 'critical':
                    # Log critical issues
                    logger.critical(f"Critical health issue: {issue['description']}")
                    # Send alerts
                    await self.send_alert(issue)
                elif issue['severity'] == 'warning':
                    logger.warning(f"Health warning: {issue['description']}")

        async def send_alert(self, issue):
            """Send alert for critical issues."""
            # Implementation depends on your alerting system
            pass

    # Usage
    monitor = ApplicationMonitor(container)

    # Run periodic checks
    async def monitoring_loop():
        while True:
            await monitor.run_health_check()
            await monitor.monitor_performance()
            await asyncio.sleep(300)  # Check every 5 minutes

#### 2. Structured Logging

    import logging
    from injectq.diagnostics import DiagnosticLogger

    # Configure diagnostic logging
    diagnostic_logger = DiagnosticLogger(container)
    diagnostic_logger.configure_logging(level=logging.DEBUG)

    # Log diagnostic events
    with diagnostic_logger.log_resolution(SomeService):
        service = container.get(SomeService)

    # Log errors with context
    try:
        complex_service = container.get(ComplexService)
    except Exception as e:
        diagnostic_logger.log_error_with_context(e, {
            'operation': 'service_resolution',
            'service_type': 'ComplexService',
            'container_state': container.get_state_summary()
        })

    # Custom diagnostic logging
    class CustomDiagnosticLogger:
        def __init__(self, container):
            self.container = container
            self.logger = logging.getLogger('injectq.diagnostics')

        def log_resolution_start(self, service_type):
            self.logger.info(f"Starting resolution of {service_type.__name__}")

        def log_resolution_success(self, service_type, duration):
            self.logger.info(f"Successfully resolved {service_type.__name__} in {duration}ms")

        def log_resolution_failure(self, service_type, error):
            self.logger.error(f"Failed to resolve {service_type.__name__}: {error}")

    # Usage
    custom_logger = CustomDiagnosticLogger(container)
    custom_logger.log_resolution_start(SomeService)
    try:
        service = container.get(SomeService)
        custom_logger.log_resolution_success(SomeService, 150)
    except Exception as e:
        custom_logger.log_resolution_failure(SomeService, e)

#### 3. Diagnostic Dashboards

    from injectq.diagnostics import DiagnosticDashboard

    # Create diagnostic dashboard
    dashboard = DiagnosticDashboard(container)

    # Generate HTML dashboard
    html_report = dashboard.generate_html_report()
    with open('diagnostics_dashboard.html', 'w') as f:
        f.write(html_report)

    # Generate JSON report for monitoring systems
    json_report = dashboard.generate_json_report()
    with open('diagnostics_report.json', 'w') as f:
        f.write(json.dumps(json_report, indent=2))

    # Real-time monitoring dashboard
    class RealTimeDashboard:
        def __init__(self, container):
            self.container = container
            self.metrics = ContainerMetrics(container)
            self.last_update = None

        def update_dashboard(self):
            """Update dashboard with latest metrics."""
            current_metrics = self.metrics.collect_metrics()

            # Update display
            self.display_metrics(current_metrics)

            # Check for alerts
            self.check_alerts(current_metrics)

            self.last_update = datetime.now()

        def display_metrics(self, metrics):
            """Display metrics in console or web interface."""
            print("
    === InjectQ Diagnostic Dashboard ===")
            print(f"Timestamp: {datetime.now()}")
            print(f"Bindings: {metrics['bindings_count']}")
            print(f"Resolutions: {metrics['resolutions_count']}")
            print(f"Memory Usage: {metrics['memory_usage']} bytes")
            print(f"Cache Hit Rate: {metrics['cache_hit_rate']}%")
            print("=" * 40)

        def check_alerts(self, metrics):
            """Check for alert conditions."""
            if metrics['memory_usage'] > 500 * 1024 * 1024:  # 500MB
                print("⚠️  High memory usage detected!")

            if metrics['cache_hit_rate'] < 50:
                print("⚠️  Low cache hit rate detected!")

    # Usage
    rt_dashboard = RealTimeDashboard(container)

    # Update every 30 seconds
    def monitoring_loop():
        while True:
            rt_dashboard.update_dashboard()
            time.sleep(30)

### ❌ Common Diagnostic Mistakes

#### 1. Over-Diagnostics

    # ❌ Bad: Too much diagnostic overhead
    class OverDiagnosticContainer:
        def __init__(self):
            self.container = InjectQ()
            # Too many monitors
            self.performance_monitor = PerformanceMonitor(self.container)
            self.memory_monitor = MemoryLeakDetector(self.container)
            self.scope_monitor = ScopeDebugger(self.container)
            self.health_monitor = HealthChecker(self.container)
            # And more...

        def get_service(self, service_type):
            # Every resolution triggers all diagnostics
            with self.performance_monitor.monitor_resolution():
                with self.memory_monitor.monitor_memory():
                    with self.scope_monitor.debug_scope():
                        return self.container.get(service_type)

    # ✅ Good: Selective diagnostics
    class SelectiveDiagnostics:
        def __init__(self, enable_diagnostics=False):
            self.container = InjectQ()
            self.enable_diagnostics = enable_diagnostics

            if enable_diagnostics:
                self.monitor = PerformanceMonitor(self.container)

        def get_service(self, service_type):
            if self.enable_diagnostics:
                with self.monitor.monitor_resolution():
                    return self.container.get(service_type)
            else:
                return self.container.get(service_type)

#### 2. Ignoring Diagnostic Data

    # ❌ Bad: Collecting but not using diagnostics
    monitor = PerformanceMonitor(container)

    # Collect data but never analyze it
    for i in range(1000):
        with monitor.monitor_resolution():
            service = container.get(SomeService)

    # Data collected but never used
    performance_data = monitor.get_performance_report()
    # Just print it and forget
    print(performance_data)

    # ✅ Good: Actionable diagnostics
    class ActionableDiagnostics:
        def __init__(self, container):
            self.container = container
            self.monitor = PerformanceMonitor(container)
            self.baseline_performance = None

        def establish_baseline(self):
            """Establish performance baseline."""
            # Run several resolutions to establish baseline
            times = []
            for _ in range(10):
                with self.monitor.monitor_resolution() as m:
                    self.container.get(SomeService)
                times.append(m.get_data()['total_time'])

            self.baseline_performance = sum(times) / len(times)

        def monitor_and_act(self):
            """Monitor and take action if needed."""
            with self.monitor.monitor_resolution() as m:
                service = self.container.get(SomeService)

            current_time = m.get_data()['total_time']

            if current_time > self.baseline_performance * 2:  # 2x slower
                self.handle_performance_degradation(current_time)

        def handle_performance_degradation(self, current_time):
            """Handle performance issues."""
            print(f"Performance degradation detected: {current_time}ms (baseline: {self.baseline_performance}ms)")

            # Take action: clear caches, restart services, etc.
            self.container.clear_caches()
            print("Caches cleared to improve performance")

## 🎯 Summary

Diagnostics provide comprehensive tools for monitoring and debugging:

- Container inspection - Examine container state and bindings
- Dependency graph analysis - Find circular dependencies and optimization opportunities
- Performance monitoring - Track resolution times and memory usage
- Resolution tracing - Debug dependency resolution issues
- Error analysis - Understand and fix resolution failures
- Scope debugging - Detect scope leaks and conflicts
- Health checks - Monitor overall system health

Key features: - Real-time monitoring and alerting - Performance profiling and optimization - Memory leak detection - Structured logging and reporting - Command-line diagnostic tools - Programmatic diagnostic APIs

Best practices: - Regular health monitoring - Structured diagnostic logging - Actionable diagnostic data - Selective diagnostic overhead - Real-time dashboards and alerts

Common scenarios: - Debugging resolution failures - Performance issue diagnosis - Memory leak detection - Scope problem identification - Health monitoring and alerting

Ready to explore performance optimization?