Skip to content

Lifecycle Logging

Lifecycle Logging (lifecyclelogging) is a comprehensive logging utility combining Python’s standard logging with rich output formatting, verbosity controls, and message storage.

Terminal window
pip install lifecyclelogging

PyPI Python CI


Rich 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 output
logger = Logging(enable_console=True, enable_file=False)
# Log at different levels
logger.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 messages
user_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 sections
labeled_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 suppressed

Register 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 marker
logger.register_verbosity_bypass_marker("CRITICAL_PATH")
# This bypasses verbosity despite high verbosity level
logger.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 context
logger.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 shutdown

Store messages for later retrieval:

from lifecyclelogging import Logging
logger = Logging(enable_console=True)
# Store messages under markers
logger.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 messages
security_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 above
logger.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 stored
logger.logged_statement(
"Disk space low",
storage_marker="ALERTS",
log_level="warning",
)
alerts = logger.stored_messages.get("ALERTS", [])
print(f"Alerts: {len(alerts)}") # 1

Attach identifiers to messages for correlation:

from lifecyclelogging import Logging
logger = Logging(enable_console=True)
# Log with identifiers
logger.logged_statement(
"Processing order",
identifiers=["order_123", "customer_456"],
log_level="info",
)
# Output:
# [order_123] [customer_456] [INFO] Processing order

Lifecycle Logging automatically integrates with Gunicorn:

gunicorn.conf.py
bind = "0.0.0.0:8000"
workers = 4
# Lifecycle Logging will detect and use Gunicorn's logger
# app.py
from lifecyclelogging import Logging
# Automatically uses Gunicorn's handlers if running under Gunicorn
logger = Logging(
enable_console=True,
enable_file=True,
)

Special formatting for exit/shutdown sequences:

from lifecyclelogging import Logging
logger = Logging(enable_console=True)
# Regular message
logger.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 setup
logger = 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 paths
logger.register_verbosity_bypass_marker("SECURITY")
logger.register_verbosity_bypass_marker("HEALTH_CHECK")
# Register storage filters
logger.register_storage_level_filter(
"ERRORS",
allowed_levels=["error", "critical"],
)
# Application logging
logger.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",
)

ParameterTypeDefaultDescription
enable_consoleboolTrueEnable console output
enable_fileboolFalseEnable file output
log_file_pathstrNonePath for log file
logger_namestr"lifecycle"Logger name prefix
enable_verbose_outputboolFalseEnable verbosity filtering
verbosity_thresholdint1Max verbosity to show
ParameterTypeDescription
messagestrThe log message
log_levelstr"debug", "info", "warning", "error", "critical"
context_markerstrContext label for message
storage_markerstrKey for message storage
identifierslist[str]Correlation identifiers
json_datadictJSON data to attach
labeled_json_datadict[str, dict]Labeled JSON sections
verboseboolMark as verbose message
verbosityintVerbosity level (higher = more verbose)
exit_runboolUse exit run formatting
PropertyTypeDescription
stored_messagesdict[str, list]Messages stored by marker
MethodDescription
register_verbosity_bypass_marker(marker)Register marker that bypasses verbosity
register_storage_level_filter(marker, allowed_levels)Set allowed levels for storage marker