Annal is a lightweight logger library for JavaScript, with no dependencies and only 3 KB in size.
Read this in other languages: English | 简体中文
Important
Annal is a pure ESM package, if you run into trouble using it in your project, see this guide.
npm install annal<script type="module">
import { journal } from 'https://esm.sh/annal';
</script>or
<script type="importmap">
{
"imports": {
"annal": "https://esm.sh/annal"
}
}
</script>
<script type="module">
import { journal } from 'annal';
</script>You may also use other CDNs of your choice, such as jsDelivr and UNPKG.
import { journal } from 'annal';
journal.info('hello world');[1970-01-01T00:00:00.000Z] INFO - hello worldAnnal selects the matching underlying console method (debug / info / warn / error) based on the record's level. Arguments are forwarded as-is, and if they include arrays or objects, they are automatically formatted by the JavaScript runtime in use.
Since arguments are forwarded directly to console, string substitution is also available:
import { journal } from 'annal';
journal.info('hello %s', 'world');A Journal instance exposes four built-in level methods and a generic log method:
import { journal, LevelInfo } from 'annal';
// → console.debug
journal.debug('detailed trace');
// → console.info
journal.info('hello');
// → console.warn
journal.warn('slow query');
// → console.error
journal.error('connection lost');
// custom log record
journal.log(LevelInfo + 2, 'notice');| Method | Description |
|---|---|
debug(...args) |
Emit at LevelDebug, dispatched to console.debug |
info(...args) |
Emit at LevelInfo, dispatched to console.info |
warn(...args) |
Emit at LevelWarn, dispatched to console.warn |
error(...args) |
Emit at LevelError, dispatched to console.error |
log(level, ...args) |
Used for emitting records at custom levels |
In addition, instances expose one derivation method:
withScope(name, options?): Derive a sub-scope instance. Scope names are joined with:, and the minimum level may be optionally overridden.
Records below an instance's minimum level are silently discarded.
You may use the createJournal factory or the Journal class to construct a new instance:
import { createJournal, LevelInfo } from 'annal';
const logger = createJournal({ scope: 'app', level: LevelInfo });
logger.info('hello world');import { createJournal, Journal, LevelInfo } from 'annal';
const logger = new Journal({ scope: 'app', level: LevelInfo });
logger.info('hello world');[1970-01-01T00:00:00.000Z] INFO app - hello worldThe two forms are completely equivalent. Journal instances are immutable, and using withScope to derive a sub-scope instance leaves the original untouched:
import { createJournal } from 'annal';
const app = createJournal({ scope: 'app' });
// scope => 'app:db'
const db = app.withScope('db');
// scope => 'app:db:sql'
const sql = db.withScope('sql');
db.error('connection lost');
sql.warn('slow query');Annal's level model follows Go's log/slog, using integers rather than a log4js-style string enum.
type Level = number;
const LevelDebug: Level = -4;
const LevelInfo: Level = 0;
const LevelWarn: Level = 4;
const LevelError: Level = 8;A Level is simply an integer: the larger the value, the more severe the event. Beyond the four built-in constants, any integer is a valid level, so the level scheme is entirely up to the consumer:
import { createJournal, LevelDebug, LevelError } from 'annal';
const LevelTrace = LevelDebug - 4;
const LevelFatal = LevelError + 4;
const logger = createJournal({ level: LevelTrace });
logger.log(LevelTrace, 'hello world');
logger.log(LevelFatal, 'goodbye universe');When using a custom level, the label is rendered with an offset, matching slog:
[1970-01-01T00:00:00.000Z] DEBUG-4 - hello world
[9999-12-31T23:59:59.999Z] ERROR+4 - goodbye universeTraditional JavaScript logging libraries typically include a built-in silent level, but Annal does not. Silencing is essentially raising the minimum level high enough to discard all records; introducing a new level would break the existing design. If you need one, define it yourself:
import { createJournal, LevelDebug } from 'annal';
const LevelSilent = Number.POSITIVE_INFINITY;
const app = createJournal();
const db = app.withScope('db', {
level: import.meta.env.DEV ? LevelDebug : LevelSilent,
});
// only emitted in development
db.error('connection lost');type Level = number;
interface JournalOptions {
// minimum level, default LevelInfo (0)
level?: Level;
// scope prefix, default '' (no prefix)
scope?: string;
}