Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

golog

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

golog - npm Package Compare versions

Comparing version 0.1.2 to 0.2.0

15

CHANGELOG.md

@@ -8,2 +8,16 @@ # GoLog Changelog

## [v0.2.0] - 2020-08-23
### Added
- log coloring
- auto-parsing of specific fields
- function to filter out by log level
- function to filter out by log type
### Changed
- entire interface for logging to stdout
### Removed
- support for direct file/stream writing
## [v0.1.2] - 2019-10-15

@@ -26,1 +40,2 @@

[v0.1.2]: https://gitlab.com/GCSBOSS/golog/-/tags/v0.1.2
[v0.2.0]: https://gitlab.com/GCSBOSS/golog/-/tags/v0.2.0

173

lib/main.js

@@ -1,111 +0,124 @@

const { EventEmitter } = require('events');
const assert = require('assert');
const util = require('util');
const fs = require('fs');
const LEVELS = {
debug: { w: 0, c: '\x1b[30;5;1m' },
info: { w: 1, c: '\x1b[0m' },
warn: { w: 2, c: '\x1b[33m' },
error: { w: 3, c: '\x1b[31m' },
fatal: { w: 4, c: '\x1b[41;5;1m' }
};
const LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3, fatal: 4 };
function output(entry){
let env = process.env.NODE_ENV;
function formatErrData(err){
// Output friendly log for dev or undfined env
/* istanbul ignore next */
if(!env || env === 'development'){
let uword = (entry.type == 'event' ? entry.level : entry.type).toUpperCase();
entry.time.setMinutes(entry.time.getMinutes() - entry.time.getTimezoneOffset());
let utime = entry.time.toISOString().slice(11, -5);
console.log(LEVELS[entry.level].c + utime + ': ' + uword + ' - ' + entry.msg + '\x1b[0m');
}
// Output complete JSON log for production and staging
/* istanbul ignore next */
else if(env !== 'testing')
console.log(JSON.stringify(entry));
}
function parseErr({ err }){
if(!err)
return {};
if(!(err instanceof Error))
err = new Error(err);
let stack = err.stack.split(/[\r\n]+\s*/g);
return {
err: null,
code: err.code,
name: err.constructor.name,
class: err.constructor.name,
message: err.message,
stack: err.stack.split(/[\r\n]+\s*/g).slice(1, -1)
};
stack: stack.slice(1, -1),
msg: stack[0] + ' ' + stack[1]
}
}
function formatReqData(req){
function parseReq({ req }){
if(!req)
return {};
return {
req: null,
method: req.method,
path: req.url,
host: req.headers.host,
agent: req.headers['user-agent']
};
path: req.url || req.path,
host: req.getHeader('host'),
agent: req.getHeader('user-agent'),
type: 'request',
msg: 'Received ' + req.method + ' request to ' + req.url
}
}
function formatData(data){
if(data.err instanceof Error)
data.err = formatErrData(data.err);
if(typeof data.req === 'object')
data.req = formatReqData(data.req);
return data;
function parseRes({ res }){
if(!res)
return {};
let req = res.req;
return {
res: null,
level: res.statusCode > 499 ? 'warn' : 'debug',
path: req.url || req.path,
status: res.statusCode,
method: req.method,
type: 'response',
msg: 'Sent ' + res.statusCode + ' response to ' + req.method + ' ' + req.url
};
}
function parseEntry(logger, level, args) {
function getEntry(level, args){
let data = typeof args[0] == 'object' ? args.shift() : {};
let msg = util.format(...args);
let type = data.type || 'event';
let time = (new Date()).toISOString();
/* istanbul ignore next */
let pid = process.pid != 1 ? process.pid : null;
let data = {};
if(typeof args[0] == 'object')
data = formatData(args.shift());
Object.assign(data, ...this.parsers.map(p => p(data)));
let msg = args.shift();
msg = util.format(msg, ...args);
if(logger.mode == 'minimal')
return `${time} - ${level} - ${msg}\r\n`;
let obj = { ...data, name: logger.name, pid: process.pid, level, msg, time };
let spaces, breaks = '\r\n';
if(logger.mode == 'pretty'){
spaces = 2;
breaks = '\r\n\r\n';
}
return JSON.stringify(obj, undefined, spaces) + breaks;
msg = msg || data.msg;
return { level, type, ...data, msg, pid, time: new Date() };
}
function log(level, ...args) {
function log(level, ...args){
let entry = getEntry.apply(this, [ level, args ]);
entry.app = this.conf.app;
let entry = parseEntry(this, level, args);
let badLevel = LEVELS[this.conf.level].w > LEVELS[level].w;
let badType = this.conf.except.has(entry.type) ||
this.conf.only.size > 0 && !this.conf.only.has(entry.type);
if(badType || badLevel)
return false;
Object.values(this.streams).forEach(s => {
let l = LOG_LEVELS[s.level || this.level];
if(LOG_LEVELS[level] >= l)
s.stream.write(entry, 'utf8');
});
output(entry);
this.emit(level, entry);
this.emit('entry', entry);
return entry;
}
const DEFAULT_OPTS = { name: 'golog', mode: 'full', level: 'warn' };
module.exports = class GoLog {
module.exports = class GoLog extends EventEmitter {
constructor(conf = {}){
this.conf = conf || {};
this.conf.level = conf.level || 'debug';
this.conf.only = new Set([].concat(conf.only).filter(a => a));
this.conf.except = new Set([].concat(conf.except).filter(a => a));
this.parsers = [ parseErr, parseReq, parseRes ];
constructor(opts) {
opts = { ...DEFAULT_OPTS, ...opts };
super();
this.on('error', Function.prototype);
this.active = true;
this.streams = {};
let op = conf === false ? () => false : log;
this.name = opts.name;
this.mode = opts.mode;
this.level = opts.level;
Object.keys(LOG_LEVELS).forEach( l =>
this[l] = log.bind(this, l));
if(opts.stream)
this.addStream('main', opts.stream);
else if(opts.file)
this.addFile('main', opts.file);
for(let l in LEVELS)
this[l] = op.bind(this, l);
}
addStream(name, stream, level) {
assert(stream.writable);
this.streams[name] = { stream, level };
addParser(parser){
this.parsers.push(parser);
}
removeStream(name) {
delete this.streams[name];
}
addFile(name, file, level) {
let stream = fs.createWriteStream(file, { flags: 'a' });
this.addStream(name, stream, level);
}
}
};
{
"name": "golog",
"version": "0.1.2",
"description": "A light-weight heavily inspired logging library",
"version": "0.2.0",
"description": "A light-weight stdout logging library for NodeJS.",
"main": "lib/main.js",

@@ -16,6 +16,2 @@ "author": "Guilherme C. Souza",

},
"devDependencies": {
"memorystream": "^0.3.1",
"tempper": "^0.1.1"
},
"repository": {

@@ -26,13 +22,13 @@ "type": "git",

"keywords": [
"logging",
"json",
"pretty",
"simlpe",
"log",
"bunyan",
"logging",
"debug",
"file",
"stream",
"env",
"parse",
"req",
"res",
"stdout",
"12factor",
"format"
]
}
# [GoLog](https://gitlab.com/GCSBOSS/golog)
A light-weight heavily inspired logging library for NodeJS. Checkout the main features:
A light-weight stdout logging library for NodeJS. Checkout the main features:
- JSON output as well as simple _date-level-msg_ format.
- Auto-format error and request objects.
- JSON output by default
- Automatic readable output format for dev environments
- Auto-format error, request and repsonse objects.
- Support for adding custom function to parse interesting input data
- `printf`-like formatting on messages.
- Automatic pid field on log entries.
- Broadcast to many files/streams.
- Individual logging level for each file/stream.
- Log entries filtering by level or type (whitelist, blacklist)

@@ -22,8 +23,2 @@ ## Get Started

var log = new GoLog();
// Add a Writable Stream to receive log entries.
log.addStream( 'my-stdout', process.stdout );
// Add a file where to write log entries.
log.addFile( 'my-file', '/path/to/file' );
```

@@ -43,62 +38,26 @@

If you want to setup a logger with a single stream or file go with the one-liner.
If you have many loggers going to the same place rely on setting their source.
```js
var slog = new GoLog({ stream: process.stdout });
var flog = new GoLog({ file: '/path/to/file' });
```
If you have many loggers writing to the same stream, name each logger for better visibility.
```js
var l1 = new GoLog({ name: 'log-a', stream: process.stdout });
var l1 = new GoLog({ app: 'log-a' });
l1.warn('Hey!');
var l2 = new GoLog({ name: 'log-b', stream: process.stdout });
var l2 = new GoLog({ app: 'log-b' });
l2.warn('Ho!');
```
Default loggers only log entries of level `warn` or higher. Change the level for an entire logger.
Default loggers log entries of any level. Change the level for warn or higher.
```js
var log = new GoLog({ level: 'debug', stream: process.stdout });
var log = new GoLog({ level: 'warn' });
log.debug('Let\'s go!');
```
If you need these files right now, just change to a mode with better aesthetics.
You can check/use log entries right off the bat.
```js
var log = new GoLog({ stream: process.stdout });
log.warn('This is an ugly JSON');
log.mode = 'pretty'
log.warn('This is a cute JSON');
log.mode = 'minimal'
log.warn('This is just readable');
```
If you need to stop logging to a given stream/file just use `removeStream`.
```js
var log = new GoLog({ stream: process.stdout });
log.addFile('some-file', '/path/to/file');
log.warn('This goes to stdout and file');
log.removeStream('main');
log.warn('This goes to file only');
log.removeStream('some-file');
log.warn('This goes nowhere');
```
If you happen to need to setup some listeners.
```js
var log = new GoLog();
log.on('fatal', ent => console.log('Show me once'));
log.on('entry', ent => console.log('Show me twice'));
log.fatal('The fatal entry');
log.debug('The debug entry');
let e1 = log.fatal('foo');
let e2 = log.debug('bar');
console.log(e1, e2)/
```

@@ -118,7 +77,7 @@

var log = new GoLog({ stream: process.stdout });
var log = new GoLog();
var server = http.createServer(function(req, res){
res.end();
log.warn({ req: req }, 'The message lives on');
log.warn({ req }, 'The message lives on');
}).listen(8765);

@@ -129,2 +88,9 @@

You can create a disabled logger if you send `false` as the input parameter
```js
var log = new GoLog(false);
log.info('Wont log this');
```
## Reporting Bugs

@@ -131,0 +97,0 @@ If you have found any problems with this module, please:

@@ -1,134 +0,86 @@

const MemoryStream = require('memorystream');
const Tempper = require('tempper');
const assert = require('assert');
const fs = require('fs');
const Logger = require('../lib/main');
var stream, log;
process.env.NODE_ENV = 'testing';
beforeEach(function(){
stream = new MemoryStream();
});
describe('Logging', function(){
var log;
describe('Setup', function(){
it('Should store the given stream to write log entries [#addStream]', function(){
before(function(){
log = new Logger();
log.addStream('foobar', stream);
assert.strictEqual(typeof log.streams.foobar, 'object');
});
it('Should create fs write stream from file path [#addFile]', function(done){
log = new Logger();
let tmp = new Tempper();
log.addFile('foobar', './foo.txt');
log.streams.foobar.stream.write('baz');
log.streams.foobar.stream.end();
setTimeout(function(){
tmp.assertExists('./foo.txt');
tmp.clear();
done();
}, 1000);
it('Should log appropriate objects according to log level', function(){
assert.strictEqual(log.debug().level, 'debug');
assert.strictEqual(log.info().level, 'info');
assert.strictEqual(log.warn().level, 'warn');
assert.strictEqual(log.error().level, 'error');
assert.strictEqual(log.fatal().level, 'fatal');
});
it('Should remove the given stream from the logger [#removeStream]', function(){
log = new Logger();
log.addStream('foobar', stream);
log.removeStream('foobar');
assert.strictEqual(typeof log.streams.foobar, 'undefined');
it('Should printf format message with input arguments', function(){
assert.strictEqual(log.debug('a b %s d %s', 'c', 'e').msg, 'a b c d e');
});
});
describe('Logging', function(){
beforeEach(function(){
log = new Logger();
log.addStream('mem', stream);
it('Should assign first object argument properties to final log entry', function(){
let e = log.info({ a: 1, b: 2 });
assert.strictEqual(e.a, 1);
assert.strictEqual(e.b, 2);
});
it('Should log the given message as JSON', function(done){
stream.on('data', buf => {
assert.strictEqual(JSON.parse(buf.toString()).msg, 'bar');
done();
});
log.warn('bar');
it('Should include entry type', function(){
assert.strictEqual(log.warn().type, 'event');
assert.strictEqual(log.error({ type: 'foobar' }).type, 'foobar');
});
it('Should log additional data in the message as JSON', function(done){
stream.on('data', buf => {
let ent = JSON.parse(buf.toString());
assert.strictEqual(ent.msg, 'bar');
assert.strictEqual(ent.foo, 'baz');
done();
});
log.warn({ foo: 'baz' }, 'bar');
});
});
it('Should not log when entry level is below logger level', function(done){
stream.on('data', done);
log.debug({ foo: 'baz' }, 'bar');
done();
});
describe('Auto-Parsing', function(){
it('Should broadcast log entries to all streams', function(done){
let i = 0;
let test = () => i > 0 ? done() : i++;
stream.on('data', test);
let stream2 = new MemoryStream();
stream2.on('data', buf => {
assert.strictEqual(JSON.parse(buf.toString()).msg, 'barbaz');
test();
});
log.addStream('other', stream2);
log.warn('barbaz');
it('Should parse error objects', function(){
let log = new Logger();
assert(Array.isArray(log.warn({ err: new Error('Foobar') }).stack));
assert(Array.isArray(log.info({ err: 'My Error' }).stack));
});
});
const http = require('http');
describe('Formatting', function(){
beforeEach(function(){
log = new Logger();
log.addStream('mem', stream);
it('Should parse http REQuest objects', function(){
let log = new Logger();
let req = http.request('http://example.com');
req.on('error', Function.prototype);
assert.strictEqual(log.warn({ req }).method, 'GET');
req.destroy();
});
it('Should format message with input variables', function(done){
stream.on('data', buf => {
assert.strictEqual(JSON.parse(buf.toString()).msg, 'bar 1 cool');
it('Should parse http RESponse objects', function(done){
let log = new Logger();
let req = http.request('http://example.com');
req.on('response', res => {
let e = log.info({ res });
assert.strictEqual(e.method, 'GET');
assert.strictEqual(e.status, 200);
assert.strictEqual(e.type, 'response');
done();
});
log.warn('bar %d %s', 1, 'cool');
req.end();
});
it('Should format error objects in the input data', function(done){
stream.on('data', buf => {
let out = JSON.parse(buf.toString());
assert.strictEqual(out.err.name, 'Error');
assert.strictEqual(out.msg, 'foo');
it('Should force level warn on 5xx response', function(done){
let log = new Logger();
let req = http.request('http://httpbin.org/status/500');
req.on('response', res => {
let e = log.info({ res });
assert.strictEqual(e.status, 500);
assert.strictEqual(e.level, 'warn');
done();
});
log.warn({ err: new Error() }, 'foo');
req.end();
});
it('Should format http request objects in the input data', function(done){
const http = require('http');
const server = http.createServer(function(req, res){
res.end();
log.warn({ req: req }, 'foo');
});
stream.on('data', buf => {
let out = JSON.parse(buf.toString());
assert.strictEqual(out.req.host, 'localhost:8765');
assert.strictEqual(out.msg, 'foo');
server.close();
done();
});
server.listen(8765);
http.get('http://localhost:8765');
it('Should execute added parsers', function(){
let log = new Logger();
log.addParser(() => ({ foobar: true }));
let e = log.info();
assert(e.foobar);
});

@@ -140,83 +92,25 @@

beforeEach(function(){
log = new Logger();
log.addStream('mem', stream);
it('Should not log when entry level is below conf level [level]', function(){
let log = new Logger({ level: 'error' });
assert(!log.warn());
});
it('Should log only essential data when in minimal mode [mode = minimal]', function(done){
stream.on('data', buf => {
let ent = buf.toString();
assert(/\ \-\ warn\ \-\ bar/g.test(ent));
done();
});
log.mode = 'minimal';
log.warn({ foo: 'baz' }, 'bar');
it('Should not log anything when conf is FALSE', function(){
let log = new Logger(false);
assert(!log.debug());
});
it('Should log readable JSON when in pretty mode [mode = pretty]', function(done){
stream.on('data', buf => {
let ent = buf.toString();
assert(/"level": "warn",[\n\r]+ "msg": "bar"/g.test(ent));
done();
});
log.mode = 'pretty';
log.warn({ foo: 'baz' }, 'bar');
it('Should only log selected types [only]', function(){
let log = new Logger({ only: 'a' });
assert(log.debug({ type: 'a' }));
assert(!log.debug({ type: 'b' }));
});
it('Should not log when entry level is below stream level [stream.level]', function(done){
log.streams.mem.level = 'error';
stream.on('data', done);
log.on('warn', () => done());
log.warn({ foo: 'baz' }, 'bar');
it('Should not log filtered types [except]', function(){
let log = new Logger({ except: [ 'a', 'c' ] });
assert(!log.debug({ type: 'a' }));
assert(log.debug({ type: 'b' }));
assert(!log.debug({ type: 'c' }));
});
it('Should add a single main file stream [file]', function(done){
let tmp = new Tempper();
log = new Logger({ file: './abc.txt' });
assert.strictEqual(typeof log.streams.main, 'object');
log.streams.main.stream.end();
setTimeout(function(){
tmp.assertExists('./abc.txt');
tmp.clear();
done();
}, 1400);
});
it('Should add a single main stream [stream]', function(done){
log = new Logger({ stream });
stream.on('data', buf => {
assert.strictEqual(JSON.parse(buf.toString()).msg, 'bar');
done();
});
log.warn('bar');
});
});
describe('Regression', function(){
beforeEach(function(){
log = new Logger();
log.addStream('mem', stream);
});
it('Should not throw exception when error event not handled', function(){
log.error('bar');
});
it('Should always append to files', function(done){
let tmp = new Tempper();
fs.writeFileSync('./test', 'abc');
log = new Logger({ file: './test' });
log.mode = 'minimal';
log.warn('hey');
log.streams.main.stream.end();
setTimeout(function(){
let data = fs.readFileSync('./test');
assert(/^abc.*\- warn \- hey/g.test(data.toString()));
tmp.clear();
done();
}, 1200);
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc