@tsjam/logger

@tsjam/logger

Vanilla TypeScript Logger

Not opinionated ts Logger with Multiple output channels 🍰
Useful for parallel console output & remote monitoring 👩‍🚀

MIT licensed npm version multichannel hashtags timestamps

Advantages:

  • appId (distinguish log between multiple app instances)
  • timestamps (milliseconds matter)
  • hashtags (tag child loggers, find and filter certain logs super-fast)
  • multiple channels output (add ur own output: e.g. parallel console output & remote monitoring)
  • metadata (for all log entries per appId or per call e.g. { userId: 007 })
  • buffering (useful for crash reporting)
  • fully customizable (use your own log format)
  • fair Errors serialization into string (check JSON.stringify(new Error('Oops')); // {})
  • sanitization of sensitive fields (perf optimized, Logs.sanitize({ password: 'ABC' }))
  • safe stringify payload at any moment (Logs.stringify(data))
  • trim stack to number of lines or fully cut (use config { trimStack: 2 })
  • zero third-party dependencies

Output example:
[app161125][2024-01-21T18:33:02.981Z][info][#user] Logged In: { username: Bob, password: '***' }

Installation

npm install @tsjam/logger

Usage

ConsoleOutput is the default output channel. Specify Ur own ones if needed...

import { jamLogger } from '@tsjam/logger';

jamLogger.info('Hello Logger!');
// [app161125][2024-01-21T18:33:02.981Z][info] Hello Logger!

Tag child loggers to easily filter logs by tags.

const logger = jamLogger.tagged('user'); // child logger with added tags

logger.info('Greetings for', { name: 'Bob' });
// [app161125][2024-01-21T18:33:02.981Z][info][#user] Greetings for { name: 'Bob' }
const logger = JamLogger.create({
appId: `ioApp${Date.now()}`,
channels: [...defaultOutputChannels /* { out: MyKibanaOutput } */], // default is ConsoleOutput
});
const aiLogger = logger.tagged('ai'); // child logger with #ai tag
const myOutput: LogOutput = {
write: ({ appId, date, level, message, data, context }: LogEntry) => {
// Format raw log entry and send it anywhere U wish
},
};

const logger = JamLogger.create({
channels: [...defaultOutputChannels, { out: myOutput }, { out: myKibanaOutput }],
});

Use simplistic BufferOutput to buffer logs for any crash reporting or remote monitoring. Do not forget to flush after report is sent.

const logBuffer = new BufferOutput(2000);
const logger = JamLogger.create({
channels: [...defaultOutputChannels, { out: logBuffer }],
});

Metadata is especially useful for remote reporting & monitoring.

import { JamLogger } from '@tsjam/logger';

const logger = JamLogger.create({
metadata: { userId: '007' }, // use it however U wish in ur output channel next to log entry
});
//...
JamLogger.updateMeta(logger.appId, { userId: '456' }); // update metadata
logger.info('Lets Play 🦑');
// [app1737064023840][2025-01-16T21:47:03.840Z][info] Lets Play 🦑
// meta: {"userId":"456"}

Pass additional meta per call.

const logger = JamLogger.create({
metadata: { userId: '007' }, // use it however U wish in ur output channel next to log entry
});
logger.info('Whats Up?', LogMeta.bake({ drink: 'dry martini 🍸' }));
// [app170723][2025-01-16T23:10:56.102Z][info] Whats Up?
// meta: { "userId": "007", "drink": "dry martini 🍸" }
jamLogger.debug('Logged in', Loges.sanitize({ name: 'Bob', password: 'ABC' }));
// [app170723][2024-02-06T16:47:56.398Z][debug] Logged in { name: 'Bob', password: '***' }

Note: to always sanitize sensitive fields use sanitizeSensitiveTranslator config option.
Yet it's more perf optimized to sanitize only when needed.

Stack is shown on Error payloads (similar to console.log);

jamLogger.warn('Oops!', new Error('Something went wrong'));
// [app170723][2024-02-06T17:02:00.108Z][warn] Oops Error: Something went wrong
// at Object.<anonymous> (...tsjam-logger/tests/logging/log.utils.spec.ts:10:49)
// at Promise.then.completed (...tsjam-logger/node_modules/jest-circus/build/utils.js:298:28)
// ...

Hide stack on Error payloads for specified levels

const logger = JamLogger.create({ errorStackLevel: LogLevel.Error }); // default WARN

logger.warn('Oops!', new Error('Something went wrong'));
// [app170723][2024-02-06T17:02:00.108Z][warn] Oops Error: Something went wrong

Trim stack to few lines

const logger = JamLogger.create({ trimStack: 2 });
logger.warn('Oops!', new Error('Spoiled the milk!'));
// [app170723][2024-02-06T17:13:59.496Z][warn] Oops! Spoiled the milk! Stack:
// at Object.<anonymous> (...tsjam-logger/tests/logging/log.utils.spec.ts:10:15)
// ...

Note: it's also possible to trim the stack to specified depth via trimStack


There are some built-in translators for log data u could use while baking ur own logger:

  • jsonStringifyTranslator – safely stringify log data Logs.stringify(data)
  • stringifyErrorStackTranslator – fairly serialize Error correctly Logs.stringifyError(error)
  • sanitizeSensitiveTranslator - sanitize sensitive fields defaults: ['password', 'token', 'secret', 'sessionId']

Note: These translators applied either to a Single log call or to All logs by default, U could add Ur own too.

Note: U could add custom translator if U need one for all channels transformation. Otherwise, it's recommended to process raw logEntry in Ur particular output channel.


@tsjam/logger is MIT licensed


TSJam Logger Documentation

NPM package

@seeAlso

TSJam API Documentation