An isomorphic, opinionated logging library that focuses on:
- Simplicity: zero runtime dependencies, ~500 LoCs
- Readability: produces plain-text, human-readable output
- Performance: uses asynchronous logging techniques when possible
- Child loggers: supports chained child loggers using prefix concatenation
- Isomorphism: supports browsers and server-side runtimes
- ESM: ships with separate ESM and CommonJS builds
- Flexibility: log level can be changed at runtime
In many areas this library stands opposite to pino, hence the name.
-etto
forms nouns from nouns, denoting a diminutive
import pinetto from 'pinetto';
const { default: pinetto } = require('pinetto');
const root = pinetto({ level: 'debug' });
root.info('Hello, %s!', 'World');
// => 2024-09-09T19:05:28.884Z INF Hello, World!
const child = logger.child('[foo]');
child.debug('Hello, %s!', () => 'World');
// => 2024-09-09T19:06:02.643Z INF [foo] Hello, World!
const grandchild = child.child('[bar]');
grandchild.debug('Hello, %s!', 'World');
// => 2024-09-09T19:06:02.643Z INF [foo][bar] Hello, World!
// The log level can be changed at runtime and the change
// propagates to child loggers.
logger.level = 'warn';
child.info('Hello, world!');
// => <prints nothing>
Option | Description | Default value |
---|---|---|
level |
Starting log level, one of "trace" , "debug" , "info" , "warn" , "error" |
"info" |
writer |
Log writer function (see below) | Depends on the environment |
datetime |
A function that returns a date/time string (see below) | datetimeISO |
printf
-style syntax is supported:
const logger = pinetto({ level: 'debug' });
logger.info('Hello, %s!', 'World');
The datetime
option may be used to customize whether and how each log line
will be prefixed with a date/time string:
const logger = pinetto({
level: 'debug',
datetime: () => `${new Date().getFullYear()} `,
});
logger.info('Hello, %s!', 'World');
// => 2024 INF Hello, World!
Pinetto ships with two datetime functions: datetimeVoid
, which returns an
empty string, and datetimeISO
, which returns an ISO 8601 string
(well, technically it's the RFC 3339 string returned by
Date#toISOString
).
Note that datetime functions must return a string that ends with a space character.
If a log argument is provided in the form of a function it will invoked only when the log triggers and its return value will be passed to the formatter.
This helps with reducing the number of expensive serialization operations,
such as JSON.stringify()
, taking place even when the log level is such
that the result of the serialization will never be used:
logger.level = 'warn';
logger.info('Foo %s', () => JSON.serialize({ bar: 42 }));
// JSON.serialize() will never be invoked
Pinetto ships with three different writers:
-
ConsoleWriter
, which falls back ontoconsole.log()
and works pretty much everywhere; -
ProcessWriter
, which falls back ontoprocess.stdout.write()
and can only be used in Node.js-like environments; -
BufferedWriter
, which buffers entries and periodically flushes its buffer out usingprocess.stdout.write()
(when in Node.js-like environments) orconsole.log()
(everywhere else).
By default, pinetto will use ProcessWriter
in Node.js-like environments and
ConsoleWriter
everywhere else. A custom writer can be set via the respective
constructor option:
import pinetto, { BufferedWriter } from 'pinetto';
const logger = pinetto({ level: 'debug', writer: new BufferedWriter() });
Pinetto is released under the MIT license.
The following packages have been vendored into pinetto, although slowly diverging from the respective sources:
- boolean at commit 6af03e40f55ed848eb2d2f84c9d11cd629a94b6d (BSD-3-CLAUSE)
- fast-printf at commit 8372e5cbc7d4f16a655fd4c42077db0147c077af (MIT)