@metamask/logger
Version:
A lightweight logging package using @metamask/streams
135 lines • 5.97 kB
JavaScript
/**
* A Logger is a logging facility that supports multiple transports and tags.
* The transports are the actual logging functions, and the tags are used to
* identify the source of the log message independent of its location in the
* code.
*
* @example
* ```ts
* const logger = new Logger('my-logger');
* logger.info('Hello, world!');
* >>> [my-logger] Hello, world!
* ```
*
* Sub-loggers can be created by calling the `subLogger` method. They inherit
* the tags and transports of their parent logger, and can add additional tags
* to their own messages.
*
*
* @example
* ```ts
* const subLogger = logger.subLogger('sub');
* subLogger.info('Hello, world!');
* >>> [my-logger, sub] Hello, world!
* ```
*
* The transports can be configured to ignore certain log levels, or to write
* different tags to different destinations, and so on. The default transports
* write to the console, but other transports can be added by passing a custom
* transport function to the constructor. The transports must be synchronous,
* but they can initiate asynchronous operations if needed.
*
* @example
* ```ts
* const logger = new Logger({
* tags: ['my-logger'],
* transports: [
* (entry) => {
* if (entry.tags.includes('vat')) {
* fs.writeFile('vat.log', `${entry.message}\n`, { flag: 'a' }).catch(
* (error) => {
* console.error('Error writing to vat.log:', error);
* },
* );
* }
* },
* ],
* });
* ```
*/
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _Logger_instances, _Logger_options, _Logger_dispatch;
import { parseOptions, mergeOptions } from "./options.mjs";
import { lunser } from "./stream.mjs";
// We make use of harden() if it exists, but we don't want to fail if it doesn't.
const harden = globalThis.harden ?? ((value) => value);
/**
* The logger class.
*/
export class Logger {
/**
* The constructor for the logger. Sub-loggers can be created by calling the
* `subLogger` method. Sub-loggers inherit the transports and tags of their
* parent logger.
*
* @param options - The options for the logger, or a string to use as the
* logger's tag.
* @param options.transports - The transports, which deliver the log messages
* to the appropriate destination.
* @param options.level - The log level for the logger, used as a default
* argument for the transports.
* @param options.tags - The tags for the logger, which are accumulated by
* sub-loggers and passed to the transports.
*/
constructor(options = undefined) {
_Logger_instances.add(this);
_Logger_options.set(this, void 0);
__classPrivateFieldSet(this, _Logger_options, parseOptions(options), "f");
// Create aliases for the log methods, allowing them to be used in a
// manner similar to the console object.
const bind = (level) => harden(__classPrivateFieldGet(this, _Logger_instances, "m", _Logger_dispatch).bind(this, {
...__classPrivateFieldGet(this, _Logger_options, "f"),
level,
}));
this.log = bind('log');
this.debug = bind('debug');
this.info = bind('info');
this.warn = bind('warn');
this.error = bind('error');
}
/**
* Creates a sub-logger with the given options.
*
* @param options - The options for the sub-logger, or a string to use as the
* sub-logger's tag.
* @returns The sub-logger.
*/
subLogger(options = {}) {
return new Logger(mergeOptions(__classPrivateFieldGet(this, _Logger_options, "f"), typeof options === 'string' ? { tags: [options] } : options));
}
/**
* Injects a stream of log messages into the logger.
*
* @param stream - The stream of log messages to inject.
* @param onError - The function to call if an error occurs while draining
* the stream. If not provided, the error will be lost to the void.
*/
injectStream(stream, onError) {
stream
.drain(async ({ params }) => {
const [, args] = params;
const { level, tags, message, data } = lunser(args);
const logArgs = message === undefined ? [] : [message, ...(data ?? [])];
__classPrivateFieldGet(this, _Logger_instances, "m", _Logger_dispatch).call(this, { level, tags }, ...logArgs);
})
.catch((problem) => onError?.(problem));
}
}
_Logger_options = new WeakMap(), _Logger_instances = new WeakSet(), _Logger_dispatch = function _Logger_dispatch(options, ...args) {
const { transports, level, tags } = mergeOptions(__classPrivateFieldGet(this, _Logger_options, "f"), options);
const [message, ...data] = args;
const entry = harden({ level, tags, message, data });
transports.forEach((transport) => transport(entry));
};
harden(Logger);
//# sourceMappingURL=logger.mjs.map