Rich Formatting
Beautiful console output with colors and structure
Lifecycle Logging (lifecyclelogging) is a comprehensive logging utility combining Python’s standard logging with rich output formatting, verbosity controls, and message storage.
pip install lifecycleloggingRich Formatting
Beautiful console output with colors and structure
Verbosity Control
Threshold-based filtering with bypass markers
Message Storage
Store and retrieve messages programmatically
Gunicorn Ready
Automatic integration with WSGI servers
from lifecyclelogging import Logging
# Create a logger with console outputlogger = Logging(enable_console=True, enable_file=False)
# Log at different levelslogger.logged_statement("Debug message", log_level="debug")logger.logged_statement("Info message", log_level="info")logger.logged_statement("Warning message", log_level="warning")logger.logged_statement("Error message", log_level="error")from lifecyclelogging import Logging
logger = Logging(enable_console=True)
# Attach JSON data to log messagesuser_data = {"username": "john_doe", "email": "john@example.com"}logger.logged_statement( "User logged in", json_data=user_data, log_level="info",)
# Output includes pretty-printed JSON:# [INFO] User logged in# {# "username": "john_doe",# "email": "john@example.com"# }from lifecyclelogging import Logging
logger = Logging(enable_console=True)
# Multiple labeled data sectionslabeled_data = { "Request": {"method": "POST", "path": "/api/users"}, "Response": {"status": 201, "body": {"id": 123}},}
logger.logged_statement( "API request completed", labeled_json_data=labeled_data, log_level="info",)
# Output:# [INFO] API request completed# Request:# {"method": "POST", "path": "/api/users"}# Response:# {"status": 201, "body": {"id": 123}}from lifecyclelogging import Logging
logger = Logging( # Output destinations enable_console=True, # Enable console (stdout) output enable_file=False, # Enable file output log_file_path="app.log", # File path (if enable_file=True)
# Formatting logger_name="my_app", # Logger name prefix
# Verbosity enable_verbose_output=False, # Enable verbose message filtering verbosity_threshold=1, # Max verbosity level to show)from lifecyclelogging import Logging
# Console only (development)dev_logger = Logging( enable_console=True, enable_file=False,)
# File only (production)prod_logger = Logging( enable_console=False, enable_file=True, log_file_path="/var/log/myapp/app.log",)
# Both (debugging)debug_logger = Logging( enable_console=True, enable_file=True, log_file_path="debug.log",)Control which messages are shown based on verbosity levels:
from lifecyclelogging import Logging
logger = Logging( enable_console=True, enable_verbose_output=True, verbosity_threshold=2, # Only show messages with verbosity <= 2)
# This WILL be shown (verbosity 1 <= threshold 2)logger.logged_statement( "Important debug info", verbose=True, verbosity=1, log_level="debug",)
# This WILL be shown (verbosity 2 <= threshold 2)logger.logged_statement( "Less important debug info", verbose=True, verbosity=2, log_level="debug",)
# This will be SUPPRESSED (verbosity 3 > threshold 2)result = logger.logged_statement( "Very detailed debug info", verbose=True, verbosity=3, log_level="debug",)# result is None when suppressedRegister markers that bypass verbosity filtering:
from lifecyclelogging import Logging
logger = Logging( enable_console=True, enable_verbose_output=True, verbosity_threshold=1, # Very restrictive)
# Register bypass markerlogger.register_verbosity_bypass_marker("CRITICAL_PATH")
# This bypasses verbosity despite high verbosity levellogger.logged_statement( "Critical path message - always shown", context_marker="CRITICAL_PATH", verbose=True, verbosity=100, # Would normally be suppressed log_level="debug",)Add context labels to messages:
from lifecyclelogging import Logging
logger = Logging(enable_console=True)
# Messages with contextlogger.logged_statement( "Application starting", context_marker="STARTUP", log_level="info",)
logger.logged_statement( "Loading configuration", context_marker="CONFIG", log_level="debug",)
logger.logged_statement( "Graceful shutdown", context_marker="SHUTDOWN", log_level="info",)
# Output:# [STARTUP] [INFO] Application starting# [CONFIG] [DEBUG] Loading configuration# [SHUTDOWN] [INFO] Graceful shutdownStore messages for later retrieval:
from lifecyclelogging import Logging
logger = Logging(enable_console=True)
# Store messages under markerslogger.logged_statement( "User login failed", storage_marker="SECURITY_EVENTS", log_level="warning",)
logger.logged_statement( "Password reset requested", storage_marker="SECURITY_EVENTS", log_level="info",)
logger.logged_statement( "Database connection timeout", storage_marker="ERRORS", log_level="error",)
# Retrieve stored messagessecurity_events = logger.stored_messages["SECURITY_EVENTS"]errors = logger.stored_messages["ERRORS"]
print(f"Security events: {len(security_events)}")print(f"Errors: {len(errors)}")Control which log levels are stored:
from lifecyclelogging import Logging
logger = Logging(enable_console=True)
# Only store WARNING and abovelogger.register_storage_level_filter( "ALERTS", allowed_levels=["warning", "error", "critical"],)
# This will NOT be stored (info < warning)logger.logged_statement( "Normal operation", storage_marker="ALERTS", log_level="info",)
# This WILL be storedlogger.logged_statement( "Disk space low", storage_marker="ALERTS", log_level="warning",)
alerts = logger.stored_messages.get("ALERTS", [])print(f"Alerts: {len(alerts)}") # 1Attach identifiers to messages for correlation:
from lifecyclelogging import Logging
logger = Logging(enable_console=True)
# Log with identifierslogger.logged_statement( "Processing order", identifiers=["order_123", "customer_456"], log_level="info",)
# Output:# [order_123] [customer_456] [INFO] Processing orderLifecycle Logging automatically integrates with Gunicorn:
bind = "0.0.0.0:8000"workers = 4
# Lifecycle Logging will detect and use Gunicorn's logger
# app.pyfrom lifecyclelogging import Logging
# Automatically uses Gunicorn's handlers if running under Gunicornlogger = Logging( enable_console=True, enable_file=True,)Special formatting for exit/shutdown sequences:
from lifecyclelogging import Logging
logger = Logging(enable_console=True)
# Regular messagelogger.logged_statement("Normal operation", log_level="info")
# Exit run message (special formatting)logger.logged_statement( "Shutting down gracefully", exit_run=True, log_level="info",)
# Output with visual separator:# [INFO] Normal operation# ════════════════════════════════════════# [EXIT] [INFO] Shutting down gracefully# ════════════════════════════════════════from lifecyclelogging import Logging
# Production-ready logger setuplogger = Logging( enable_console=True, enable_file=True, log_file_path="/var/log/myapp/app.log", logger_name="myapp", enable_verbose_output=True, verbosity_threshold=2,)
# Register bypass markers for critical pathslogger.register_verbosity_bypass_marker("SECURITY")logger.register_verbosity_bypass_marker("HEALTH_CHECK")
# Register storage filterslogger.register_storage_level_filter( "ERRORS", allowed_levels=["error", "critical"],)
# Application logginglogger.logged_statement( "Application started", context_marker="STARTUP", log_level="info",)
def handle_request(request_id: str, user_id: str): logger.logged_statement( "Request received", identifiers=[request_id, user_id], json_data={"method": "POST", "path": "/api/data"}, log_level="info", )
# Detailed trace (only shown if verbosity allows) logger.logged_statement( "Parsing request body", verbose=True, verbosity=3, log_level="debug", )
# Error logging with storage try: # ... process request ... pass except Exception as e: logger.logged_statement( f"Request failed: {e}", identifiers=[request_id], storage_marker="ERRORS", log_level="error", )
def shutdown(): # Report stored errors errors = logger.stored_messages.get("ERRORS", []) if errors: logger.logged_statement( f"Session ended with {len(errors)} errors", exit_run=True, log_level="warning", ) else: logger.logged_statement( "Clean shutdown", exit_run=True, log_level="info", )| Parameter | Type | Default | Description |
|---|---|---|---|
enable_console | bool | True | Enable console output |
enable_file | bool | False | Enable file output |
log_file_path | str | None | Path for log file |
logger_name | str | "lifecycle" | Logger name prefix |
enable_verbose_output | bool | False | Enable verbosity filtering |
verbosity_threshold | int | 1 | Max verbosity to show |
| Parameter | Type | Description |
|---|---|---|
message | str | The log message |
log_level | str | "debug", "info", "warning", "error", "critical" |
context_marker | str | Context label for message |
storage_marker | str | Key for message storage |
identifiers | list[str] | Correlation identifiers |
json_data | dict | JSON data to attach |
labeled_json_data | dict[str, dict] | Labeled JSON sections |
verbose | bool | Mark as verbose message |
verbosity | int | Verbosity level (higher = more verbose) |
exit_run | bool | Use exit run formatting |
| Property | Type | Description |
|---|---|---|
stored_messages | dict[str, list] | Messages stored by marker |
| Method | Description |
|---|---|
register_verbosity_bypass_marker(marker) | Register marker that bypasses verbosity |
register_storage_level_filter(marker, allowed_levels) | Set allowed levels for storage marker |