0.2.0 • Published 6 years ago

log-n-roll v0.2.0

Weekly downloads
6
License
MIT
Repository
github
Last release
6 years ago

log-n-roll

Tiny 1kb logger with slim api and ability of limitless extension.

NPM versionBuild Status

  • Tiny: weighs less than kilobyte gzipped
  • Pluggable: one built-in plugin for pretty formatting and limitless possibilities for extension

Note

This packege is Proof-of-Concept and should be considered as an unstable. Nevertheless, the basic idea has already been proved. Release coming soon.

Install

This project uses node and npm. Go check them out if you don't have them locally installed.

$ npm install log-n-roll

Then with a module bundler like rollup or webpack, use as you would anything else:

// using ES6 modules
import log from 'log-n-roll';

// using CommonJS modules
var log = require('log-n-roll');

The UMD build is also available on unpkg:

<script src="https://unpkg.com/log-n-roll/dist/log-n-roll.umd.js"></script>

You can find the library on window.log.

Usage

const log = require('log-n-roll');

log.trace('Trace shows stacktrace');

// Using the built-in plugin
log.use(log.prefixer);
log.trace('Using any number of plugins adds to the stacktrace only one extra line');

log.info("By default, the level of the default logger is 'trace'. All messages are displayed");

// Loading the processor
let two = 2;
for (let i = 0; i < 1000000; i++) {
  two *= two;
  two = Math.sqrt(two);
}

log.debug('Debug shows the time difference from the last call of any logger method');
log.info('Placeholders are supported. Two = %s', two);
log.warn('Warn message');
log.error('Error message');

// Getting the named logger and setting its level to 'warn'
const child = log('child', 'warn');
child.info('Messages below the level of the logger are ignored');

child.level = 'info';
child.info('The level of the logger can be changed at any time');

child().info(
  'Root logger can be obtained from any logger: child() %s== log() %s== log',
  child() === log() ? '=' : '!',
  log() === log ? '=' : '!',
);

child('any').info(
  'Any logger can be obtained from any logger: child("any") %s== log("any")',
  child('any') === log('any') ? '=' : '!',
);

log('any').debug('Any logger has extended the level from the default logger');
child('any').info('Any logger has extended the plugins props from the default logger');

Output:

Trace: Trace shows stacktrace
    at Object.<anonymous> (C:\Users\u36\Dropbox\kutuluk\log-n-roll\examples\basic.js:3:5)
    ...
Trace: 17:08:01 [TRACE] default : Using any number of plugins adds to the stacktrace only one extra line
    at Function.trace (C:\Users\u36\Dropbox\kutuluk\log-n-roll\dist\log-n-roll.js:1:730)
    at Object.<anonymous> (C:\Users\u36\Dropbox\kutuluk\log-n-roll\examples\basic.js:7:5)
    ...
17:08:01 [ INFO] default : By default, the level of the default logger is 'trace'. All messages are displayed
   +44ms [DEBUG] default : Debug shows the time difference from the last call of any logger method
17:08:01 [ INFO] default : Placeholders are supported. Two = 2
17:08:01 [ WARN] default : Warn message
17:08:01 [ERROR] default : Error message
17:08:01 [ INFO]   child : The level of the logger can be changed at any time
17:08:01 [ INFO] default : Root logger can be obtained from any logger: child() === log() === log
17:08:01 [ INFO]     any : Any logger can be obtained from any logger: child("any") === log("any")
    +0ms [DEBUG]     any : Any logger has extended the level from the default logger
17:08:01 [ INFO]     any : Any logger has extended the plugins props from the default logger

Plugins

The resulted examples of plugins are simplified. Despite the fact that they are workable, it is not recommended to use in production.

log-stacktrace.js

function getStackTrace() {
  try {
    throw new Error();
  } catch (trace) {
    return trace.stack;
  }
}

module.exports = (logger, props) => {
  // Return noop plugin if stacktrace support is absent
  if (!getStackTrace()) {
    return () => {};
  }

  props = Object.assign(
    {
      levels: ['trace', 'warn', 'error'],
      depth: 3,
    },
    props,
  );

  return (state) => {
    if (!props.levels.some(level => level === state.label)) {
      return;
    }

    let stacktrace = getStackTrace();

    const lines = stacktrace.split('\n');
    lines.splice(0, 4);
    const { depth } = props;
    if (depth && lines.length !== depth + 1) {
      const shrink = lines.splice(0, depth);
      stacktrace = shrink.join('\n');
      if (lines.length) stacktrace += `\n    and ${lines.length} more`;
    } else {
      stacktrace = lines.join('\n');
    }

    state.stacktrace = stacktrace;
  };
};

log-meta.js

module.exports = (logger, props) => (state) => {
  const meta = Object.assign({}, props);

  const { args } = state;

  if (args.length && typeof args[0] === 'object') {
    Object.assign(meta, args.shift());
  }

  state.meta = meta;
};

log-message.js

const { format } = require('util');

module.exports = () => (state) => {
  state.message = format(...state.args);
};

log-json.js

module.exports = (logger, props) => (state) => {
  const json = {};

  const fields = Object.keys(props);
  fields.forEach((name) => {
    json[name] = typeof props[name] === 'function'
      ? props[name](state)
      : state[props[name]] || state[name];
  });

  state.json = JSON.stringify(json, null, '\t');
};

to-file.js

const fs = require('fs');

module.exports = (logger, props) => {
  props = Object.assign(
    {
      file: 'app.log',
      fields: ['message', 'stacktrace'],
      separator: '\n',
      eol: '\n',
      roll: true,
    },
    props,
  );

  return (state) => {
    const fields = props.fields.map(field => state[field]).filter(field => field);

    fs.appendFileSync(props.file, fields.join(props.separator) + props.eol);

    state.roll = props.roll;
  };
};

###An example of using these plugins

plugins.js

const log = require('../dist/log-n-roll');

const stacktrace = require('../examples/plugins/log-stacktrace');
const meta = require('../examples/plugins/log-meta');
const message = require('../examples/plugins/log-message');
const json = require('../examples/plugins/log-json');

const toFile = require('../examples/plugins/to-file');

log.use(stacktrace).use(meta, { source: 'plagins.js' }).use(log.prefixer).use(message)
  .use(json, {
    message: 'message',
    timestamp: state => new Date(state.timestamp).toISOString(),
    level: 'label',
    logger: 'logger',
    meta: 'meta',
    stacktrace: state => (state.stacktrace ? state.stacktrace.split('\n') : []),
  });

log.info({ tag: 'message1' }, 'Hello, %s', 'console!');

log.use(toFile, { file: 'my.log' });

log.info({ tag: 'message2' }, 'Hello, %s', 'file!');

const child = log('child')
  .use(toFile, {
    fields: ['json'],
    eol: ',\n',
    roll: false,
  })
  .use(meta, { format: 'json' })
  .unuse(log.prefixer);

child.trace({ tag: 'message3' }, 'Goodbye, %s', 'console!');

Console output:

17:06:19 [ INFO] default : Hello, console!
17:06:19 [ INFO] default : Hello, file!

my.log:

17:06:19 [ INFO] default : Hello, file!
{
	"message": "Goodbye, console!",
	"timestamp": "2018-02-07T13:06:19.333Z",
	"level": "trace",
	"logger": "child",
	"meta": {
		"source": "plagins.js",
		"format": "json",
		"tag": "message3"
	},
	"stacktrace": [
		"    at Object.<anonymous> (C:\\Users\\u36\\Dropbox\\kutuluk\\log-n-roll\\examples\\plugins.js:35:7)",
		"    at Module._compile (module.js:635:30)",
		"    at Object.Module._extensions..js (module.js:646:10)",
		"    and 4 more"
	]
},
0.2.0

6 years ago

0.1.0

6 years ago

0.0.3

6 years ago

0.0.2

6 years ago

0.0.1

6 years ago