@studio/log
Advanced tools
+83
| export = logger; | ||
| /** | ||
| * @typedef {LoggerBase & LoggerAPI} Logger | ||
| */ | ||
| /** | ||
| * @param {string} ns | ||
| * @param {LogData} [data] | ||
| * @returns {Logger} | ||
| */ | ||
| declare function logger(ns: string, data?: LogData | undefined): Logger; | ||
| declare namespace logger { | ||
| export { pipe, hasStream, reset, LogTopic, Logger, Writable, LogValue, LogArray, LogData, LogErrorProps, LogError, LogEntry, LoggerAPI }; | ||
| } | ||
| type Logger = LoggerBase & LoggerAPI; | ||
| /** | ||
| * @param {Writable} stream | ||
| * @returns {Writable} | ||
| */ | ||
| declare function pipe(stream: Writable): Writable; | ||
| /** | ||
| * @returns {boolean} | ||
| */ | ||
| declare function hasStream(): boolean; | ||
| declare function reset(): void; | ||
| type LogTopic = (() => void) & ((arg0: unknown) => void) & ((arg0: string | LogData, arg1: unknown) => void) & ((arg0: string, arg1: LogData, arg2: unknown) => void); | ||
| type Writable = import('stream').Writable; | ||
| type LogValue = undefined | null | boolean | number | string | LogValue[] | LogData; | ||
| type LogArray = LogValue[]; | ||
| type LogData = { | ||
| [k: string]: LogValue; | ||
| }; | ||
| type LogErrorProps = { | ||
| code?: string | undefined; | ||
| cause?: Error | LogError | undefined; | ||
| }; | ||
| type LogError = Error & LogErrorProps; | ||
| type LogEntry = { | ||
| ts: number; | ||
| ns: string; | ||
| topic: string; | ||
| msg?: string | undefined; | ||
| data?: LogData | undefined; | ||
| stack?: string | undefined; | ||
| cause?: string | undefined; | ||
| }; | ||
| type LoggerAPI = { | ||
| ok: LogTopic; | ||
| warn: LogTopic; | ||
| error: LogTopic; | ||
| issue: LogTopic; | ||
| ignore: LogTopic; | ||
| input: LogTopic; | ||
| output: LogTopic; | ||
| send: LogTopic; | ||
| receive: LogTopic; | ||
| fetch: LogTopic; | ||
| finish: LogTopic; | ||
| launch: LogTopic; | ||
| terminate: LogTopic; | ||
| spawn: LogTopic; | ||
| broadcast: LogTopic; | ||
| disk: LogTopic; | ||
| timing: LogTopic; | ||
| money: LogTopic; | ||
| numbers: LogTopic; | ||
| wtf: LogTopic; | ||
| }; | ||
| declare class LoggerBase { | ||
| /** | ||
| * @param {string} ns | ||
| */ | ||
| constructor(ns: string); | ||
| /** @type {string} */ | ||
| ns: string; | ||
| /** @type {LogData | null} */ | ||
| data: LogData | null; | ||
| /** | ||
| * @param {string} ns | ||
| * @param {LogData} [data] | ||
| * @returns {Logger} | ||
| */ | ||
| child(ns: string, data?: LogData | undefined): Logger; | ||
| } |
+168
-48
@@ -10,2 +10,32 @@ /* | ||
| /** | ||
| * @typedef {import('stream').Writable} Writable | ||
| */ | ||
| /** | ||
| * @typedef {undefined | null | boolean | number | string | LogArray | LogData} LogValue | ||
| * @typedef {LogValue[]} LogArray | ||
| * @typedef {{ [k: string]: LogValue }} LogData | ||
| */ | ||
| /** | ||
| * @typedef {Object} LogErrorProps | ||
| * @property {string} [code] | ||
| * @property {Error | LogError} [cause] | ||
| */ | ||
| /** | ||
| * @typedef {Error & LogErrorProps} LogError | ||
| */ | ||
| /** | ||
| * @typedef {Object} LogEntry | ||
| * @property {number} ts | ||
| * @property {string} ns | ||
| * @property {string} topic | ||
| * @property {string} [msg] | ||
| * @property {LogData} [data] | ||
| * @property {string} [stack] | ||
| * @property {string} [cause] | ||
| */ | ||
| // Make it work with local symlinks: | ||
@@ -20,2 +50,6 @@ let state = global['@studio/log.2']; | ||
| /** | ||
| * @param {string} key | ||
| * @returns {boolean} | ||
| */ | ||
| function isCauseProperty(key) { | ||
@@ -25,2 +59,21 @@ return key !== 'name' && key !== 'message' && key !== 'stack'; | ||
| /** | ||
| * @param {string | Error | LogError} error | ||
| * @returns {string} | ||
| */ | ||
| function stack(error) { | ||
| if (typeof error === 'string') { | ||
| return error; | ||
| } | ||
| const msg = error.name && error.message | ||
| ? `${error.name}: ${error.message}` | ||
| : error.name || String(error); | ||
| const s = error.stack; | ||
| return !s ? msg : s.indexOf(msg) === 0 ? s : `${msg}\n${s}`; | ||
| } | ||
| /** | ||
| * @param {LogEntry} entry | ||
| * @param {LogError} error | ||
| */ | ||
| function addError(entry, error) { | ||
@@ -34,19 +87,27 @@ const cause = error.cause; | ||
| entry.data.code = error.code; | ||
| if (cause_props.length) { | ||
| entry.data.cause = cause_props.reduce( | ||
| (o, k) => ((o[k] = cause[k]), o), {}); | ||
| if (cause && cause_props.length) { | ||
| entry.data.cause = cause_props.reduce((o, k) => { | ||
| o[k] = cause[k]; | ||
| return o; | ||
| }, {}); | ||
| } | ||
| } | ||
| entry.stack = error.stack || String(error); | ||
| entry.stack = stack(error); | ||
| if (cause) { | ||
| entry.cause = cause.stack || String(cause); | ||
| entry.cause = stack(cause); | ||
| } | ||
| } | ||
| /** | ||
| * @param {LogData} err | ||
| * @returns {err is LogError} | ||
| */ | ||
| function errorLike(err) { | ||
| if (typeof err !== 'object') { | ||
| return false; | ||
| } | ||
| if (typeof err.stack === 'string') { | ||
| return true; | ||
| } | ||
| return typeof err === 'object' | ||
| && err.toString !== Object.prototype.toString | ||
| return err.toString !== Object.prototype.toString | ||
| && typeof err.name === 'string' | ||
@@ -56,2 +117,10 @@ && typeof err.message === 'string'; | ||
| /** | ||
| * @param {string} ns | ||
| * @param {LogData | null} base_data | ||
| * @param {string} topic | ||
| * @param {unknown} [msg] | ||
| * @param {unknown} [data] | ||
| * @param {unknown} [error] | ||
| */ | ||
| function write(ns, base_data, topic, msg, data, error) { | ||
@@ -66,3 +135,3 @@ const entry = { ts: Date.now(), ns, topic }; | ||
| if (data) { | ||
| if (errorLike(data)) { | ||
| if (errorLike(/** @type {LogData} */ (data))) { | ||
| addError(entry, data); | ||
@@ -72,3 +141,3 @@ } else { | ||
| if (error) { | ||
| addError(entry, error); | ||
| addError(entry, /** @type {LogError} */ (error)); | ||
| } | ||
@@ -82,6 +151,20 @@ } | ||
| /** | ||
| * @typedef {(function(): void) & (function(unknown): void) & (function(string | LogData, unknown): void) & (function(string, LogData, unknown): void)} LogTopic | ||
| */ | ||
| /** | ||
| * @param {string} topic | ||
| * @returns {LogTopic} | ||
| */ | ||
| function log(topic) { | ||
| return function (message, data, error) { | ||
| /** | ||
| * @param {unknown} [msg] | ||
| * @param {unknown} [data] | ||
| * @param {unknown} [error] | ||
| * @this {LoggerBase} | ||
| */ | ||
| return function (msg, data, error) { | ||
| if (state.stream) { | ||
| write(this.ns, this.data, topic, message, data, error); | ||
| write(this.ns, this.data, topic, msg, data, error); | ||
| } | ||
@@ -91,20 +174,18 @@ }; | ||
| function errorHandler() { | ||
| let writing = false; | ||
| return (err) => { | ||
| if (writing) { | ||
| return; // Prevent recursive failures | ||
| } | ||
| writing = true; | ||
| write('logger', null, 'error', 'Transform failed', err); | ||
| writing = false; | ||
| }; | ||
| } | ||
| class Logger { | ||
| class LoggerBase { | ||
| /** | ||
| * @param {string} ns | ||
| */ | ||
| constructor(ns) { | ||
| /** @type {string} */ | ||
| this.ns = ns; | ||
| /** @type {LogData | null} */ | ||
| this.data = null; | ||
| } | ||
| /** | ||
| * @param {string} ns | ||
| * @param {LogData} [data] | ||
| * @returns {Logger} | ||
| */ | ||
| child(ns, data) { | ||
@@ -116,32 +197,71 @@ if (this.data) { | ||
| } | ||
| } | ||
| /** | ||
| * @typedef {Object} LoggerAPI | ||
| * @property {LogTopic} ok | ||
| * @property {LogTopic} warn | ||
| * @property {LogTopic} error | ||
| * @property {LogTopic} issue | ||
| * @property {LogTopic} ignore | ||
| * @property {LogTopic} input | ||
| * @property {LogTopic} output | ||
| * @property {LogTopic} send | ||
| * @property {LogTopic} receive | ||
| * @property {LogTopic} fetch | ||
| * @property {LogTopic} finish | ||
| * @property {LogTopic} launch | ||
| * @property {LogTopic} terminate | ||
| * @property {LogTopic} spawn | ||
| * @property {LogTopic} broadcast | ||
| * @property {LogTopic} disk | ||
| * @property {LogTopic} timing | ||
| * @property {LogTopic} money | ||
| * @property {LogTopic} numbers | ||
| * @property {LogTopic} wtf | ||
| */ | ||
| for (const topic of Object.keys(topics)) { | ||
| LoggerBase.prototype[topic] = log(topic); | ||
| } | ||
| Object.keys(topics).forEach((topic) => { | ||
| Logger.prototype[topic] = log(topic); | ||
| }); | ||
| /** | ||
| * @typedef {LoggerBase & LoggerAPI} Logger | ||
| */ | ||
| module.exports = Object.assign((ns, data) => { | ||
| const log = state.loggers[ns] || (state.loggers[ns] = new Logger(ns)); | ||
| log.data = data || null; | ||
| return log; | ||
| }, { | ||
| /** | ||
| * @param {string} ns | ||
| * @param {LogData} [data] | ||
| * @returns {Logger} | ||
| */ | ||
| function logger(ns, data) { | ||
| const l = state.loggers[ns] || (state.loggers[ns] = new LoggerBase(ns)); | ||
| l.data = data || null; | ||
| return l; | ||
| } | ||
| logger.pipe = pipe; | ||
| logger.hasStream = hasStream; | ||
| logger.reset = reset; | ||
| pipe(stream) { | ||
| if (stream) { | ||
| stream.on('error', errorHandler()); | ||
| } | ||
| state.stream = stream; | ||
| return stream; | ||
| }, | ||
| /** | ||
| * @param {Writable} stream | ||
| * @returns {Writable} | ||
| */ | ||
| function pipe(stream) { | ||
| state.stream = stream; | ||
| return stream; | ||
| } | ||
| hasStream() { | ||
| return Boolean(state.stream); | ||
| }, | ||
| /** | ||
| * @returns {boolean} | ||
| */ | ||
| function hasStream() { | ||
| return Boolean(state.stream); | ||
| } | ||
| reset() { | ||
| state.loggers = {}; | ||
| state.stream = null; | ||
| } | ||
| function reset() { | ||
| state.loggers = {}; | ||
| state.stream = null; | ||
| } | ||
| }); | ||
| module.exports = logger; |
+26
-11
| { | ||
| "name": "@studio/log", | ||
| "version": "2.0.0", | ||
| "version": "2.1.0", | ||
| "description": "A tiny streaming ndJSON logger", | ||
@@ -8,7 +8,12 @@ "main": "lib/log.js", | ||
| "lint": "eslint .", | ||
| "test": "mocha && mochify", | ||
| "test": "npm run test:node && npm run test:browser", | ||
| "test:node": "mocha", | ||
| "test:browser": "mochify", | ||
| "watch": "mocha --watch", | ||
| "posttest": "npm run lint", | ||
| "preversion": "npm test", | ||
| "version": "changes --commits", | ||
| "build": "tsc --project tsconfig.pack.json", | ||
| "clean": "rimraf --glob 'lib/*.d.ts'", | ||
| "prepack": "npm run build", | ||
| "postpack": "npm run clean", | ||
| "preversion": "npm run lint && tsc && npm test", | ||
| "version": "changes --commits --footer", | ||
| "postversion": "git push --follow-tags && npm publish" | ||
@@ -28,2 +33,7 @@ }, | ||
| }, | ||
| "mochify": { | ||
| "driver": "puppeteer", | ||
| "bundle": "esbuild --color --bundle --sourcemap=inline --define:global=window --define:process.env.NODE_DEBUG=\"\"", | ||
| "bundle_stdin": "require" | ||
| }, | ||
| "dependencies": { | ||
@@ -33,10 +43,15 @@ "@studio/log-topics": "^1.0.0" | ||
| "devDependencies": { | ||
| "@sinonjs/referee-sinon": "^4.0.0", | ||
| "@studio/changes": "^1.5.0", | ||
| "@mochify/cli": "^0.4.1", | ||
| "@mochify/driver-puppeteer": "^0.3.1", | ||
| "@sinonjs/referee-sinon": "^12.0.0", | ||
| "@studio/browser-stream": "^1.0.0", | ||
| "@studio/eslint-config": "^1.0.2", | ||
| "@studio/changes": "^3.0.0", | ||
| "@studio/eslint-config": "^6.0.0", | ||
| "@studio/ndjson": "^2.0.0", | ||
| "eslint": "^5.0.1", | ||
| "mocha": "^5.2.0", | ||
| "mochify": "^5.8.0" | ||
| "@studio/tsconfig": "^1.3.0", | ||
| "esbuild": "^0.20.0", | ||
| "eslint": "^8.56.0", | ||
| "mocha": "^10.2.0", | ||
| "rimraf": "^5.0.5", | ||
| "typescript": "^5.3.3" | ||
| }, | ||
@@ -43,0 +58,0 @@ "repository": { |
-192
| # Changes | ||
| ## 2.0.0 | ||
| With this release, Studio Log becomes a tiny 3.3KB library. Formatters and the | ||
| CLI have been moved to separate modules and with the new `console` format, | ||
| Studio Log can be used in browsers too. | ||
| The most important API change is the removal of the default transform. | ||
| Updated examples of how to configure the logger can be found in the README. | ||
| - 💥 [`3750908`](https://github.com/javascript-studio/studio-log/commit/37509087ea324ed19158431bd3eebf748c0b919b) | ||
| __BREAKING__: Slim down API | ||
| > - Change `out` to `pipe` and let it return the stream instead of the | ||
| > logger. | ||
| > - Remove `transform`. Use stream pipes instead. | ||
| > - Remove `mute` and `muteAll`. Use a custom transform instead. | ||
| > - Remove `filter`. Use a custom trnasform instead. | ||
| > - Remove default transform. Add a serializing transform like Studio | ||
| > ndjson to the pipeline yourself. | ||
| - 💥 [`8da64cc`](https://github.com/javascript-studio/studio-log/commit/8da64cc19b7f36140ce07e456d1080753f41e010) | ||
| __BREAKING__: Extract format and CLI modules | ||
| > - Move topics into `@studio/log-topics` module | ||
| > - Move format into `@studio/log-format` module | ||
| > - Move emojilog into `@studio/emojilog` module | ||
| - 📚 [`612f818`](https://github.com/javascript-studio/studio-log/commit/612f818ddf24c1953068df49497c44a5150ebe47) | ||
| Document v2.0 API changes | ||
| - 📚 [`eca4548`](https://github.com/javascript-studio/studio-log/commit/eca4548ac425a3905b71a27b6f0068670077a815) | ||
| Improve "Transform streams" documentation | ||
| - 📚 [`6096722`](https://github.com/javascript-studio/studio-log/commit/6096722f9f1616bf5bb089f7bf7d92a0bca2aef0) | ||
| Use new Studio Changes `--commits` feature | ||
| - ✨ [`281934c`](https://github.com/javascript-studio/studio-log/commit/281934c63451728c0faca7180cc2e91ca0c014bf) | ||
| Add test runner for browser support | ||
| - ✨ [`583ed68`](https://github.com/javascript-studio/studio-log/commit/583ed68e631344d40f9cf9c5624e2e6a1bea705c) | ||
| Use Sinon + Referee | ||
| ## 1.7.5 | ||
| - 🐛 Adjust whitespace after emoji to be consistent | ||
| > With Unicode 9 most emoji are rendered with the correct width now. Some | ||
| > still need an extra space though. This changes the spacing to make them | ||
| > look consistent. | ||
| ## 1.7.4 | ||
| - 🐛 Log all non-error related cause properties | ||
| > Previously, only the `code` property of the cause error was logged. With | ||
| > this change any property that is not `name`, `message` or `stack` is | ||
| > added to the `data.cause` object. | ||
| ## 1.7.3 | ||
| - 🐛 Handle error like objects correctly | ||
| ## 1.7.2 | ||
| - 🐛 Fix --map if chunks have multiple lines | ||
| > When passing `--map sourcemaps.map` to `emojilog`, the created transform | ||
| > stream expected each chunk to contain a single line. With this change, | ||
| > the sourcemaps lookup also works for multiline chunks. | ||
| - ✨ Use Sinon 5 default sandbox | ||
| - 📚 Fix typo in message docs | ||
| ## 1.7.1 | ||
| - 🐛 Fix unwiring filters | ||
| > Filters must be unwired before re-configuring. This refactoring also | ||
| > removes some duplication in reset. | ||
| ## 1.7.0 | ||
| - 🍏 Allow to add filters directly to a child namespace | ||
| ## 1.6.0 | ||
| - 🍏 Add source maps support | ||
| > Use `--map source.js.map` to specify a source maps file. | ||
| ## 1.5.1 | ||
| - 🐛 Restore Node 4 compatibility | ||
| - 📚 Add `cause.js` example | ||
| - 📚 Move `demo.js` into examples dir | ||
| ## 1.5.0 | ||
| - 🍏 Serialize the error `cause` as a new JSON property | ||
| - 🍏 Serialize the error `code` into the `data` object | ||
| - 🍏 Serialize the error `cause.code` into the `data` object | ||
| - 🍏 Support the new `cause` property in the basic and fancy formatters | ||
| - 📚 Add new feature to docs and improve usage example and API docs | ||
| - 📚 Add cause example to demo | ||
| ## 1.4.1 | ||
| - ✨ Add install instructions | ||
| ## 1.4.0 | ||
| - 🍏 Add global log filter stream support | ||
| > A global filter stream can be configured which will receive all log | ||
| > entries before they are passed to the transform stream. This can be used | ||
| > to enrich the log data with generic environment information. | ||
| - 🍏 Add support for logger base data | ||
| > When creating a logger, a `data` property can be passed which will be | ||
| > included in the `data` of each log entry. | ||
| - 🍏 Add support for child loggers | ||
| > Child loggers have their namespace joined with their parent by a blank | ||
| > and the `data` property of the parent and the child logger are merged. | ||
| - 🍏 Add `mute()` to logger instance | ||
| - 🐛 Do not invoke filters if out stream was removed | ||
| ## 1.3.0 | ||
| - 🍏 Add log instance filter stream support | ||
| > Filters are object streams to modify the log data before passing it to | ||
| > the transform stream. They can be used to x-out confidential information | ||
| > or add generated information to log entries. | ||
| - ✨ Add npm 5 `package-lock.json` | ||
| ## 1.2.0 | ||
| The ndjson parsing and serialization was refactored into [a separate | ||
| module][studio-ndjson]. This enables error handling for serialization failures. | ||
| - 🍏 Use the [Studio ndjson][studio-ndjson] parser transform | ||
| - 🍏 Handle transform error events. If a transform error occurs, an error | ||
| message is logged instead of throwing up the stack. | ||
| - 🍏 Replace the internal default transform with the more robust implementation | ||
| from [Studio ndjson][studio-ndjson]. | ||
| - ✨ Make log functions no-ops if no output is given. This avoids pointless | ||
| `JSON.stringify` invocations and therefore improves performance a tiny bit. | ||
| [studio-ndjson]: https://github.com/javascript-studio/studio-ndjson | ||
| ## 1.1.1 | ||
| 🐛 Fix screenshot image to work outside of GitHub | ||
| ## 1.1.0 | ||
| 🍏 Add `hasStream()` to the API which returns whether an output stream was set. | ||
| ## 1.0.5 | ||
| Fixes and improvements for the fancy format transform. | ||
| - 🐛 Escape all non-printable characters. Print escape sequences, if available, | ||
| and fall back to hex values. Do not escape emoji‼️ | ||
| - 🐛 Escape newlines and tabs in strings (Fixes #3) | ||
| - 🐛 Format empty objects as `{}` without blanks (Fixes #1) | ||
| - 🐛 Format primitive data values (Fixes #4) | ||
| ## 1.0.4 | ||
| 🙈 Support Node 4 | ||
| ## 1.0.3 | ||
| ✨ Handle non-json prefix in `emojilog`. Attempt to parse JSON starting from | ||
| the first occurrence of the `{` character. Anything before that is forwarded to | ||
| stdout. | ||
| ## 1.0.2 | ||
| 🐛 Make it work with local symlinks | ||
| ## 1.0.1 | ||
| 🙈 Disabled by default | ||
| ## 1.0.0 | ||
| ✨ Initial release |
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
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
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
317
166.39%16772
-5.38%13
62.5%3
200%