log-segment
Advanced tools
Comparing version 1.0.9 to 1.0.10
230
main.js
@@ -5,21 +5,15 @@ 'use strict' | ||
const chalk = require('chalk') | ||
const fs = require('fs') | ||
const fs = require('fs-extra') | ||
const nodemailer = require('nodemailer') | ||
// @todo transport for each level | ||
// console, file, stream, email (telegram, sms ...) | ||
// @todo custom format messages | ||
const mode = { | ||
CONSOLE: 0, | ||
FILE: 1 | ||
// STREAM | ||
// OTHERS (telegram, sms ...) | ||
FILE: 1, | ||
EMAIL: 2 | ||
} | ||
/** | ||
* @todo params.format | ||
* @class | ||
*/ | ||
const Log = function (params) { | ||
// default console | ||
let __levels = { | ||
@@ -35,3 +29,3 @@ '*': { | ||
color: 'green', | ||
marker: '✔' // ✔ ✔️ | ||
marker: '✔' | ||
}, | ||
@@ -48,3 +42,3 @@ warning: { | ||
color: 'magenta', | ||
marker: '😱' // ☠' | ||
marker: '😱' | ||
} | ||
@@ -66,3 +60,11 @@ } | ||
function __init (params) { | ||
const __memoize = { | ||
check: {} | ||
} | ||
/** | ||
* @constructor | ||
* @param {Object} params | ||
*/ | ||
const __init = function (params) { | ||
__markers = {} | ||
@@ -72,3 +74,6 @@ __setLevels(__levels) | ||
function set (params) { | ||
/** | ||
* @param {Object} params | ||
*/ | ||
const set = function (params) { | ||
if (!params) { | ||
@@ -78,2 +83,4 @@ return | ||
__reset() | ||
if (params.segments) { | ||
@@ -103,6 +110,17 @@ __setSegments(params.segments) | ||
/** | ||
* @return current settings | ||
*/ | ||
const get = function () { | ||
return { | ||
levels: Object.assign({}, __levels), | ||
segments: Object.assign({}, __segments), | ||
enabled: Object.assign({}, __enabled) | ||
} | ||
} | ||
/** | ||
* add segment: if already exists, override | ||
* add level: if already exists, override | ||
*/ | ||
function add (params) { | ||
const add = function (params) { | ||
if (!params) { | ||
@@ -120,5 +138,15 @@ return | ||
function value (label, value) { | ||
/** | ||
* @param {string} label | ||
* @param {*} value | ||
*/ | ||
const value = function (label, value) { | ||
return function () { | ||
if (typeof value === 'object') { | ||
if (value instanceof Error) { | ||
return value.stack | ||
} | ||
if (value instanceof Date) { | ||
return value.toISOString() | ||
} | ||
try { | ||
@@ -135,3 +163,3 @@ return `[${label}=${JSON.stringify(value)}]` | ||
function __setSegments (segments) { | ||
const __setSegments = function (segments) { | ||
__segments = {} | ||
@@ -141,3 +169,3 @@ __addSegments(segments) | ||
function __addSegments (segments) { | ||
const __addSegments = function (segments) { | ||
for (const i in segments) { | ||
@@ -148,5 +176,3 @@ if (__segments[i]) { | ||
let _segment = segments[i] | ||
if (!chalk[_segment.color]) { | ||
console.warn('log-segment, unknown color', _segment.color, 'for segment', i) | ||
} | ||
__checkSetting(_segment, 'segment') | ||
__segments[i] = _segment | ||
@@ -156,3 +182,3 @@ } | ||
function __setLevels (levels) { | ||
const __setLevels = function (levels) { | ||
// remove current levels | ||
@@ -168,3 +194,3 @@ for (const i in __levels) { | ||
function __addLevels (levels) { | ||
const __addLevels = function (levels) { | ||
for (const i in levels) { | ||
@@ -177,8 +203,5 @@ let _level = levels[i] | ||
Log.prototype[i] = __print(i) | ||
__checkSetting(_level, 'level') | ||
// cache markers | ||
if (_level.marker) { | ||
if (!chalk[_level.color]) { | ||
console.warn('log-segment, unknown color', _level.color, 'for level', i) | ||
} | ||
if (_level.color && chalk[_level.color]) { | ||
@@ -194,7 +217,64 @@ __markers[i] = chalk[_level.color](_level.marker) | ||
const __checkSetting = function (setting, part) { | ||
switch (setting.mode) { | ||
case mode.EMAIL: | ||
if (!setting.email) { | ||
console.warn('log-segment, mode is EMAIL but email settings missing for', part, 'fallback to console') | ||
setting.mode = mode.CONSOLE | ||
} | ||
break | ||
case mode.FILE: | ||
if (!setting.file) { | ||
console.warn('log-segment, mode is FILE but no file specified for', part, 'fallback to console') | ||
setting.mode = mode.CONSOLE | ||
} | ||
break | ||
case mode.CONSOLE: | ||
default: | ||
if (setting.color && !chalk[setting.color]) { | ||
console.warn('log-segment, unknown color', setting.color, 'for', part) | ||
} | ||
break | ||
} | ||
} | ||
const __reset = function () { | ||
__memoize.check = {} | ||
__resetFiles() | ||
} | ||
/** | ||
* clear file streams | ||
*/ | ||
const __resetFiles = function () { | ||
for (const _file in __files) { | ||
__files[_file].stream.end() | ||
process.removeListener('beforeExit', __files[_file].onProcessExit) | ||
delete __files[_file] | ||
} | ||
} | ||
/** | ||
* memoized | ||
* @param {string} segment | ||
* @param {string} level | ||
* @return bool | ||
*/ | ||
const __check = function (segment, level) { | ||
if (!__memoize.check[segment + level]) { | ||
__memoize.check[segment + level] = ((segment === '*') || | ||
__enabled.segments === '*' || | ||
tools.array.contains(__enabled.segments, segment)) && | ||
((__enabled.levels === '*') || | ||
tools.array.contains(__enabled.levels, level)) | ||
} | ||
return __memoize.check[segment + level] | ||
} | ||
/** | ||
* | ||
* @param {string} level level name | ||
*/ | ||
function __print (level) { | ||
const __print = function (level) { | ||
return function (segment) { | ||
@@ -236,4 +316,15 @@ if (!__check(segment, level)) { | ||
function __output (segment, level, data) { | ||
// @todo custom format | ||
/** | ||
* @todo custom format | ||
*/ | ||
const __output = function (segment, level, data) { | ||
if (__segments[segment] && __segments[segment].mode === mode.EMAIL) { | ||
return __outputEmail(__segments[segment].email, data) | ||
} | ||
if (__levels[level] && __levels[level].mode === mode.EMAIL) { | ||
return __outputEmail(__levels[level].email, data) | ||
} | ||
// file | ||
if (__segments[segment] && __segments[segment].mode === mode.FILE) { | ||
@@ -243,6 +334,10 @@ return __outputFile(__segments[segment].file, data) | ||
if (__levels[level].mode === mode.FILE) { | ||
return __outputFile(__segments[segment].file, data) | ||
return __outputFile(__levels[level].file, data) | ||
} | ||
// mode console | ||
// console | ||
return __outputConsole(data) | ||
} | ||
const __outputConsole = function (data) { | ||
console.log.apply(console, data) | ||
@@ -253,41 +348,59 @@ return true | ||
/** | ||
* @todo open file err | ||
* @param {string} file /path/to/file | ||
* @param {string[]} data | ||
*/ | ||
function __outputFile (file, data) { | ||
const __outputFile = function (file, data) { | ||
// open stream, if not already opened | ||
if (!__files[file]) { | ||
__files[file] = fs.createWriteStream(file, {flags: 'a', defaultEncoding: 'utf8'}) | ||
/* debug | ||
__files[file].on('finish', function () { | ||
console.log('file has been written') | ||
}) | ||
__files[file].on('open', function () { | ||
console.log('file has been open') | ||
}) | ||
*/ | ||
process.on('beforeExit', () => { | ||
if (__files[file]) { | ||
__files[file].end() | ||
fs.ensureFile(file, (err) => { | ||
if (err) { | ||
throw new Error('unable to write', file) | ||
} | ||
__files[file] = { | ||
stream: fs.createWriteStream(file, {flags: 'a', defaultEncoding: 'utf8'}), | ||
onProcessExit: () => { | ||
if (__files[file].stream) { | ||
__files[file].stream.end() | ||
} | ||
} | ||
} | ||
process.on('beforeExit', __files[file].onProcessExit) | ||
/* debug | ||
__files[file].on('finish', function () { | ||
console.log('file has been written') | ||
}) | ||
__files[file].on('open', function () { | ||
console.log('file has been open') | ||
}) | ||
*/ | ||
__outputFile(file, data) | ||
}) | ||
return true | ||
} | ||
data.push('\n') | ||
return __files[file].write(data.join(' ')) | ||
__files[file].stream.write(data.join(' ')) | ||
return true | ||
} | ||
/** | ||
* @param {string} segment | ||
* @param {string} level | ||
* @todo html | ||
* @param {*} email | ||
* @param {*} data | ||
*/ | ||
function __check (segment, level) { | ||
return ((segment === '*') || | ||
__enabled.segments === '*' || | ||
tools.array.contains(__enabled.segments, segment)) && | ||
((__enabled.levels === '*') || | ||
tools.array.contains(__enabled.levels, level)) | ||
const __outputEmail = function (email, data) { | ||
if (!email._transporter) { | ||
email._transporter = nodemailer.createTransport(email.transporter) | ||
} | ||
const _options = Object.assign(email.options) | ||
_options.text = data.join('\n') | ||
email._transporter.sendMail(_options, (err, info) => { | ||
if (err) { | ||
__outputConsole(['ERROR SENDING EMAIL', _options]) | ||
__outputConsole([data]) | ||
} | ||
}) | ||
return true | ||
} | ||
@@ -313,2 +426,3 @@ | ||
Log.prototype.set = set | ||
Log.prototype.get = get | ||
Log.prototype.add = add | ||
@@ -315,0 +429,0 @@ Log.prototype.value = Log.prototype.val = Log.prototype.v = value |
{ | ||
"name": "log-segment", | ||
"version": "1.0.9", | ||
"version": "1.0.10", | ||
"description": "logger with partition", | ||
"main": "main.js", | ||
"dependencies": { | ||
"a-toolbox": "^x", | ||
"chalk": "^x" | ||
"a-toolbox": "^0.1.x", | ||
"chalk": "^1.x", | ||
"fs-extra": "^2.x", | ||
"nodemailer": "^4.x" | ||
}, | ||
"devDependencies": { | ||
"standard": "^x", | ||
"pre-commit": "^x", | ||
"tap": "^x" | ||
"tap": "^x", | ||
"standard": "^x" | ||
}, | ||
"engines": { | ||
"node": ">4.x" | ||
"node": ">6.x" | ||
}, | ||
@@ -28,3 +30,3 @@ "files": [ | ||
"type": "git", | ||
"url": "git+https://github.com/simone-sanfratello/log-segment.git" | ||
"url": "git+https://github.com/braceslab/log-segment.git" | ||
}, | ||
@@ -36,4 +38,4 @@ "keywords": [ | ||
], | ||
"author": "Simone Sanfratello <simone.sanfra@gmail.com>", | ||
"author": "Simone Sanfratello <simone@braceslab.com>", | ||
"license": "MIT" | ||
} | ||
} |
273
README.md
# log-segment | ||
Node.js logger with partition | ||
[![NPM Version](http://img.shields.io/npm/v/log-segment.svg?style=flat)](https://www.npmjs.org/package/log-segment) | ||
[![NPM Downloads](https://img.shields.io/npm/dm/log-segment.svg?style=flat)](https://www.npmjs.org/package/log-segment) | ||
[![NPM](https://nodei.co/npm-dl/log-segment.png)](https://nodei.co/npm/log-segment/) | ||
[![JS Standard Style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/) | ||
[![NPM](https://nodei.co/npm-dl/log-segment.png)](https://nodei.co/npm/log-segment/) | ||
Javascript logger with partition | ||
### Description | ||
## Purpose | ||
````log-segment```` provide a lightweight logger full customizable to quickly enable and disable segments and levels. | ||
Why Another Log Module? Because not all logs are equal. | ||
I'd like to use a pliable tool that allow to easy and quickly manage logs by two factors: **level** and **segment**. | ||
### Install | ||
## Installing | ||
````bash | ||
npm install log-segment --save | ||
$ npm i -g log-segment | ||
```` | ||
### Usage | ||
### Quick start | ||
#### Default | ||
``log-segment`` is single-ton, so just require and use everywhere. | ||
````js | ||
const log = require('log-segment') | ||
log.info('*', 'info message') | ||
log.success('*', 'success message') | ||
log.warning('*', 'warning message') | ||
log.error('*', 'error message') | ||
log.panic('*', 'panic message') | ||
const sql = 'INSERT INTO table ...' | ||
log.info('sql', 'executing query ...', log.value('sql', sql)) | ||
db.query(sql) | ||
.then(() => { | ||
log.success('sql', 'query done.', log.value('sql', sql)) | ||
}) | ||
.catch((err) => { | ||
log.error('sql', 'query error', log.value('sql', sql)) | ||
}) | ||
```` | ||
output | ||
(attach screenshot) | ||
````js | ||
const log = require('log-segment') | ||
<!-- | ||
#### Customize | ||
require('express')().all('/*', (request, response) => { | ||
log.info('http', 'request', request.method, request.baseUrl) | ||
doSomething(request) | ||
.then((output) => { | ||
response.send(output) | ||
log.success('http', 'response on request', request.method, request.baseUrl) | ||
}) | ||
.catch((err) => { | ||
response.sendStatus(500) | ||
log.error('http', 'response on request', request.method, request.baseUrl, log.value('err', err)) | ||
}) | ||
}) | ||
```` | ||
![quickstart](./doc/img/quickstart.jpg "quickstart") | ||
#### Default settings | ||
Default settings provide any levels and segments enabled to console output. | ||
Default levels are: *info, success, warning, error, panic*. | ||
There is no hierarchy by levels. | ||
````json | ||
{ | ||
levels: { | ||
'*': { color: 'white' }, | ||
info: { color: 'blue', marker: 'ℹ️' }, | ||
success: { color: 'green', marker: '✔' }, | ||
warning: { color: 'yellow', marker: '❗️️' }, | ||
error: { color: 'red', marker: '✗️' }, | ||
panic: { color: 'magenta', marker: '😱' } | ||
}, | ||
segments: { '*': { color: 'white' } }, | ||
enabled: { segments: '*', levels: '*' } } | ||
```` | ||
##### Custom segments | ||
````js | ||
const log = require('log-segment') | ||
log.set({ | ||
segments: { | ||
http: { | ||
color: 'white' | ||
color: 'cyan' | ||
}, | ||
html: { | ||
color: 'magenta' | ||
}, | ||
sql: { | ||
color: 'magenta' | ||
mode: log.mode.FILE, | ||
file: '/tmp/myapp/sql.log' | ||
} | ||
} | ||
}) | ||
const sql = 'INSERT INTO table ...' | ||
log.info('sql', 'executing query ...', log.value('sql', sql)) | ||
log.success('sql', 'query done.', log.value('sql', sql)) | ||
const request = { | ||
method: 'GET', | ||
baseUrl: '/it/4x/api.html#req' | ||
} | ||
const err = new Error('file not found') | ||
log.info('http', 'request', request.method, request.baseUrl) | ||
log.error('http', 'response on request', request.method, request.baseUrl, log.value('err', err)) | ||
let username = null | ||
log.warning('html', 'rendering missing value', log.value('username', username)) | ||
```` | ||
![custom segments](./doc/img/custom-segments.jpg "custom segments") | ||
#### Custom levels | ||
````js | ||
const log = require('log-segment') | ||
log.set({ | ||
segments: { | ||
http: { | ||
color: 'yellow' | ||
}, | ||
sql: { | ||
color: 'white' | ||
} | ||
}, | ||
levels: { | ||
info: { | ||
color: 'blue', | ||
marker: 'ℹ️' | ||
trace: { | ||
marker: '[TRACE]' | ||
}, | ||
success: { | ||
color: 'green', | ||
marker: '✔' | ||
}, | ||
warning: { | ||
color: 'yellow', | ||
marker: '❗️️' | ||
marker: '[WARN]' | ||
}, | ||
error: { | ||
color: 'red', | ||
marker: '✗️' | ||
marker: '[ERR]' | ||
} | ||
@@ -72,19 +150,130 @@ } | ||
log.info('*', 'info message') | ||
log.success('*', 'success message') | ||
log.warning('*', 'warning message') | ||
log.error('*', 'error message') | ||
const sql = 'INSERT INTO table ...' | ||
log.trace('sql', 'executing query ...', log.value('sql', sql)) | ||
log.trace('sql', 'query done.', log.value('sql', sql)) | ||
const request = { | ||
method: 'GET', | ||
baseUrl: '/it/4x/api.html#req' | ||
} | ||
const err = new Error('file not found') | ||
log.trace('http', 'request', request.method, request.baseUrl) | ||
log.error('http', 'response on request', request.method, request.baseUrl, log.value('err', err)) | ||
let username = null | ||
log.warning('html', 'rendering missing value', log.value('username', username)) | ||
```` | ||
--> | ||
### API | ||
... | ||
![custom levels](./doc/img/custom-levels.jpg "custom levels") | ||
### Roadmap | ||
### Use Cases | ||
````js | ||
log.set({ | ||
segments: { | ||
http: { color: 'cyan' }, | ||
network: { color: 'blue' }, | ||
db: { color: 'yellow' }, | ||
sql: { } | ||
} | ||
}) | ||
```` | ||
**Development** | ||
Just enable everything on console | ||
````js | ||
log.set({ enabled: { segments: '*', levels: '*' } }) | ||
```` | ||
**Debug** | ||
Enable only segments to focus on, at any levels, to find that bug | ||
````js | ||
log.set({ enabled: { segments: ['sql', 'network'] } }) | ||
```` | ||
**Production** | ||
Different behaviour for each level: | ||
- disable not interesting parts: success level, template rendering | ||
- different file for each type: *info, warning, error* | ||
and remove marks | ||
- separate sql file | ||
- on panic send email | ||
````js | ||
log.set({ | ||
enabled: { | ||
segments: ['sql'], | ||
levels: ['info', 'warning', 'error', 'panic'] | ||
}, | ||
segments: { | ||
sql: { | ||
mode: log.mode.FILE, | ||
file: '/var/log/myapp/sql' | ||
} | ||
}, | ||
levels: { | ||
info: { | ||
marker: '[i]', | ||
mode: log.mode.FILE, | ||
file: '/var/log/myapp/info' | ||
}, | ||
warning: { | ||
marker: '[warn]', | ||
mode: log.mode.FILE, | ||
file: '/var/log/myapp/warn' | ||
}, | ||
error: { | ||
marker: '[err]', | ||
mode: log.mode.FILE, | ||
file: '/var/log/myapp/error' | ||
}, | ||
panic: { | ||
mode: log.mode.MAIL, | ||
email: { | ||
transporter: { | ||
service: 'gmail', | ||
auth: { | ||
user: '***@gmail.com', | ||
pass: '***' | ||
} | ||
} | ||
} | ||
options: { | ||
from: '"log-segment" <log-segment@test.test>', | ||
to: 'sys-admin@gmail.com', | ||
subject: 'myapp PANIC' | ||
} | ||
} | ||
} | ||
} | ||
}) | ||
```` | ||
## Documentation | ||
See [documentation](./doc/README.md) for further informations. | ||
## Changelog | ||
v. 1.1.0 | ||
- Add support for email mode | ||
## TODO | ||
- add message info: trace, timestamp, chrono | ||
- custom format in message | ||
- custom format in log.value | ||
- custom transport by segment and level: console, file, stream, email (sms, telegram and whatever) | ||
- custom mode: stream, (sms, telegram and whatever) | ||
- multiple mode for each setting (example: on panic send email, log to file, send sms, call mom) | ||
- customizable action (example: on error run function) | ||
- browser support (browserify?) | ||
- .test() test settings: write files, send emails ... | ||
- (evaluate) support workers (as transport) | ||
@@ -96,2 +285,4 @@ - [pino](https://github.com/pinojs/pino) | ||
- [debug](https://github.com/visionmedia/debug) | ||
- [log](https://github.com/tj/log.js) | ||
- [bunyan](https://github.com/trentm/node-bunyan) | ||
- others? | ||
@@ -105,3 +296,3 @@ | ||
Copyright (c) 2016-2017 Simone Sanfratello | ||
Copyright (c) 2017, [braces lab](https://braceslab.com) | ||
@@ -108,0 +299,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Wildcard dependency
QualityPackage has a dependency with a floating version range. This can cause issues if the dependency publishes a new major version.
Found 2 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
17965
372
1
313
4
+ Addedfs-extra@^2.x
+ Addednodemailer@^4.x
+ Addeda-toolbox@0.1.15(transitive)
+ Addedansi-regex@2.1.1(transitive)
+ Addedansi-styles@2.2.1(transitive)
+ Addedchalk@1.1.3(transitive)
+ Addedescape-string-regexp@1.0.5(transitive)
+ Addedfs-extra@2.1.2(transitive)
+ Addedgraceful-fs@4.2.11(transitive)
+ Addedhas-ansi@2.0.0(transitive)
+ Addedjsonfile@2.4.0(transitive)
+ Addednodemailer@4.7.0(transitive)
+ Addedstrip-ansi@3.0.1(transitive)
+ Addedsupports-color@2.0.0(transitive)
- Removeda-toolbox@1.7.3(transitive)
- Removedchalk@5.4.1(transitive)
- Removedhash.js@1.1.7(transitive)
- Removedinherits@2.0.4(transitive)
- Removedminimalistic-assert@1.0.1(transitive)
Updateda-toolbox@^0.1.x
Updatedchalk@^1.x