docs: add user guide, cookbook, and rewrite README
This commit is contained in:
parent
741b959820
commit
1a74f2afa4
3 changed files with 621 additions and 54 deletions
|
|
@ -1,37 +1,216 @@
|
|||
# Using logging in multiple modules
|
||||
# Logging cookbook
|
||||
|
||||
Multiple calls to `logging.getLogger('someLogger')` return a reference to the
|
||||
same logger object. This is true not only within the same module, but also
|
||||
across modules as long as it is in the same Python interpreter process. It is
|
||||
true for references to the same object; additionally, application code can
|
||||
define and configure a parent logger in one module and create (but not
|
||||
configure) a child logger in a separate module, and all logger calls to the
|
||||
child will pass up to the parent. Here is a main module:
|
||||
Recipes and patterns for common logging tasks with `@administratrix/esm-logging`.
|
||||
|
||||
``javascript
|
||||
import * as logging from 'eslib/logging';
|
||||
import * as my_module from './my_module';
|
||||
## Using logging across multiple modules
|
||||
|
||||
// create logger with 'spam_application'
|
||||
var logger = logging.getLogger('spam_application');
|
||||
logger.setLevel(logging.DEBUG);
|
||||
`getLogger` returns the same logger instance for a given scope name across
|
||||
your entire application. Configure handlers on a parent logger and create
|
||||
child loggers in each module:
|
||||
|
||||
// create file handler which logs even debug messages
|
||||
var fh = logging.FileHandler('spam.log')
|
||||
fh.setLevel(logging.DEBUG);
|
||||
```javascript
|
||||
// main.js
|
||||
import * as logging from '@administratrix/esm-logging';
|
||||
import { ConsoleHandler } from '@administratrix/esm-logging/src/handler';
|
||||
import { Formatter } from '@administratrix/esm-logging/src/formatter';
|
||||
import { doWork } from './worker.js';
|
||||
|
||||
// create console handler with a higher log level
|
||||
var ch = logging.StreamHandler();
|
||||
ch.setLevel(logging.ERROR);
|
||||
const logger = logging.manager.MANAGER.getLogger('myapp');
|
||||
logger.setLevel(logging.log_level.DEBUG);
|
||||
|
||||
// create formatter and add it to the handlers
|
||||
var formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s');
|
||||
fh.setFormatter(formatter);
|
||||
ch.setFormatter(formatter);
|
||||
const handler = new ConsoleHandler();
|
||||
handler.level = logging.log_level.DEBUG;
|
||||
handler.formatter = new Formatter({
|
||||
fmt: '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
});
|
||||
logger.addHandler(handler);
|
||||
|
||||
// add the handlers to the logger
|
||||
logger.addHandler(fh);
|
||||
logger.addHandler(ch);
|
||||
|
||||
logger.info('creating an instance of auxiliary_module.Auxiliary')
|
||||
logger.info('Application starting');
|
||||
doWork();
|
||||
logger.info('Application finished');
|
||||
```
|
||||
|
||||
```javascript
|
||||
// worker.js
|
||||
import * as logging from '@administratrix/esm-logging';
|
||||
|
||||
const logger = logging.manager.MANAGER.getLogger('myapp.worker');
|
||||
|
||||
export function doWork() {
|
||||
logger.debug('Starting work');
|
||||
// ... do something ...
|
||||
logger.info('Work completed');
|
||||
}
|
||||
```
|
||||
|
||||
Because `myapp.worker` is a child of `myapp`, its records propagate up to the
|
||||
handler configured on `myapp`. No handler configuration is needed in
|
||||
`worker.js`.
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
2026-03-14 10:00:00.000 - myapp - INFO - Application starting
|
||||
2026-03-14 10:00:00.001 - myapp.worker - DEBUG - Starting work
|
||||
2026-03-14 10:00:00.002 - myapp.worker - INFO - Work completed
|
||||
2026-03-14 10:00:00.003 - myapp - INFO - Application finished
|
||||
```
|
||||
|
||||
## Logging to multiple destinations
|
||||
|
||||
Attach multiple handlers to a single logger, each with its own level and
|
||||
formatter:
|
||||
|
||||
```javascript
|
||||
import * as logging from '@administratrix/esm-logging';
|
||||
import { ConsoleHandler, StderrHandler } from '@administratrix/esm-logging/src/handler';
|
||||
import { Formatter } from '@administratrix/esm-logging/src/formatter';
|
||||
|
||||
const logger = logging.manager.MANAGER.getLogger('myapp');
|
||||
logger.setLevel(logging.log_level.DEBUG);
|
||||
|
||||
// console handler: shows everything with detailed format
|
||||
const consoleHandler = new ConsoleHandler();
|
||||
consoleHandler.level = logging.log_level.DEBUG;
|
||||
consoleHandler.formatter = new Formatter({
|
||||
fmt: '%(asctime)s [%(levelname)s] %(name)s: %(message)s',
|
||||
});
|
||||
logger.addHandler(consoleHandler);
|
||||
|
||||
// stderr handler: only errors, compact format
|
||||
const errorHandler = new StderrHandler(logging.log_level.ERROR);
|
||||
errorHandler.formatter = new Formatter({
|
||||
fmt: 'ERROR %(asctime)s %(name)s: %(message)s',
|
||||
datefmt: '%Y-%m-%d %H:%M:%S',
|
||||
});
|
||||
logger.addHandler(errorHandler);
|
||||
```
|
||||
|
||||
## Using basicConfig for simple scripts
|
||||
|
||||
For quick scripts, `basicConfig` sets up a handler on the root logger:
|
||||
|
||||
```javascript
|
||||
import * as logging from '@administratrix/esm-logging';
|
||||
|
||||
logging.config.basicConfig({
|
||||
level: logging.log_level.INFO,
|
||||
format: '%(levelname)s: %(message)s',
|
||||
});
|
||||
|
||||
const logger = logging.manager.MANAGER.getLogger('script');
|
||||
logger.info('Running');
|
||||
logger.warning('Check this');
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
INFO: Running
|
||||
WARNING: Check this
|
||||
```
|
||||
|
||||
## Filtering records
|
||||
|
||||
### By scope
|
||||
|
||||
Only allow records from a specific part of the hierarchy:
|
||||
|
||||
```javascript
|
||||
import { Filter } from '@administratrix/esm-logging/src/filter';
|
||||
|
||||
const dbFilter = new Filter('myapp.db');
|
||||
handler.addFilter(dbFilter);
|
||||
// handler now only processes records from 'myapp.db' and its children
|
||||
```
|
||||
|
||||
### By custom criteria
|
||||
|
||||
Use any object with a `filter(record)` method:
|
||||
|
||||
```javascript
|
||||
handler.addFilter({
|
||||
filter(record) {
|
||||
// only pass records that contain 'important' in the message
|
||||
return record.msg.includes('important');
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Custom formatters
|
||||
|
||||
Create formatters with different format strings for different contexts:
|
||||
|
||||
```javascript
|
||||
import { Formatter } from '@administratrix/esm-logging/src/formatter';
|
||||
|
||||
// detailed format for development
|
||||
const devFormatter = new Formatter({
|
||||
fmt: '%(asctime)s %(levelname)s %(name)s: %(message)s',
|
||||
datefmt: '%H:%M:%S',
|
||||
});
|
||||
|
||||
// compact format for production
|
||||
const prodFormatter = new Formatter({
|
||||
fmt: '%(levelname)s:%(name)s:%(message)s',
|
||||
});
|
||||
```
|
||||
|
||||
## Custom handlers
|
||||
|
||||
Subclass `Handler` and implement `emit(record)`:
|
||||
|
||||
```javascript
|
||||
import { Handler } from '@administratrix/esm-logging/src/handler';
|
||||
|
||||
class BufferHandler extends Handler {
|
||||
constructor(level) {
|
||||
super(level);
|
||||
this.buffer = [];
|
||||
}
|
||||
|
||||
emit(record) {
|
||||
try {
|
||||
this.buffer.push(this.format(record));
|
||||
if (this.buffer.length >= 100) {
|
||||
this.flush();
|
||||
}
|
||||
} catch (e) {
|
||||
this.handleError(record);
|
||||
}
|
||||
}
|
||||
|
||||
flush() {
|
||||
// send buffered records somewhere
|
||||
const batch = this.buffer.splice(0);
|
||||
// ... process batch ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Disabling logging below a threshold
|
||||
|
||||
The manager's `disable` property suppresses all logging at or below a given
|
||||
level across all loggers:
|
||||
|
||||
```javascript
|
||||
import * as logging from '@administratrix/esm-logging';
|
||||
|
||||
// suppress DEBUG and INFO globally
|
||||
logging.manager.MANAGER.disable = logging.log_level.INFO;
|
||||
```
|
||||
|
||||
## Reconfiguring with force
|
||||
|
||||
`basicConfig` only takes effect once. To reconfigure, use `force: true`:
|
||||
|
||||
```javascript
|
||||
logging.config.basicConfig({
|
||||
level: logging.log_level.DEBUG,
|
||||
format: '%(asctime)s %(message)s',
|
||||
force: true,
|
||||
});
|
||||
```
|
||||
|
||||
This removes and closes all existing handlers on the root logger before
|
||||
applying the new configuration.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue