Logging
FastIoC provides comprehensive logging to help you understand what's happening inside your dependency injection container. The logging system tracks container initialization, dependency registration, resolution, disposal, and potential issues.
Enabling Logs
FastIoC uses Python's standard logging module with the logger name "FastIoC". To enable and configure logging:
import logging
# Get the FastIoC logger
logger = logging.getLogger("FastIoC")
# Set the logging level
logger.setLevel(logging.DEBUG) # Show all logs
# Optional: Add a custom handler if needed
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("%(name)s: %(levelname)s: %(message)s"))
logger.addHandler(handler)
Log Levels
FastIoC uses different log levels for different types of events:
DEBUG - Application Events
The DEBUG level logs normal application events and operations. This is useful for understanding the flow of dependency registration and resolution.
Events logged at DEBUG level:
-
Container initialization
FastIoC: DEBUG: IoC/DI Container initialized ... -
Dependency registration
FastIoC: DEBUG: Dependency "UserService" registered with "SCOPED" lifetime for protocol: "IUserService" -
Singleton initialization
FastIoC: DEBUG: Singleton dependency "DatabaseService" initialized -
Disposal registration
FastIoC: DEBUG: Disposal registered for dependency "DatabaseService" -
Dependency resolution
FastIoC: DEBUG: Resolve called for protocol "IUserService" -
Nested dependency resolution
FastIoC: DEBUG: Resolved Protocol "IDatabaseService" as nested dependency for "UserService" FastIoC: DEBUG: Resolved Protocol "IEmailService" as nested dependency for "UserService" (from class annotations) -
FastAPI built-in resolution
FastIoC: DEBUG: Resolved FastAPI built-in "Request" as nested dependency for "MyService" -
Hook execution
FastIoC: DEBUG: Registration hook executed for dependency "IUserService->UserService@SCOPED" FastIoC: DEBUG: Resolve hook executed for protocol "IUserService" -
Dependency override
FastIoC: DEBUG: Registered dependency "IUserService" overridden FastIoC: DEBUG: Registered dependency "IEmailService" overridden by Mock Container -
Disposal execution
FastIoC: DEBUG: Dependency "DatabaseService" disposed -
FastAPI integration
FastIoC: DEBUG: FastAPI application injectified FastIoC: DEBUG: FastAPI router injectified
INFO - Skipped Dependencies
The INFO level logs when FastIoC skips resolving a dependency because it's not registered in the container (and it's not a built-in type, FastAPI special type, or Pydantic model).
Events logged at INFO level:
-
Unregistered protocol skipped
FastIoC: INFO: Skipped protocol "ICustomService": No registered dependency found -
Nested unregistered protocol skipped
FastIoC: INFO: (Nested)Skipped protocol "IHelperService": No registered dependency found
This helps you identify dependencies that might be missing from your container configuration.
WARNING - Potential Issues
The WARNING level logs situations that might indicate configuration problems or unexpected behavior, but don't prevent the application from running.
Events logged at WARNING level:
-
Scoped dependency depends on transient
This warns you that a scoped dependency depends on a transient one, which might not be what you intended (the transient will be recreated each time).FastIoC: WARNING: Request-scoped dependency "UserService" depends on transient dependency "HelperService" -
Built-in type registered as protocol
This warns when you register a dependency for built-in Python types (likeFastIoC: WARNING: Dependency "MyService" registered for protocol "dict" which is a built-in, type, Pydantic model, FastAPI special class. This may override default behavior; make sure this is what you intendstr,int,dict), Pydantic models, or FastAPI special classes (likeRequest,Response). This can override FastAPI's default behavior.
EXCEPTION - Errors During Disposal
The EXCEPTION level logs errors that occur during the disposal process. These errors don't stop other dependencies from being disposed.
Events logged at EXCEPTION level:
- Disposal error
FastIoC: EXCEPTION: Error disposing "DatabaseService": Connection already closed
Configuration Examples
Basic Setup - Show All Logs
import logging
from fastioc import Container
# Configure logging
logging.basicConfig(
level=logging.DEBUG,
format="%(name)s: %(levelname)s: %(message)s"
)
# Create container - you'll see initialization log
container = Container()
Production Setup - Only Warnings and Errors
import logging
logger = logging.getLogger("FastIoC")
logger.setLevel(logging.WARNING) # Only show warnings and errors
Development Setup - Verbose Logging
import logging
# Get the FastIoC logger
logger = logging.getLogger("FastIoC")
logger.setLevel(logging.DEBUG)
# Create a detailed formatter
formatter = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
# Add console handler
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
# Optionally, log to file
file_handler = logging.FileHandler("fastioc.log")
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
Selective Logging - Different Levels per Handler
import logging
logger = logging.getLogger("FastIoC")
logger.setLevel(logging.DEBUG) # Capture all logs
# Console: Only warnings and errors
console = logging.StreamHandler()
console.setLevel(logging.WARNING)
console.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
logger.addHandler(console)
# File: Everything (DEBUG and above)
file_handler = logging.FileHandler("fastioc_debug.log")
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
logger.addHandler(file_handler)
Complete Example with Logging
Here's a complete example showing what logs you might see during typical operations:
import logging
from fastapi import FastAPI
from fastioc import Container
# Enable DEBUG logging
logging.basicConfig(
level=logging.DEBUG,
format="%(name)s: %(levelname)s: %(message)s"
)
# Create container
# LOG: FastIoC: DEBUG: IoC/DI Container initialized ...
container = Container()
# Define services
class IDatabaseService:
pass
class IEmailService:
pass
class DatabaseService(IDatabaseService):
def __init__(self):
self.connected = True
def __dispose__(self):
self.connected = False
class EmailService(IEmailService):
def __init__(self, db: IDatabaseService):
self.db = db
# Register dependencies
container.add_singleton(IDatabaseService, DatabaseService)
# LOG: FastIoC: DEBUG: Singleton dependency "DatabaseService" initialized
# LOG: FastIoC: DEBUG: Disposal registered for dependency "DatabaseService"
# LOG: FastIoC: DEBUG: Dependency "DatabaseService" registered with "SINGLETON" lifetime for protocol: "IDatabaseService"
container.add_scoped(IEmailService, EmailService)
# LOG: FastIoC: DEBUG: Resolved Protocol "IDatabaseService" as nested dependency for "EmailService"
# LOG: FastIoC: DEBUG: Dependency "EmailService" registered with "SCOPED" lifetime for protocol: "IEmailService"
# Create FastAPI app
app = FastAPI()
# Injectify
container.injectify(app)
# LOG: FastIoC: DEBUG: FastAPI application injectified
# Define endpoint
@app.get("/test")
async def test(email: IEmailService):
return {"status": "ok"}
# When endpoint is called:
# LOG: FastIoC: DEBUG: Resolve called for protocol "IEmailService"
# During shutdown:
# await container.dispose()
# LOG: FastIoC: DEBUG: Dependency "DatabaseService" disposed
Understanding Log Output
Tracking Dependency Resolution
When DEBUG logging is enabled, you can trace exactly how FastIoC resolves your dependencies:
FastIoC: DEBUG: Resolve called for protocol "IUserService"
FastIoC: DEBUG: Resolved Protocol "IDatabaseService" as nested dependency for "UserService"
FastIoC: DEBUG: Resolved Protocol "IAuthService" as nested dependency for "UserService"
FastIoC: DEBUG: Resolved Protocol "IEmailService" as nested dependency for "UserService"
This shows that UserService depends on three other services, and FastIoC resolved all of them.
Identifying Missing Dependencies
INFO logs help you spot dependencies that aren't registered:
FastIoC: INFO: Skipped protocol "IOptionalService": No registered dependency found
If you see this for a dependency you expected to be registered, check your container configuration.
Catching Configuration Issues
WARNING logs alert you to potential problems:
FastIoC: WARNING: Request-scoped dependency "UserService" depends on transient dependency "HelperService"
This might indicate that you should register HelperService as scoped instead of transient for better performance and consistency.
Disabling Logs
To completely disable FastIoC logs:
import logging
logger = logging.getLogger("FastIoC")
logger.setLevel(logging.CRITICAL) # Only critical errors (effectively disables logs)
# Or completely disable:
logger.disabled = True
Best Practices
-
Development: Use
DEBUGlevel to understand how dependencies are wiredlogger.setLevel(logging.DEBUG) -
Testing: Use
WARNINGlevel to catch configuration issues without noiselogger.setLevel(logging.WARNING) -
Production: Use
WARNINGorERRORlevel for minimal logginglogger.setLevel(logging.WARNING) -
Debugging Issues: Enable
DEBUGtemporarily and log to a filehandler = logging.FileHandler("fastioc_debug.log") handler.setLevel(logging.DEBUG) logger.addHandler(handler) -
CI/CD: Disable logs during automated tests to reduce noise
logger.disabled = True
Log Level Summary
| Level | Use Case | Example Events |
|---|---|---|
| DEBUG | Development, understanding flow | Container init, registration, resolution, disposal |
| INFO | Identifying skipped dependencies | Unregistered protocols |
| WARNING | Catching potential issues | Scoped -> Transient dependency, built-in type registration |
| EXCEPTION | Disposal errors | Failed cleanup operations |