or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

handler-system.mdindex.mdinventory-system.mdlogging-system.mdmarkdown-processing.mdplugin-integration.mdrendering-system.md

logging-system.mddocs/

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.