Advanced Usage
This section dives deep into the more advanced capabilities of Safe Init, providing seasoned Python developers with the insights needed to leverage this tool to its fullest. Whether you're looking to customize logging, monitor function execution more closely, or integrate Safe Init seamlessly into complex environments, this guide has you covered.
Custom Logger Configuration
While Safe Init uses structlog for logging by default, it's flexible enough to accommodate your custom logging setup. Here’s how you can integrate a custom structlog logger:
Example: Custom structlog Logger
import structlog
from contextvars import ContextVar
from typing import Callable
from structlog.stdlib import LoggerFactory
# Configure structlog to use standard library's logging module
structlog.configure(logger_factory=LoggerFactory())
# Define your custom logger configuration
structlog.configure(
processors=[
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.processors.TimeStamper(fmt="ISO"),
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
structlog.stdlib.render_to_log_kwargs,
],
context_class=dict,
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
# Custom function to retrieve the configured logger
def my_logger_getter():
return structlog.get_logger('my_custom_logger')
# Assign your custom logger to Safe Init using ContextVars
safe_init_logger_getter: ContextVar[Callable] = ContextVar("safe_init_logger_getter")
safe_init_logger_getter.set(my_logger_getter)
This example demonstrates how to set up a structlog logger with custom formatting and assign it to Safe Init, ensuring all internal logging respects your preferred configuration.
Custom Slack Webhook URL
While the SAFE_INIT_SLACK_WEBHOOK_URL environment variable is the standard way to set your Slack webhook URL, Safe Init allows you to programmatically set this URL:
Example: Setting Slack Webhook URL Programmatically
from contextvars import ContextVar
safe_init_webhook_url: ContextVar[str] = ContextVar("safe_init_slack_webhook_url")
safe_init_webhook_url.set("https://hooks.slack.com/services/xxx/yyy/zzz")
This approach is particularly useful in scenarios where environment variables might not be the best option for configuration management.
Monkey Patching Slack Notifications
For cases where you need to modify the behavior of Slack notifications (e.g., to add filtering logic or to enhance messages with additional information), monkey patching allows you to intervene in the notification process:
Example: Monkey Patching Slack Notification Function
from safe_init.slack import slack_notify as original_slack_notify
def custom_slack_notify(context_message, e, **kwargs):
# Add custom logic here, e.g., filtering based on exception type
if isinstance(e, ValueError):
return # Skip notification for ValueError
# Modify context message
context_message = f"Custom Prefix: {context_message}"
# Call the original slack_notify function
original_slack_notify(context_message, e, **kwargs)
# Monkey patch the slack_notify function
import safe_init.slack
safe_init.slack.slack_notify = custom_slack_notify
Monitoring Timeouts with Tracing
Safe Init's tracing capabilities can be invaluable for monitoring and diagnosing timeouts in your Lambda functions. Here's how to leverage this feature for detailed insights into function execution times:
Example: Enabling Function Call Tracing
from safe_init.tracer import traced
# Assume this is your Lambda handler
def my_lambda_handler(event, context):
# Your Lambda logic here
pass
# Wrap your handler with Safe Init's tracing decorator
traced_handler = traced(my_lambda_handler)
# Set `traced_handler` as the entry point in SAFE_INIT_HANDLER
Or as a decorator:
from safe_init.tracer import traced
@traced
def my_lambda_handler(event, context):
# Your Lambda logic here
pass
# Set `my_lambda_handler` as the entry point in SAFE_INIT_HANDLER
This setup ensures that all function calls within my_lambda_handler are traced, giving you visibility into potential performance bottlenecks.
Dynamic Error Handling Strategies
While Safe Init provides a robust error handling mechanism out of the box, you might have scenarios where dynamic error handling strategies are necessary, such as different handling logic based on the exception type or context.
Example: Custom Error Handling Logic
from safe_init.decorator import safe_wrapper
# Original handler function
def my_handler(event, context):
# Your logic here
pass
# Custom wrapper to add error handling logic
def my_custom_error_handler(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except ValueError as e:
# Custom handling for ValueError
print(f"Handling ValueError: {e}")
# Re-raise or handle the exception
except Exception as e:
# Fallback error handling
raise e
return wrapper
# Apply Safe Init's safe_wrapper and your custom error handling
wrapped_handler = safe_wrapper(my_custom_error_handler(my_handler))
This approach allows you to wrap your handler with both Safe Init's safety mechanisms and your custom error handling logic. It's particularly useful for scenarios where specific exceptions require unique handling beyond logging and notifications.