another-logger
Advanced tools
Comparing version 3.0.0-pre2 to 3.0.0-pre3
177
index.js
@@ -17,2 +17,3 @@ 'use strict'; | ||
label: '', | ||
stream: process.stdout, | ||
}; | ||
@@ -22,3 +23,3 @@ const defaultLevels = { | ||
info: {style: 'blue'}, | ||
log: {text: 'info', style: 'blue'}, | ||
log: {style: 'blue'}, | ||
success: {style: 'green'}, | ||
@@ -44,4 +45,4 @@ warn: {text: 'warning', style: 'yellow'}, | ||
baseLevels = Object.assign({}, defaultLevels, baseLevels); | ||
// default config sets no ignored levels, so no assign necessary, but we do have | ||
// to resolve arrays o objects | ||
// Default config sets no ignored levels, so no assign necessary, but we do have | ||
// to resolve arrays or objects | ||
if (Array.isArray(baseConfig.ignoredLevels)) { | ||
@@ -55,7 +56,8 @@ baseConfig.ignoredLevels = baseConfig.ignoredLevels.reduce((acc, val) => { | ||
/** | ||
* Applies a terminal color style to a bit of text via `chalk`. | ||
* Applies a terminal color style to a bit of text via `chalk`. If `chalk` is | ||
* not in the project, returns the text as-is. | ||
* @todo support chalk's fn calls, e.g. .rgb() | ||
* | ||
* @param {string} text The text to apply the style to | ||
* @param {string} styleString The style string to apply, a space- or | ||
* @param {string} style The style string to apply, a space- or | ||
* period-separated list of *named* (no custom rgb() calls, etc.) `chalk` styles | ||
@@ -66,5 +68,6 @@ * (see the `chalk` package documentation for a list: | ||
*/ | ||
function style (text, styleString) { | ||
if (!chalk) return text; | ||
const parts = styleString.split(/[. ]/g); | ||
function applyStyle (text, style) { | ||
if (!chalk || !style) return text; | ||
if (typeof style === 'function') return style(text); | ||
const parts = style.split(/[. ]/g); | ||
let stylefn = chalk; | ||
@@ -86,3 +89,29 @@ while (parts.length) { | ||
let fakeConsole; | ||
let lastResult; | ||
/** | ||
* Returns the output of console.table as a string isntead of writing it to | ||
* stdout. | ||
* @param {...any} contents Arguments as passed to `console.table` | ||
* @returns {string} | ||
*/ | ||
function consoleTable (...contents) { | ||
if (!fakeConsole) { | ||
// `Console.table` internally calls `Console.log` to display results, so | ||
// we override the log function to store the result in a variable | ||
// rather than sending it to stdout. Because we pass process.stdout to | ||
// the console constructor, the output string will contain color codes. | ||
// eslint-disable-next-line no-console | ||
fakeConsole = new console.Console(process.stdout); | ||
fakeConsole.log = result => { | ||
lastResult = result; | ||
}; | ||
} | ||
// Calling the table function stores the result in `lastResult`... | ||
fakeConsole.table(...contents); | ||
// ...so we can just return that variable now! | ||
return lastResult; | ||
} | ||
/** | ||
* Returns a new logger object and stuff. | ||
@@ -93,16 +122,17 @@ * @param {Object} config An object containing configuration options | ||
* messages | ||
* @param {string[] | Object} config.ignoredLevels A list of level names that | ||
* should be excluded from the output | ||
* @param {Object} levels An object containing levels to use for the logger. | ||
* Keys of the object are level names as they're called from code, and each key | ||
* should map to an object with options I can't document here because JSDoc is | ||
* stupid and doesn't like custom object things | ||
* @param {Object | string[]} config.ignoreLevels An object mapping level names | ||
* to a boolean indicating whether or not the level should be ignored, or an | ||
* array of level names to be ignored | ||
* @param {Stream?} config.stream A writable stream (or any object that has a | ||
* `.write()` method) that the output of this level is sent to | ||
* @param {Object} config.levels An object containing levels to use for the | ||
* logger. Keys of the object are level names as they're called from code, and | ||
* each key should map to an object with options I can't document here because | ||
* JSDoc is stupid and doesn't like custom object things | ||
* @returns {Object} The logger object | ||
*/ | ||
function createLogger (config, levels) { | ||
function createLogger (config = {}) { | ||
// Compute the calculated levels/config options by applying the defaults | ||
levels = levels || config && config.levels || {}; | ||
levels = Object.assign({}, baseLevels, levels); | ||
const levels = Object.assign({}, baseLevels, config.levels); | ||
config = Object.assign({}, baseConfig, config); | ||
delete config.levels; // don't rely on this since it may not be passed in | ||
@@ -117,69 +147,58 @@ // Resolve ignoredLevels | ||
} | ||
// Merge with base config | ||
// Merge with base config's ignored levels | ||
config.ignoredLevels = Object.assign({}, baseConfig.ignoredLevels, config.ignoredLevels); | ||
// Construct the base logger object | ||
const logger = { | ||
_config: Object.assign({}, baseConfig, config), | ||
_log (level, ...contents) { | ||
// If the log level is ignored, do nothing | ||
if (this._config.ignoredLevels[level]) return; | ||
// Assemble all the parts of the message prefix | ||
const time = this._config.timestamps ? timestamp() : ''; | ||
const label = this._config.label || ''; | ||
const prefix = [ | ||
time, | ||
label, | ||
this[level]._text, | ||
].filter(s => s).join(' '); | ||
// Format contents and write message | ||
contents = util.format(...contents); | ||
// eslint-disable-next-line no-console | ||
console.log(`${prefix} ${contents}`); | ||
}, | ||
_trace (level, ...contents) { | ||
if (this._config.ignoredLevels[level]) return; | ||
// Remove the first two lines, leaving a newline as the first char | ||
const stacktrace = new Error().stack.replace(/.*\n.*/, ''); | ||
this._log(level, util.format(...contents) + stacktrace); | ||
}, | ||
_table (level, ...contents) { | ||
if (this._config.ignoredLevels[level]) return; | ||
// HACK: This code calls the built-in console.table() function but | ||
// on a proxy that hijacks the output function and sends the | ||
// generated table to our logger. | ||
const fakeConsole = new Proxy(console, { | ||
get: (c, prop) => { | ||
// Replace the log function with our custom log | ||
if (prop === 'log') { | ||
return tableString => { | ||
// If the table is multiline, add a newline at the | ||
// beginning to preserve alignment | ||
if (tableString.indexOf('\n') !== -1) { | ||
tableString = `\n${tableString}`; | ||
} | ||
this._log(level, tableString); | ||
}; | ||
} | ||
// Symbol properties used in the table function need to be | ||
// passed through as-is | ||
return c[prop]; | ||
}, | ||
}); | ||
// this is literally a console logging utility, chill out eslint | ||
// eslint-disable-next-line no-console | ||
console.constructor.prototype.table.apply(fakeConsole, contents); | ||
}, | ||
}; | ||
// Create the logger object | ||
const logger = {}; | ||
// Add logging functions for each level | ||
// Private functions - always called with `levelObj` (below) as `this` | ||
function log (...contents) { | ||
if (config.ignoredLevels[this.name]) return; | ||
const time = config.timestamps ? timestamp() : ''; | ||
const label = config.label || ''; | ||
const prefix = [ | ||
time, | ||
label, | ||
this.cachedText, | ||
].filter(s => s).join(' '); | ||
contents = util.format(...contents); | ||
this.stream.write(`${prefix} ${contents}\n`); | ||
} | ||
function trace (...contents) { | ||
if (config.ignoredLevels[this.name]) return; | ||
// Remove the first two lines, leaving a newline as the first char | ||
const stacktrace = new Error().stack.replace(/.*\n.*/, ''); | ||
log.call(this, util.format(...contents) + stacktrace); | ||
} | ||
function table (...contents) { | ||
if (config.ignoredLevels[this.name]) return; | ||
let tableString = consoleTable(...contents); | ||
// If the table is multiline, add a newline at the beginning to preserve | ||
// alignment. `indexOf` check because passing e.g. a number to the table | ||
// function results in that number being returned, and numbers don't | ||
// have an `indexOf` method. | ||
if (typeof tableString === 'string' && tableString.indexOf('\n') !== -1) { | ||
tableString = `\n${tableString}`; | ||
} | ||
log.call(this, tableString); | ||
} | ||
// Add levels to the logger and cache some stuff | ||
for (const level of Object.keys(levels)) { | ||
// Bind the log functions for this level | ||
logger[level] = logger._log.bind(logger, level); | ||
logger[level].trace = logger._trace.bind(logger, level); | ||
logger[level].table = logger._table.bind(logger, level); | ||
// Bake in the styled text to save time later | ||
logger[level]._text = style(levels[level].text || level, levels[level].style); | ||
// Duplicate the object so we don't accidentally modify base config | ||
const levelObj = {...levels[level]}; | ||
// Store extra options on the level object | ||
levelObj.cachedText = applyStyle(levelObj.text || level, levelObj.style); | ||
levelObj.name = level; | ||
if (!levelObj.stream) { | ||
levelObj.stream = config.stream || process.stdout; | ||
} | ||
// Bind private functions to the level object and put them on the logger | ||
const levelFunc = log.bind(levelObj); | ||
levelFunc.trace = trace.bind(levelObj); | ||
levelFunc.table = table.bind(levelObj); | ||
logger[level] = levelFunc; | ||
} | ||
// \o/ | ||
return logger; | ||
@@ -186,0 +205,0 @@ } |
{ | ||
"name": "another-logger", | ||
"version": "3.0.0-pre2", | ||
"version": "3.0.0-pre3", | ||
"description": "Yet another Node console.log alternative.", | ||
@@ -10,6 +10,8 @@ "main": "index.js", | ||
"scripts": { | ||
"test": "eslint . && mocha" | ||
"lint": "eslint .", | ||
"test": "ava" | ||
}, | ||
"devDependencies": { | ||
"@geo1088/eslint-config": "^3.0.8", | ||
"ava": "^2.0.0", | ||
"eslint": "^5.6.1" | ||
@@ -16,0 +18,0 @@ }, |
@@ -37,3 +37,3 @@ # another-logger [![npm](https://img.shields.io/npm/v/another-logger.svg)](https://www.npmjs.com/package/another-logger) | ||
}, | ||
ignoredLevels: ['debug'] | ||
ignoredLevels: ['debug'], | ||
}); | ||
@@ -53,4 +53,4 @@ | ||
### `const log = require('another-logger')(config)` | ||
### `const log = require('another-logger')` | ||
### `const log = require('another-logger')(config);` | ||
### `const log = require('another-logger');` | ||
@@ -65,3 +65,3 @@ When requiring the module, you get two things in one: a default logger instance, and a constructor function that can be called to create additional logger instances. When calling the constructor function, the argument `config` is an object with the following properties: | ||
- `style` - The style to use for displaying this level's name. This can be a function or a string; if it's a string, it will be parsed as a space and/or period-separated list of [`chalk`](https://npmjs.com/package/chalk)'s named styles (red, gray, bgBlue, etc). See that package's README for a full list. | ||
- `style` - The style to use for displaying this level's name. This can be a function or a string; if it's a string, it will be parsed as a space- and/or period-separated list of [`chalk`](https://npmjs.com/package/chalk)'s named styles (red, gray, bgBlue, etc). See that package's README for a full list. | ||
@@ -93,8 +93,8 @@ - `stream` - The stream this log should output to. Overrides the logger setting on a per-level basis. | ||
levels: { | ||
uhoh: {text: 'Uh-oh!', style: 'magenta'} | ||
} | ||
uhoh: {text: 'Uh-oh!', style: 'magenta'}, | ||
}, | ||
}); | ||
``` | ||
### `log.<name>(content...)` | ||
### `log.<name>(content...);` | ||
@@ -108,3 +108,3 @@ Execute a log. `name` can be any level name - one of the defaults of `debug`, `info`, `success`, `warn`, or `error`, or a custom one provided in the logger's config. Content arguments are processed via `require('util').format()` which means it works in the same way as `console.log` in regards to format strings, object previewing, etc. | ||
### `log.<name>.trace(content...)` | ||
### `log.<name>.trace(content...);` | ||
@@ -120,3 +120,3 @@ The same as the normal log, but appends a stack trace to the log output. Essentially the same as `console.trace()`. | ||
### `log.<name>.table(tabularData, properties?)` | ||
### `log.<name>.table(tabularData, properties?);` | ||
@@ -137,4 +137,6 @@ Tries to generate and display a table from the object or array `tabularData`, displaying only properties whose names are in `properties` if it is passed. Logs the argument plainly if a table can't be generated. Throws an error if `properties` is given and is not an array of strings. For more information, see the [Node.js `Console.table` docs](https://nodejs.org/docs/v11.6.0/api/console.html#console_console_table_tabulardata_properties). | ||
//=> └─────────┴───┴───┴───┘ | ||
``` | ||
## License | ||
MIT © 2018 Geo1088 |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
72531
12
383
136
0
3