Advanced Usage¶
Contextual Logging¶
One of the key features of ctxlog is the ability to add context to your logs. This is particularly useful for tracking complex operations across multiple functions or modules.
def process_payment(payment):
log = logger.new(event="payment_process").ctx(payment_id=payment.id)
try:
transaction_id = charge_payment(payment)
log.ctx(transaction_id=transaction_id).info("Payment processed")
except Exception as e:
log.exc(e).error("Payment failed")
Log Chaining¶
Log chaining allows you to create a hierarchy of logs for complex operations. This is useful for tracking multi-step processes and maintaining context across different stages.
def process_order(order):
log = logger.new(event="order_process").ctx(order_id=order.id)
validate_order(log.new("validation"), order)
log.info("Order processed")
def validate_order(log, order):
log = log.ctx(order_id=order.id)
# Perform validation
log.info("Order validated")
When the parent log is emitted, all child logs are included in the output. This creates a structured log entry that shows the entire process flow.
Exception Handling¶
ctxlog provides a convenient way to attach exception details to logs:
try:
# Some code that might raise an exception
result = 1 / 0
except Exception as e:
log = logger.new()
log.exc(e).error("Operation failed")
The exc() method attaches the exception type, message, and traceback to the log entry. This makes it easier to debug issues in production.
Thread Safety¶
ctxlog is designed to be thread-safe. Each handler uses a lock to prevent interleaved output from multiple threads.
Custom Handlers¶
You can create custom handlers by extending the Handler base class:
from ctxlog import Handler, LogLevel
class CustomHandler(Handler):
def __init__(self, level=None, serialize=False):
super().__init__(level, serialize)
# Initialize your handler
def emit(self, log_entry):
# Implement your custom emission logic
formatted = self.format(log_entry)
# Send the formatted log to your destination
def close(self):
# Clean up any resources
pass
Performance Considerations¶
For high-performance applications, consider the following:
Lazy Evaluation: Use the
new()method to create a log context, but only call the logging methods (info(),error(), etc.) if the log will actually be emitted:log = logger.new() log.ctx(user_id=user.id) # Only build and emit the log if needed if expensive_condition(): log.info("Expensive operation completed")
Handler Levels: Set appropriate levels for each handler to minimize processing:
# Console gets INFO and above console_handler = ConsoleHandler(level=LogLevel.INFO) # File gets everything for debugging file_handler = FileHandler( level=LogLevel.DEBUG, file_path="debug.log" )
Serialization: Only use
serialize=Truewhen needed, as JSON serialization adds overhead.
Integration with Other Libraries¶
ctxlog can be integrated with other logging systems:
import logging
from ctxlog import Handler
class PythonLoggingHandler(Handler):
def __init__(self, logger_name="ctxlog", level=None, serialize=False):
super().__init__(level, serialize)
self.logger = logging.getLogger(logger_name)
def emit(self, log_entry):
level_name = log_entry.get("level", "").upper()
message = self.format(log_entry)
# Map ctxlog levels to Python logging levels
if level_name == "DEBUG":
self.logger.debug(message)
elif level_name == "INFO":
self.logger.info(message)
elif level_name == "WARNING":
self.logger.warning(message)
elif level_name == "ERROR":
self.logger.error(message)
elif level_name == "CRITICAL":
self.logger.critical(message)