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

structured-log-dev

v0.0.10

Published

(Development version). NodeJS structured logging framework based on Serilog.

Downloads

7

Readme

#structured-log

A structured logging framework for Javascript. Based on Serilog.

WARNING: structured-log is a work in progress and will change over time.

Installation

Server-side (Node.js) via npm

npm install --save structured-log

Client-side (Web Browser) via Bower

bower install --save structured-log

Basic Setup

This section describes the most basic structured-log configuration for server and client.

Server-side

In your NodeJS script:

var structuredLog = require('structured-log');
var coloredConsoleSink = require('structured-log/colored-console-sink');

var log = structuredLog.configuration()
	.writeTo(coloredConsoleSink())
    .createLogger();

Client-side

In your HTML file:

  <script type='text/javascript' src='bower_components/structured-log/structured-log.min.js />

In your Javascript code:

var log = structuredLog.configuration() 
	.writeTo(structuredLog.consoleSink())
    .createLogger();

Multiple sinks

Setup of structured-log is via a fluent API that configures and creates a logger. One example of this is to specify multilple sinks, eg:

var log = structuredLog.configuration() 
	.writeTo(consoleSink)
	.writeTo(httpSink({ url: '<some-url>' }))
	.createLogger();

Writing to another log

A log can easily be piped to another log:

var someOtherLog = structuredLog.configuration()
	// ... setup ...
	.createLogger(); 

var log = structuredLog.configuration() 
	.writeTo(consoleSink)
	.writeTo(someOtherLog)
	.createLogger();

Basic Usage

Debugging:

log.trace('My debug message!');
log.debug('My debug message!');
log.verbose('My debug message!');

Information:

log.info('Something happened in the application...');

Information alternative:

log('Something happened in the application...');

Warnings:

log.warn('Some not-fatal error happened...');

Errors:

log.error('Something bad happened...');
log.error(exceptionOrErrorObject, 'Something bad happend...');

Structured Logging

All the logging functions accept a message template and a set of key/value properties that are used to render the template when constring the log message for display. The properties are maintained separately to the template and rendered message which is what makes this a structured logging system.

Here are some examples that have been adapted for Javascript from the Serilog C# examples:

var position = { Latitude: 25, Longitude: 134 };
var elapsedMs = 34;

log.info("Processed {@Position} in {Elapsed:000} ms.", {
	Position: position,
	Elapsed: elapsedMs
});

Properties can also be specified by positional parameters, the same as how it works in Serilog C#:

log.info("Processed {@Position} in {Elapsed:000} ms.", position, elapsedMs);

Included Sinks

A sink is a plugin that is invoked for each log event. Usualy a sink defines an output method for logs, such as the ability to output to the console.

structured-log includes a number of built-in sinks.

Server-side

All sinks are imported using the Nodejs require function as follows:

var someSink = require('<sink-name>');

| Name | Description | Batched/Unbatched | | ---- | ----------- | ----------------- | | console-sink | Writes formatted log events to the console | Unbatched | | colored-console-sink | Same as above, but with colors | Unbatched | | json-console-sink | Writes structured json to the console for each log event | Unbatched | | stream-sink | Writes formatted log events to a Nodejs stream | Unbatched | | json-stream-sink | Writes structured json tot he console for each log event | Unbatched | | http-sink | Outputs structured json log events via HTTP post | Batched |

Client-side

| Name | Description | | ---- | ------------- | | console-sink | Writes formatted log events to the console | Unbatched | | json-console-sink | Writes structured json to the console for each log event | Unbatched | | http-sink | Outputs structured json log events via HTTP post | Batched |

Batching

Some of the sinks are batched. Batched sinks process multiple log events at once usually for increased performance or to reduce timing issues (eg HTTP logs being received out of order).

Configuring Batched Sinks

All batched sinks (even custom batched sinks) have the same standard configuration options.

var httpSink = require('structured-log-http-sink'); 

var log = structuredLog.configuration()
	.writeTo(httpSink({
		url: 'http://somelogreceiver',    // Configuration for the custom sink.
		batchSize: 1000,          // Flush the queue every 1000 logs.
		timeDuration: 3000,         // Milliseconds to wait before flushing the queue.            
	})
	.createLogger();

batchSize specifies the amount of logs to include in a batch. When this number of logs are in the queue the queue will be flushed and processed by the sink.

timeDuration specifies the amount of time that will pass before the log queue is flushed. This ensure that the queue is periodically flushed even if not enought logs events have been queued to trigger the batchSize flush.

Either of these options can be omitted and be set to default values.

Flushing Queued Logs

The queue of batched logs can be flushed at any time by calling the flush function.

If it suits your purpose you can simply call flush:

log.flush();

If you need a callback when the flush has completed you have two options.

The first option is the standard Javascript-style callback:

log.flush(function (err) {
	if (err) {
		// An error occurred while flushing.
	}
	else {
		// The queue was flushed successfully.
	}
});

The second option is to use the promise that is returned by flush:

log.flush()
	.then(function () {
		// The queue was flushed successfully.
	})
	.catch(function (err) {
		// An error occurred while flushing.
	})
	.done(); // Terminate the promise chain.

3rd-party Sinks

A number of additional sinks are available as separate packages.

If you release your own custom sink for structured-log please let us know and we'll add it to the list!

Server-side (via npm)

| Name | Description | | ---- | ------------- | | email-sink | Outputs formattted log messages via SMTP | | mongodb-sink | Writes structure json log events to the MongoDB database |

Client-side (via bower)

| Name | Description | | ---- | ------------- | | websockets-sink | Outputs formattted log messages via websockets | | socketio-sink | Outputs formattted log messages via the Socket.IO library |

Make your own sink

It is very easy to make your own sink. You first have to decide if the sink should process log events individually or as a batch.

There are plenty of built-in examples of sinks. So can you can always copy and modify an existing sink.

Non-batched custom sink

Non-batched sinks process each log event individually:

var myCustomSink = function (options) {
	return {
		emit: function (logEvent) {
			//
			// ... your custom log event processing goes here ...
			//
		}
	};
};

var customSinkOptions = {
	// Whatever custom options you need...
};

var log = structuredLog.configure()
	.writeTo(myCustomSink(customSinkOptions))
	.createLogger();

As a Nodejs module

MyCustomSink.js:

module.exports = function (options) {
	return {
		emit: function (logEvent) {
			//
			// ... your custom log event processing goes here ...
			//
		}
	};
};

SomewhereElse.js:

var myCustomSink = require('./MyCustomSink');

var customSinkOptions = {
	// Whatever custom options you need...
};

var log = structuredLog.configure()
	.writeTo(myCustomSink(customSinkOptions))
	.createLogger();

Batched custom sink

Batched sinks process a batch of log events at a time. structured-log buffers log events until the log queue is flushed. By simply replacing the emit function with emitBatch you can convert your sink to work in batched mode, accepting an array of log events instead of just a single log event.

var myCustomSink = function (options) {
	return {
		emitBatch: function (logEvents) {
			//
			// ... process the array of log events ...
			//
		}
	};
};

Advanced Setup

The fluent API has numerous functions to configure your log.

Log Levels

Set the minimum log level that is output:

var log = structuredLog.configuration()
	.minimumLevel('WARN')
	.writeTo(consoleSink())
	.createLogger();

minimumLevel applies to subsequent sinks in the configuration, so you can use it to set a different level for each sink:

var log = structuredLog.configuration()
	.minimumLevel('VERBOSE')
	.writeTo(consoleSink())
	.minimumLevel('INFO')
	.writeTo(httpSink())
	.minimumLevel('ERROR')
	.writeTo(emailSink())
	.createLogger();

Filtering

Custom filtering can be applied to include/exclude logging based on a predicate function.

var log = structuredLog.configuration()
	.filter(function (logEvent) {
		return someCondition(logEvent);
	})
	.writeTo(consoleSink())
	.createLogger();

This kind of filtering affects subsequent sinks in the configuration, you can use it in combination with clearFilter to provide different filters for different sinks:

var log = structuredLog.configuration()
	.filter(function (logEvent) {
		return okForConsole(logEvent);
	}))
	.writeTo(consoleSink())
	.resetFilter()
	.filter(function (logEvent) {
		return okForHttp(logEvent);
	}))
	.createLogger();

Logs can also be filtered after configuration, this effectively creates a new log with the added filter:

var log2 = log.filter(function (logEvent) {
	// ... some condition ...
});

log2.info("This log is filtered by the new criteria!");   

Enrichment

Enrichment can be used to add key/value properties to all logs output via a particular logger.

var log = structuredLog.configuration()
	.enrich({
		UserId: getCurUserId(),
		SessionId: getCurSessionId(),
	})
	.writeTo(consoleSink())
	.createLogger();

A function can also be provided that is evaluated at runtime to attach properties to log events in a more dynamic fashion:

var log = structuredLog.configuration()
	.enrich(function () {
		return {
			UserId: getCurUserId(),
			SessionId: getCurSessionId(),		
		};
	})
	.writeTo(consoleSink())
	.createLogger();

Any number of properties can be attached to log messages in this manner. The properties may then be used in the log messages themselves:

log.info("Current user {UserId} has done something.");

As with other configuration, the enrich only affects subsequent sinks.

Logs can also be enriched after configuration, this effectively creates a new log with additional properties:

var log2 = log.enrich({ NewProperty: 'just for log2' };

log2.info("I've added a new property: {NewProperty}");   

Tagging

Logs can be tagged with string values. This is useful to filter and categories logs generated by an application:

var log = structuredLog.configuration()
	.tag("authentication-system")
	.writeTo(consoleSink())
	.createLogger();

Logs can also be tagged after configuration, this effectively creates a new log that has the desired tag:

var log2 = log.tag('some-new-tag');

log2.info("This log is tagged with 'some-new-tag'");