Advanced Application Framework for Python with a focus on Command Line Interfaces
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
The logging system provides comprehensive logging functionality with console and file output support, multiple log levels, colorized output options, and integration with Python's standard logging module.
Base interface for logging functionality that defines the contract for logging operations.
class LogHandler:
"""
Logging handler interface for application logging functionality.
Provides methods for logging at different levels and controlling
log output configuration and formatting.
"""
def set_level(self, level: str) -> None:
"""
Set the logging level.
Args:
level: Logging level ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
"""
def get_level(self) -> str:
"""
Get the current logging level.
Returns:
Current logging level string
"""
def debug(self, msg: str, **kwargs: Any) -> None:
"""
Log a debug message.
Args:
msg: Message to log
**kwargs: Additional keyword arguments for formatting
"""
def info(self, msg: str, **kwargs: Any) -> None:
"""
Log an info message.
Args:
msg: Message to log
**kwargs: Additional keyword arguments for formatting
"""
def warning(self, msg: str, **kwargs: Any) -> None:
"""
Log a warning message.
Args:
msg: Message to log
**kwargs: Additional keyword arguments for formatting
"""
def error(self, msg: str, **kwargs: Any) -> None:
"""
Log an error message.
Args:
msg: Message to log
**kwargs: Additional keyword arguments for formatting
"""
def fatal(self, msg: str, **kwargs: Any) -> None:
"""
Log a fatal/critical message.
Args:
msg: Message to log
**kwargs: Additional keyword arguments for formatting
"""from cement import App, Controller, ex
class BaseController(Controller):
class Meta:
label = 'base'
@ex(help='demonstrate logging')
def demo(self):
"""Demonstrate different logging levels."""
self.app.log.debug('This is a debug message')
self.app.log.info('Application started successfully')
self.app.log.warning('This is a warning message')
self.app.log.error('An error occurred')
try:
# Simulate an operation that might fail
result = 10 / 0
except ZeroDivisionError:
self.app.log.error('Division by zero error occurred')
class MyApp(App):
class Meta:
label = 'myapp'
base_controller = 'base'
handlers = [BaseController]
with MyApp() as app:
app.setup()
# Set logging level
app.log.set_level('DEBUG')
app.run()from cement import App, init_defaults
CONFIG = init_defaults('myapp')
CONFIG['log.logging'] = {
'file': '/var/log/myapp.log',
'level': 'INFO',
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
'rotate': True,
'max_bytes': 1024000, # 1MB
'max_files': 5
}
class MyApp(App):
class Meta:
label = 'myapp'
config_defaults = CONFIG
log_handler = 'logging'
with MyApp() as app:
app.setup()
app.log.info('Application started')
app.log.debug('Debug information')
app.log.warning('Warning message')
app.run()from cement import App, init_defaults
CONFIG = init_defaults('myapp')
CONFIG['log.colorlog'] = {
'level': 'DEBUG',
'format': '%(log_color)s%(levelname)s%(reset)s - %(message)s',
'colors': {
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'bold_red'
}
}
class MyApp(App):
class Meta:
label = 'myapp'
extensions = ['colorlog']
log_handler = 'colorlog'
config_defaults = CONFIG
with MyApp() as app:
app.setup()
app.log.debug('Debug message in cyan')
app.log.info('Info message in green')
app.log.warning('Warning message in yellow')
app.log.error('Error message in red')
app.run()from cement import App, Controller, ex
import logging
class LoggingController(Controller):
class Meta:
label = 'logging'
stacked_on = 'base'
stacked_type = 'nested'
@ex(
help='set logging level',
arguments=[
(['level'], {
'choices': ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
'help': 'logging level to set'
})
]
)
def set_level(self):
"""Set the logging level."""
level = self.app.pargs.level
self.app.log.set_level(level)
self.app.log.info(f'Logging level set to {level}')
@ex(help='show current logging level')
def get_level(self):
"""Show current logging level."""
level = self.app.log.get_level()
print(f'Current logging level: {level}')
@ex(help='test all logging levels')
def test(self):
"""Test all logging levels."""
self.app.log.debug('Debug level message')
self.app.log.info('Info level message')
self.app.log.warning('Warning level message')
self.app.log.error('Error level message')
self.app.log.fatal('Fatal level message')
class BaseController(Controller):
class Meta:
label = 'base'
class MyApp(App):
class Meta:
label = 'myapp'
base_controller = 'base'
handlers = [
BaseController,
LoggingController
]
with MyApp() as app:
app.run()
# Usage:
# myapp logging set-level DEBUG
# myapp logging get-level
# myapp logging testfrom cement import App, Controller, ex
import json
import logging
class StructuredLogHandler(logging.Handler):
"""Custom handler for structured JSON logging."""
def emit(self, record):
log_entry = {
'timestamp': self.format_time(record),
'level': record.levelname,
'message': record.getMessage(),
'module': record.module,
'function': record.funcName,
'line': record.lineno
}
# Add extra fields if present
if hasattr(record, 'user_id'):
log_entry['user_id'] = record.user_id
if hasattr(record, 'request_id'):
log_entry['request_id'] = record.request_id
print(json.dumps(log_entry))
def format_time(self, record):
import datetime
return datetime.datetime.fromtimestamp(record.created).isoformat()
class BaseController(Controller):
class Meta:
label = 'base'
@ex(help='demonstrate structured logging')
def structured_demo(self):
"""Demonstrate structured logging with extra fields."""
# Log with extra context
extra = {
'user_id': 'user_123',
'request_id': 'req_456'
}
self.app.log.info('User login attempt', extra=extra)
self.app.log.warning('Invalid login credentials', extra=extra)
self.app.log.error('Database connection failed', extra=extra)
class MyApp(App):
class Meta:
label = 'myapp'
base_controller = 'base'
handlers = [BaseController]
def setup(self):
super().setup()
# Add structured logging handler
structured_handler = StructuredLogHandler()
self.log.logger.addHandler(structured_handler)
with MyApp() as app:
app.run()from cement import App, Controller, ex
import contextlib
import time
@contextlib.contextmanager
def log_execution_time(logger, operation_name):
"""Context manager to log operation execution time."""
start_time = time.time()
logger.info(f'Starting {operation_name}')
try:
yield
execution_time = time.time() - start_time
logger.info(f'Completed {operation_name} in {execution_time:.2f} seconds')
except Exception as e:
execution_time = time.time() - start_time
logger.error(f'Failed {operation_name} after {execution_time:.2f} seconds: {e}')
raise
class BaseController(Controller):
class Meta:
label = 'base'
@ex(help='demonstrate logging with context manager')
def timed_operation(self):
"""Demonstrate timed operation logging."""
with log_execution_time(self.app.log, 'data processing'):
# Simulate some work
time.sleep(2)
self.app.log.info('Processing data...')
time.sleep(1)
self.app.log.info('Data processing completed')
class MyApp(App):
class Meta:
label = 'myapp'
base_controller = 'base'
handlers = [BaseController]
with MyApp() as app:
app.run()from cement import App, init_defaults
import logging
import sys
CONFIG = init_defaults('myapp')
CONFIG['log.logging'] = {
'level': 'INFO',
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
}
class MyApp(App):
class Meta:
label = 'myapp'
config_defaults = CONFIG
log_handler = 'logging'
def setup(self):
super().setup()
# Add file handler
file_handler = logging.FileHandler('/var/log/myapp.log')
file_formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
file_handler.setFormatter(file_formatter)
file_handler.setLevel(logging.INFO)
# Add console handler for errors only
error_handler = logging.StreamHandler(sys.stderr)
error_formatter = logging.Formatter(
'ERROR: %(message)s'
)
error_handler.setFormatter(error_formatter)
error_handler.setLevel(logging.ERROR)
# Add handlers to logger
self.log.logger.addHandler(file_handler)
self.log.logger.addHandler(error_handler)
with MyApp() as app:
app.setup()
app.log.info('This goes to file and console')
app.log.warning('This goes to file and console')
app.log.error('This goes to file, console, and stderr')
app.run()Install with Tessl CLI
npx tessl i tessl/pypi-cement