0
# Logging System
1
2
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.
3
4
## Capabilities
5
6
### Log Handler Interface
7
8
Base interface for logging functionality that defines the contract for logging operations.
9
10
```python { .api }
11
class LogHandler:
12
"""
13
Logging handler interface for application logging functionality.
14
15
Provides methods for logging at different levels and controlling
16
log output configuration and formatting.
17
"""
18
19
def set_level(self, level: str) -> None:
20
"""
21
Set the logging level.
22
23
Args:
24
level: Logging level ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
25
"""
26
27
def get_level(self) -> str:
28
"""
29
Get the current logging level.
30
31
Returns:
32
Current logging level string
33
"""
34
35
def debug(self, msg: str, **kwargs: Any) -> None:
36
"""
37
Log a debug message.
38
39
Args:
40
msg: Message to log
41
**kwargs: Additional keyword arguments for formatting
42
"""
43
44
def info(self, msg: str, **kwargs: Any) -> None:
45
"""
46
Log an info message.
47
48
Args:
49
msg: Message to log
50
**kwargs: Additional keyword arguments for formatting
51
"""
52
53
def warning(self, msg: str, **kwargs: Any) -> None:
54
"""
55
Log a warning message.
56
57
Args:
58
msg: Message to log
59
**kwargs: Additional keyword arguments for formatting
60
"""
61
62
def error(self, msg: str, **kwargs: Any) -> None:
63
"""
64
Log an error message.
65
66
Args:
67
msg: Message to log
68
**kwargs: Additional keyword arguments for formatting
69
"""
70
71
def fatal(self, msg: str, **kwargs: Any) -> None:
72
"""
73
Log a fatal/critical message.
74
75
Args:
76
msg: Message to log
77
**kwargs: Additional keyword arguments for formatting
78
"""
79
```
80
81
## Usage Examples
82
83
### Basic Logging
84
85
```python
86
from cement import App, Controller, ex
87
88
class BaseController(Controller):
89
class Meta:
90
label = 'base'
91
92
@ex(help='demonstrate logging')
93
def demo(self):
94
"""Demonstrate different logging levels."""
95
self.app.log.debug('This is a debug message')
96
self.app.log.info('Application started successfully')
97
self.app.log.warning('This is a warning message')
98
self.app.log.error('An error occurred')
99
100
try:
101
# Simulate an operation that might fail
102
result = 10 / 0
103
except ZeroDivisionError:
104
self.app.log.error('Division by zero error occurred')
105
106
class MyApp(App):
107
class Meta:
108
label = 'myapp'
109
base_controller = 'base'
110
handlers = [BaseController]
111
112
with MyApp() as app:
113
app.setup()
114
115
# Set logging level
116
app.log.set_level('DEBUG')
117
118
app.run()
119
```
120
121
### File Logging Configuration
122
123
```python
124
from cement import App, init_defaults
125
126
CONFIG = init_defaults('myapp')
127
CONFIG['log.logging'] = {
128
'file': '/var/log/myapp.log',
129
'level': 'INFO',
130
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
131
'rotate': True,
132
'max_bytes': 1024000, # 1MB
133
'max_files': 5
134
}
135
136
class MyApp(App):
137
class Meta:
138
label = 'myapp'
139
config_defaults = CONFIG
140
log_handler = 'logging'
141
142
with MyApp() as app:
143
app.setup()
144
145
app.log.info('Application started')
146
app.log.debug('Debug information')
147
app.log.warning('Warning message')
148
149
app.run()
150
```
151
152
### Colorized Logging
153
154
```python
155
from cement import App, init_defaults
156
157
CONFIG = init_defaults('myapp')
158
CONFIG['log.colorlog'] = {
159
'level': 'DEBUG',
160
'format': '%(log_color)s%(levelname)s%(reset)s - %(message)s',
161
'colors': {
162
'DEBUG': 'cyan',
163
'INFO': 'green',
164
'WARNING': 'yellow',
165
'ERROR': 'red',
166
'CRITICAL': 'bold_red'
167
}
168
}
169
170
class MyApp(App):
171
class Meta:
172
label = 'myapp'
173
extensions = ['colorlog']
174
log_handler = 'colorlog'
175
config_defaults = CONFIG
176
177
with MyApp() as app:
178
app.setup()
179
180
app.log.debug('Debug message in cyan')
181
app.log.info('Info message in green')
182
app.log.warning('Warning message in yellow')
183
app.log.error('Error message in red')
184
185
app.run()
186
```
187
188
### Custom Logging Configuration
189
190
```python
191
from cement import App, Controller, ex
192
import logging
193
194
class LoggingController(Controller):
195
class Meta:
196
label = 'logging'
197
stacked_on = 'base'
198
stacked_type = 'nested'
199
200
@ex(
201
help='set logging level',
202
arguments=[
203
(['level'], {
204
'choices': ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
205
'help': 'logging level to set'
206
})
207
]
208
)
209
def set_level(self):
210
"""Set the logging level."""
211
level = self.app.pargs.level
212
self.app.log.set_level(level)
213
self.app.log.info(f'Logging level set to {level}')
214
215
@ex(help='show current logging level')
216
def get_level(self):
217
"""Show current logging level."""
218
level = self.app.log.get_level()
219
print(f'Current logging level: {level}')
220
221
@ex(help='test all logging levels')
222
def test(self):
223
"""Test all logging levels."""
224
self.app.log.debug('Debug level message')
225
self.app.log.info('Info level message')
226
self.app.log.warning('Warning level message')
227
self.app.log.error('Error level message')
228
self.app.log.fatal('Fatal level message')
229
230
class BaseController(Controller):
231
class Meta:
232
label = 'base'
233
234
class MyApp(App):
235
class Meta:
236
label = 'myapp'
237
base_controller = 'base'
238
handlers = [
239
BaseController,
240
LoggingController
241
]
242
243
with MyApp() as app:
244
app.run()
245
246
# Usage:
247
# myapp logging set-level DEBUG
248
# myapp logging get-level
249
# myapp logging test
250
```
251
252
### Structured Logging
253
254
```python
255
from cement import App, Controller, ex
256
import json
257
import logging
258
259
class StructuredLogHandler(logging.Handler):
260
"""Custom handler for structured JSON logging."""
261
262
def emit(self, record):
263
log_entry = {
264
'timestamp': self.format_time(record),
265
'level': record.levelname,
266
'message': record.getMessage(),
267
'module': record.module,
268
'function': record.funcName,
269
'line': record.lineno
270
}
271
272
# Add extra fields if present
273
if hasattr(record, 'user_id'):
274
log_entry['user_id'] = record.user_id
275
if hasattr(record, 'request_id'):
276
log_entry['request_id'] = record.request_id
277
278
print(json.dumps(log_entry))
279
280
def format_time(self, record):
281
import datetime
282
return datetime.datetime.fromtimestamp(record.created).isoformat()
283
284
class BaseController(Controller):
285
class Meta:
286
label = 'base'
287
288
@ex(help='demonstrate structured logging')
289
def structured_demo(self):
290
"""Demonstrate structured logging with extra fields."""
291
# Log with extra context
292
extra = {
293
'user_id': 'user_123',
294
'request_id': 'req_456'
295
}
296
297
self.app.log.info('User login attempt', extra=extra)
298
self.app.log.warning('Invalid login credentials', extra=extra)
299
self.app.log.error('Database connection failed', extra=extra)
300
301
class MyApp(App):
302
class Meta:
303
label = 'myapp'
304
base_controller = 'base'
305
handlers = [BaseController]
306
307
def setup(self):
308
super().setup()
309
310
# Add structured logging handler
311
structured_handler = StructuredLogHandler()
312
self.log.logger.addHandler(structured_handler)
313
314
with MyApp() as app:
315
app.run()
316
```
317
318
### Logging with Context Managers
319
320
```python
321
from cement import App, Controller, ex
322
import contextlib
323
import time
324
325
@contextlib.contextmanager
326
def log_execution_time(logger, operation_name):
327
"""Context manager to log operation execution time."""
328
start_time = time.time()
329
logger.info(f'Starting {operation_name}')
330
331
try:
332
yield
333
execution_time = time.time() - start_time
334
logger.info(f'Completed {operation_name} in {execution_time:.2f} seconds')
335
except Exception as e:
336
execution_time = time.time() - start_time
337
logger.error(f'Failed {operation_name} after {execution_time:.2f} seconds: {e}')
338
raise
339
340
class BaseController(Controller):
341
class Meta:
342
label = 'base'
343
344
@ex(help='demonstrate logging with context manager')
345
def timed_operation(self):
346
"""Demonstrate timed operation logging."""
347
with log_execution_time(self.app.log, 'data processing'):
348
# Simulate some work
349
time.sleep(2)
350
self.app.log.info('Processing data...')
351
time.sleep(1)
352
self.app.log.info('Data processing completed')
353
354
class MyApp(App):
355
class Meta:
356
label = 'myapp'
357
base_controller = 'base'
358
handlers = [BaseController]
359
360
with MyApp() as app:
361
app.run()
362
```
363
364
### Multiple Log Destinations
365
366
```python
367
from cement import App, init_defaults
368
import logging
369
import sys
370
371
CONFIG = init_defaults('myapp')
372
CONFIG['log.logging'] = {
373
'level': 'INFO',
374
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
375
}
376
377
class MyApp(App):
378
class Meta:
379
label = 'myapp'
380
config_defaults = CONFIG
381
log_handler = 'logging'
382
383
def setup(self):
384
super().setup()
385
386
# Add file handler
387
file_handler = logging.FileHandler('/var/log/myapp.log')
388
file_formatter = logging.Formatter(
389
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
390
)
391
file_handler.setFormatter(file_formatter)
392
file_handler.setLevel(logging.INFO)
393
394
# Add console handler for errors only
395
error_handler = logging.StreamHandler(sys.stderr)
396
error_formatter = logging.Formatter(
397
'ERROR: %(message)s'
398
)
399
error_handler.setFormatter(error_formatter)
400
error_handler.setLevel(logging.ERROR)
401
402
# Add handlers to logger
403
self.log.logger.addHandler(file_handler)
404
self.log.logger.addHandler(error_handler)
405
406
with MyApp() as app:
407
app.setup()
408
409
app.log.info('This goes to file and console')
410
app.log.warning('This goes to file and console')
411
app.log.error('This goes to file, console, and stderr')
412
413
app.run()
414
```