You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

@studio/log

Package Overview
Dependencies
Maintainers
2
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@studio/log - npm Package Compare versions

Comparing version
2.0.0
to
2.1.0
+83
lib/log.d.ts
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;
{
"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": {

# 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