npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@obsidize/rx-console

v6.2.1

Published

A logging library that is tiny, highly configurable, and platform-agnostic

Downloads

35

Readme

@obsidize/rx-console

A logging library that is tiny, highly configurable, and platform-agnostic.

More configurable than modules like debug, without the restrictiveness / platform-locking of modules like winston.

Highlights

  • Zero dependencies
  • Compatible on anything that runs JavaScript
  • Compact (~7Kb es5 file)

Goals

  • Allow runtime configuration of log levels
  • Ability to intercept log events, so they can be transported to a persistence source
  • Make no assumptions about the persistence layer (i.e. no dependency declarations that lock this module to node or the browser, like node 'fs' or 'stream' packages)
  • Optional integration with RxJS
  • Configurability at each abstraction layer (example code shown further down)

Installation

npm install -P -E @obsidize/rx-console

API

Source documentation can be found here

Usage (NodeJS / Vanilla JavaScript)

The below snippet can be tested with runkit on NPM.

const {
	Logger, 
	LogLevel, 
	getPrimaryLoggerTransport
} = require('@obsidize/rx-console');

// get a reference to the root log event transport
getPrimaryLoggerTransport()
	// set custom filter for what events get emitted
	.setFilter(ev => ev.level >= LogLevel.DEBUG)
	// turn on default global `console` variable usage
	.setDefaultBroadcastEnabled(true);

const logger = new Logger('RunKitLogger');

logger.debug('test');
// "2021-03-15T21:13:42.356Z [DEBUG] [RunKitLogger] test"

const someObj = { 
	myValueIs: 42, 
	isOptionalParam: true, 
	someOtherValue: 'yep' 
};

logger.info('some object info: ', someObj);
// "2021-03-15T21:13:42.360Z [INFO] [RunKitLogger] some object info: "
// Object {myValueIs: 42, isOptionalParam: true, someOtherValue: "yep"}

logger.warn('something unusual happened');
// "2021-03-15T21:13:42.363Z [WARN] [RunKitLogger] something unusual happened

logger.fatal('EXPLOSIONS?!?!?!');
// "2021-03-15T21:13:42.366Z [FATAL] [RunKitLogger] EXPLOSIONS?!?!?!"

logger.verbose('im obnoxious');
// Does not log anything because VERBOSE < DEBUG

Usage (TypeScript)

TypeScript usage is virtually identical to vanilla JavaScript.

Here's an example of using a logger within the context of a class:

import { Logger, LogLevel, getPrimaryLoggerTransport } from '@obsidize/rx-console';

// NOTE: this only needs to be done once, as a part of your app's main setup routine
getPrimaryLoggerTransport()
	.setFilter(ev => ev.level >= LogLevel.DEBUG)
	// can also be fed in a 'prod' flag from your app
	.setDefaultBroadcastEnabled(true);

class MyServiceThing {

	private readonly logger = new Logger('MyServiceThing');

	test(): void {
		this.logger.debug('test!');
	}
}

const service = new MyServiceThing();
service.test(); // 2021-02-16T00:42:20.777Z [DEBUG] [MyServiceThing] test!

Usage (RxJS)

Log events can be redirected to an rxjs stream like so:

import { Observable, fromEventPattern, inverval } from 'rxjs';
import { buffer, map, filter } from 'rxjs/operators';
import { LogEvent, getPrimaryLoggerTransport, stringifyLogEvent } from '@obsidize/rx-console';

// no-op function for example purposes
const writeToFile = (..._args: any[]) => { };

// combine the buffered events into a single string
const serializeEvents = (events: LogEvent[]): string => {
	// customize this however you want
	return events.map(ev => stringifyLogEvent(ev)).join('\n') + '\n';
};

// Wraps the transport's EventEmitter as an rxjs observable using 
// the `fromEventPattern` rxjs creator function.
const eventStream = getPrimaryLoggerTransport()
	.events() // get a reference to the transport's EventEmitter instance
	.asObservable<Observable<LogEvent>>(fromEventPattern);

eventStream.pipe(

	// required if event caching is not disabled
	// (caching is enabled by default)
	map((event: LogEvent) => event.clone()),

	// accumulate log events for 1 second
	buffer(interval(1000)),

	// if we didn't get any new logs after 5 seconds, just skip this round
	filter((events: LogEvent[]) => events.length > 0),

	// stringify and concatenate the buffered events
	map((events: LogEvent[]) => serializeEvents(events))

).subscribe(outputString => {

	// Dump the buffer to a file, or send to a server, or wherever.
	writeToFile(outputString);
});

Runtime Event Filtering

Both LoggerTransport and Logger extend the LogEventGuardContext class, which allows you to suppress events both on the transport side (shared between loggers that use the transport) and on the logger side (one-off filtering).

import { Logger, LogLevel, getPrimaryLoggerTransport } from '@obsidize/rx-console';

getPrimaryLoggerTransport()
	.setFilter(ev => ev.level >= LogLevel.DEBUG);

const logger1 = new Logger('TestLogger')
	.setFilter(ev => ev.level >= LogLevel.TRACE);

const logger2 = new Logger('TestLogger')
	.setFilter(ev => ev.level >= LogLevel.INFO);

logger1.trace('trace test'); // suppressed because the transport's guard caught it
logger1.debug('debug test'); // "debug test"

logger2.debug('debug test'); // suppressed because this logger's guard caught it
logger2.info('info test'); // "info test"

logger2.setEnabled(false);
logger2.error('BOOM!!!'); // suppressed because the logger is disabled

Custom Serialization

The default serialization options can be configured via the shared Config instance in this module:

import { Config } from '@obsidize/rx-console';

const sharedConfig = Config.sharedInstance;
sharedConfig.parameterSeparator = ', '; // join separator for additional log arguments (default ' :: ')
sharedConfig.stringifyMaxLength = 2000; // set 2000 characters before truncation (default 250)
sharedConfig.levelNameMap.update({ CUSTOM_LEVEL_NAME: 123 }); // Uses `LogLevelNameMap.main` by default

// optionally reset to defaults if needed
sharedConfig.reset();

Custom Extensions

This module is fully customizable via class extensions:

import { LogEvent, LoggerTransport, Logger, getPrimaryLoggerTransport } from '@obsidize/rx-console';

// Create a custom event type
class CustomLogEvent extends LogEvent {

	// Add some special sauce to your custom event instances.
	specialSauceData: number = 42;
}

// Create a custom transport that knows how to create your custom event type
class CustomTransport extends LoggerTransport {

	// Create a main instance for your loggers to report back to
	public static readonly main = new CustomTransport();

	// override to return your custom event type
	public createEvent(level: number, tag: string, message: string, params: any[]): CustomLogEvent {
		return new CustomLogEvent(level, tag, message, params);
	}
}

// Create a custom logger that uses your custom transport as the default
class CustomLogger extends Logger {

	constructor(
		name: string,
		transport: CustomTransport = CustomTransport.main
	) {
		super(name, transport);
	}
}

const transport = CustomTransport.main;

transport.addListener((ev: CustomLogEvent) => {
	console.log(ev.message); // 'custom log'
	console.log(ev.specialSauceData); // 42
});

// NOTE: You can also wire your custom transport back into the default instance
transport.pipeToDefault();

const logger = new CustomLogger('TestLogger');
logger.info('custom log');

// you can also break the connection to the default instance later on
transport.unpipeFromDefault();

Examples