0
# Logging System
1
2
Specialized logging system designed for template environments and debugging mkdocstrings processing. The logging system provides enhanced logger adapters, template-friendly logging interfaces, and utility functions for debugging documentation generation.
3
4
## Capabilities
5
6
### Logger Adapter
7
8
Enhanced logger adapter with message prefixes and once-only logging support for cleaner log output.
9
10
```python { .api }
11
class LoggerAdapter(logging.LoggerAdapter):
12
"""Logger adapter with prefix and once-only logging support."""
13
14
def __init__(self, prefix: str, logger: logging.Logger) -> None:
15
"""
16
Initialize adapter with prefix.
17
18
Args:
19
prefix: Message prefix string
20
logger: Base logger instance
21
"""
22
23
def process(
24
self,
25
msg: str,
26
kwargs: MutableMapping[str, Any]
27
) -> tuple[str, Any]:
28
"""
29
Process log message and add prefix.
30
31
Args:
32
msg: Log message
33
kwargs: Logging keyword arguments
34
35
Returns:
36
Tuple of processed message and kwargs
37
"""
38
39
def log(
40
self,
41
level: int,
42
msg: object,
43
*args: object,
44
**kwargs: object
45
) -> None:
46
"""
47
Log message with enhanced functionality.
48
49
Args:
50
level: Logging level
51
msg: Message to log
52
*args: Message arguments
53
**kwargs: Logging options
54
"""
55
56
prefix: str
57
"""Message prefix string."""
58
```
59
60
**Usage Examples:**
61
62
Create logger with prefix:
63
```python
64
import logging
65
from mkdocstrings import LoggerAdapter
66
67
# Create base logger
68
base_logger = logging.getLogger("mkdocstrings.myhandler")
69
70
# Create adapter with prefix
71
logger = LoggerAdapter("MyHandler", base_logger)
72
73
# Log messages with automatic prefix
74
logger.info("Processing module") # Logs: "[MyHandler] Processing module"
75
logger.error("Failed to parse") # Logs: "[MyHandler] Failed to parse"
76
logger.debug("Debug information") # Logs: "[MyHandler] Debug information"
77
```
78
79
Handler integration:
80
```python
81
class MyHandler(BaseHandler):
82
def __init__(self, *args, **kwargs):
83
super().__init__(*args, **kwargs)
84
85
# Create logger with handler-specific prefix
86
base_logger = logging.getLogger(f"mkdocstrings.{self.name}")
87
self.logger = LoggerAdapter(f"{self.name.title()}Handler", base_logger)
88
89
def collect(self, identifier: str, options: HandlerOptions) -> CollectorItem:
90
self.logger.info(f"Collecting documentation for {identifier}")
91
92
try:
93
data = self._do_collection(identifier)
94
self.logger.debug(f"Successfully collected {len(data.members)} members")
95
return data
96
except Exception as e:
97
self.logger.error(f"Collection failed for {identifier}: {e}")
98
raise
99
```
100
101
### Template Logger
102
103
Logger wrapper designed for use within Jinja2 templates, providing template-friendly logging methods.
104
105
```python { .api }
106
class TemplateLogger:
107
"""Logger wrapper for use in Jinja templates."""
108
109
def __init__(self, logger: LoggerAdapter) -> None:
110
"""
111
Initialize with logger adapter.
112
113
Args:
114
logger: LoggerAdapter instance
115
"""
116
117
debug: Callable
118
"""Log DEBUG level messages."""
119
120
info: Callable
121
"""Log INFO level messages."""
122
123
warning: Callable
124
"""Log WARNING level messages."""
125
126
error: Callable
127
"""Log ERROR level messages."""
128
129
critical: Callable
130
"""Log CRITICAL level messages."""
131
```
132
133
**Usage Examples:**
134
135
Use in templates:
136
```python
137
from mkdocstrings import TemplateLogger, LoggerAdapter
138
139
# Create template logger
140
base_logger = logging.getLogger("mkdocstrings.template")
141
adapter = LoggerAdapter("Template", base_logger)
142
template_logger = TemplateLogger(adapter)
143
144
# Make available in template context
145
template_globals = {
146
"logger": template_logger,
147
"data": data
148
}
149
```
150
151
Template usage:
152
```jinja2
153
{# In Jinja2 template #}
154
{% if data.deprecated %}
155
{{ logger.warning("Rendering deprecated item: " + data.name) }}
156
{% endif %}
157
158
{% if not data.docstring %}
159
{{ logger.info("No docstring found for " + data.name) }}
160
{% endif %}
161
162
{# Log debug information #}
163
{{ logger.debug("Rendering " + data.type + " with " + data.members|length|string + " members") }}
164
```
165
166
Handler template integration:
167
```python
168
def render(self, data: CollectorItem, options: HandlerOptions, *, locale: str | None = None) -> str:
169
# Create template logger
170
template_logger = get_template_logger(self.name)
171
172
# Render template with logger
173
template = self.env.get_template("handler.html")
174
return template.render(
175
data=data,
176
options=options,
177
logger=template_logger
178
)
179
```
180
181
### Utility Functions
182
183
Helper functions for creating and managing loggers in different contexts.
184
185
```python { .api }
186
def get_logger(name: str) -> LoggerAdapter:
187
"""
188
Return a pre-configured logger for MkDocs.
189
190
Args:
191
name: Logger name
192
193
Returns:
194
LoggerAdapter instance
195
"""
196
197
def get_template_logger(handler_name: str | None = None) -> TemplateLogger:
198
"""
199
Return a logger for use in templates.
200
201
Args:
202
handler_name: Optional handler name for prefix
203
204
Returns:
205
TemplateLogger instance
206
"""
207
208
def get_template_logger_function(logger_func: Callable) -> Callable:
209
"""
210
Create wrapper function for template logging.
211
212
Args:
213
logger_func: Logger function to wrap
214
215
Returns:
216
Wrapped logger function
217
"""
218
219
def get_template_path(context: Context) -> str:
220
"""
221
Return path to template using given context.
222
223
Args:
224
context: Jinja2 template context
225
226
Returns:
227
Template path string
228
"""
229
```
230
231
**Usage Examples:**
232
233
Get configured logger:
234
```python
235
from mkdocstrings import get_logger
236
237
# Get logger for specific component
238
logger = get_logger("mkdocstrings.python")
239
240
logger.info("Python handler initialized")
241
logger.debug("Loading configuration")
242
```
243
244
Get template logger:
245
```python
246
from mkdocstrings import get_template_logger
247
248
# Get template logger for handler
249
template_logger = get_template_logger("python")
250
251
# Use in template rendering
252
template_context = {
253
"logger": template_logger,
254
"data": data,
255
"options": options
256
}
257
```
258
259
Template path debugging:
260
```python
261
from mkdocstrings import get_template_path
262
263
# In template rendering function
264
def render_template(self, template_name: str, **context):
265
template = self.env.get_template(template_name)
266
267
# Log template path for debugging
268
if template.environment.globals.get("logger"):
269
template_path = get_template_path(template.new_context())
270
template.environment.globals["logger"].debug(f"Rendering template: {template_path}")
271
272
return template.render(**context)
273
```
274
275
## Integration Patterns
276
277
### Handler Logging
278
279
Integrate logging throughout handler lifecycle:
280
281
```python
282
class MyHandler(BaseHandler):
283
def __init__(self, *args, **kwargs):
284
super().__init__(*args, **kwargs)
285
286
# Set up handler logger
287
self.logger = get_logger(f"mkdocstrings.{self.name}")
288
self.logger.info(f"Initialized {self.name} handler")
289
290
def collect(self, identifier: str, options: HandlerOptions) -> CollectorItem:
291
self.logger.debug(f"Collecting {identifier}")
292
293
try:
294
# Collection logic
295
data = self._parse_identifier(identifier)
296
self.logger.info(f"Collected {data.type} {data.name} with {len(data.members)} members")
297
return data
298
299
except ImportError as e:
300
self.logger.error(f"Import failed for {identifier}: {e}")
301
raise CollectionError(f"Could not import {identifier}")
302
303
except Exception as e:
304
self.logger.exception(f"Unexpected error collecting {identifier}")
305
raise
306
307
def render(self, data: CollectorItem, options: HandlerOptions, *, locale: str | None = None) -> str:
308
self.logger.debug(f"Rendering {data.name}")
309
310
# Create template logger
311
template_logger = get_template_logger(self.name)
312
313
try:
314
html = self.render_template("handler.html",
315
data=data,
316
options=options,
317
logger=template_logger
318
)
319
320
self.logger.debug(f"Rendered {len(html)} characters for {data.name}")
321
return html
322
323
except Exception as e:
324
self.logger.error(f"Rendering failed for {data.name}: {e}")
325
raise
326
```
327
328
### Plugin Logging
329
330
Plugin-level logging for configuration and lifecycle events:
331
332
```python
333
class MkdocstringsPlugin(BasePlugin):
334
def __init__(self):
335
super().__init__()
336
self.logger = get_logger("mkdocstrings.plugin")
337
338
def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None:
339
self.logger.info("Configuring mkdocstrings plugin")
340
341
# Log configuration details
342
if self.config.get("handlers"):
343
handler_names = list(self.config["handlers"].keys())
344
self.logger.info(f"Configured handlers: {', '.join(handler_names)}")
345
346
self.logger.debug(f"Default handler: {self.config['default_handler']}")
347
348
if self.config.get("custom_templates"):
349
self.logger.info(f"Using custom templates from: {self.config['custom_templates']}")
350
351
return config
352
353
def on_post_build(self, config: MkDocsConfig, **kwargs):
354
self.logger.info("mkdocstrings plugin build completed")
355
356
# Log inventory statistics
357
if hasattr(self, "handlers") and self.handlers.inventory:
358
item_count = len(self.handlers.inventory)
359
self.logger.info(f"Generated inventory with {item_count} items")
360
```
361
362
### Template Error Reporting
363
364
Enhanced error reporting in templates:
365
366
```jinja2
367
{# Template: handler.html #}
368
{% set template_logger = get_template_logger("python") %}
369
370
{% if not data %}
371
{{ template_logger.error("No data provided to template") }}
372
<div class="error">No documentation data available</div>
373
{% else %}
374
375
{% if not data.name %}
376
{{ template_logger.warning("Data missing name attribute") }}
377
<div class="warning">Documentation object missing name</div>
378
{% endif %}
379
380
{% for member in data.members %}
381
{% if not member.signature %}
382
{{ template_logger.debug("Member " + member.name + " has no signature") }}
383
{% endif %}
384
{% endfor %}
385
386
{% endif %}
387
```
388
389
### Debug Mode
390
391
Enhanced debugging with verbose logging:
392
393
```python
394
def enable_debug_logging():
395
"""Enable verbose debug logging for troubleshooting."""
396
import logging
397
398
# Set debug level for all mkdocstrings loggers
399
logging.getLogger("mkdocstrings").setLevel(logging.DEBUG)
400
401
# Create debug file handler
402
debug_handler = logging.FileHandler("mkdocstrings_debug.log")
403
debug_handler.setLevel(logging.DEBUG)
404
405
# Format with detailed information
406
formatter = logging.Formatter(
407
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
408
)
409
debug_handler.setFormatter(formatter)
410
411
# Add to root mkdocstrings logger
412
logging.getLogger("mkdocstrings").addHandler(debug_handler)
413
414
# Usage
415
if debug_mode:
416
enable_debug_logging()
417
```
418
419
This logging system provides comprehensive debugging capabilities while maintaining clean, informative output for normal operation.