Compare commits
1 Commits
developmen
...
bugfix/S3C
Author | SHA1 | Date |
---|---|---|
bbuchanan9 | 52e6fc82ea |
|
@ -1,31 +0,0 @@
|
|||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- development/**
|
||||
- q/*/**
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkokut
|
||||
uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
run: sudo apt-get update -q
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '16'
|
||||
- name: Install Yarn
|
||||
run: npm install -g yarn
|
||||
- name: install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: run lint
|
||||
run: echo "linter is disabled temporarily ()" || yarn run --silent lint -- --max-warnings 0
|
||||
- name: run lint_md
|
||||
run: yarn --silent lint_md
|
||||
- name: run test
|
||||
run: yarn test
|
||||
- name: run coverage
|
||||
run: yarn coverage
|
11
README.md
11
README.md
|
@ -1,5 +1,6 @@
|
|||
# WereLogs
|
||||
|
||||
[![CircleCI][badgepub]](https://circleci.com/gh/scality/werelogs)
|
||||
[![Scality CI][badgepriv]](http://ci.ironmann.io/gh/scality/werelogs)
|
||||
|
||||
This repository provides a NodeJS Library that aims to be an efficient logging
|
||||
|
@ -19,7 +20,7 @@ https://github.com/scality/Guidelines/blob/master/CONTRIBUTING.md).
|
|||
In order to install WereLogs, you can use NPM with github's HTTP url, and save
|
||||
it in your own package.json:
|
||||
|
||||
```sh
|
||||
```
|
||||
$> npm i --save scality/werelogs
|
||||
```
|
||||
|
||||
|
@ -157,3 +158,11 @@ In order to find out the known issues, it is advised to take a look at the
|
|||
[project's github page](http://github.com/scality/werelogs). There, you should
|
||||
be able to find the issues, tagged with the releases they are impacting,
|
||||
whether they're open or closed.
|
||||
|
||||
## Contributing
|
||||
|
||||
The contributing rules for this project are defined in the associated
|
||||
CONTRIBUTING.md file.
|
||||
|
||||
[badgepub]: https://circleci.com/gh/scality/werelogs.svg?style=svg
|
||||
[badgepriv]: http://ci.ironmann.io/gh/scality/werelogs.svg?style=svg&circle-token=a946e81ad65b99814403b5e57f017d9ecbe93f0a
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
general:
|
||||
branches:
|
||||
ignore:
|
||||
- /^ultron\/.*/ # Ignore ultron/* branches
|
||||
artifacts:
|
||||
- coverage/
|
||||
- doc/
|
||||
|
||||
machine:
|
||||
node:
|
||||
version: 6.13.1
|
||||
test:
|
||||
override:
|
||||
- npm run lint_md
|
||||
- npm run lint -- --max-warnings 0
|
||||
- npm run gendoc
|
||||
- npm run coverage
|
||||
# running ft_test packs werelogs and installs it + deps into
|
||||
# tests/functional. Pack is like publishing werelogs in a local tgz
|
||||
# archive that can be installed.
|
||||
# This step shall ensure that no issue is encountered when installing
|
||||
# the package, and allows to functionally test werelogs.
|
||||
- npm run ft_test
|
|
@ -1,65 +1,45 @@
|
|||
interface WerelogsConfigOptions {
|
||||
level?: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
||||
dump?: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
||||
streams?: object[];
|
||||
}
|
||||
interface config {}
|
||||
|
||||
declare class WerelogsConfig {
|
||||
constructor(config?: WerelogsConfigOptions);
|
||||
reset(): WerelogsConfig;
|
||||
update(config: WerelogsConfig): WerelogsConfig;
|
||||
logger: any;
|
||||
level: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
||||
dump: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
||||
end: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
||||
}
|
||||
|
||||
interface LogDictionary {
|
||||
interface LogDictionnary {
|
||||
httpMethod?: string;
|
||||
httpURL?: string;
|
||||
[field: string]: any;
|
||||
}
|
||||
|
||||
declare module 'werelogs' {
|
||||
export class RequestLogger {
|
||||
constructor(
|
||||
logger: any,
|
||||
logLevel: string,
|
||||
dumpThreshold: string,
|
||||
endLevel: string,
|
||||
uids?: string|Array<string>
|
||||
);
|
||||
getUids(): Array<string>;
|
||||
getSerializedUids(): string;
|
||||
addDefaultFields(fields: LogDictionary): LogDictionary;
|
||||
trace(msg: string, data?: LogDictionary): void;
|
||||
debug(msg: string, data?: LogDictionary): void;
|
||||
info(msg: string, data?: LogDictionary): void;
|
||||
warn(msg: string, data?: LogDictionary): void;
|
||||
error(msg: string, data?: LogDictionary): void;
|
||||
fatal(msg: string, data?: LogDictionary): void;
|
||||
end(msg: string, data?: LogDictionary): void;
|
||||
errorEnd(msg: string, data?:LogDictionary): void;
|
||||
}
|
||||
declare class RequestLogger {
|
||||
constructor(
|
||||
logger: any,
|
||||
logLevel: string,
|
||||
dumpThreshold: string,
|
||||
endLevel: string,
|
||||
uids?: string|Array<string>
|
||||
);
|
||||
getUids(): Array<string>;
|
||||
getSerializedUids(): string;
|
||||
addDefaultFields(fields: LogDictionnary): LogDictionnary;
|
||||
trace(msg: string, data?: LogDictionnary): void;
|
||||
debug(msg: string, data?: LogDictionnary): void;
|
||||
info(msg: string, data?: LogDictionnary): void;
|
||||
warn(msg: string, data?: LogDictionnary): void;
|
||||
error(msg: string, data?: LogDictionnary): void;
|
||||
fatal(msg: string, data?: LogDictionnary): void;
|
||||
end(msg: string, data?: LogDictionnary): void;
|
||||
}
|
||||
|
||||
declare module 'werelogs' {
|
||||
export class Logger {
|
||||
name: string;
|
||||
constructor(name: string);
|
||||
|
||||
constructor(name: string, config?: config);
|
||||
setLevel(levelName: string): void;
|
||||
setDumpLevelThreshold(levelName: string): void;
|
||||
newRequestLogger(uids?: string|Array<string>): RequestLogger;
|
||||
newRequestLoggerFromSerializedUids(uids: string): RequestLogger;
|
||||
trace(msg: string, data?: LogDictionary): void;
|
||||
debug(msg: string, data?: LogDictionary): void;
|
||||
info(msg: string, data?: LogDictionary): void;
|
||||
warn(msg: string, data?: LogDictionary): void;
|
||||
error(msg: string, data?: LogDictionary): void;
|
||||
fatal(msg: string, data?: LogDictionary): void;
|
||||
}
|
||||
|
||||
export function configure(config: WerelogsConfigOptions): void;
|
||||
|
||||
export class API {
|
||||
constructor(config: WerelogsConfigOptions);
|
||||
reconfigure(config: WerelogsConfigOptions): void;
|
||||
Logger: Logger;
|
||||
trace(msg: string, data?: LogDictionnary): void;
|
||||
debug(msg: string, data?: LogDictionnary): void;
|
||||
info(msg: string, data?: LogDictionnary): void;
|
||||
warn(msg: string, data?: LogDictionnary): void;
|
||||
error(msg: string, data?: LogDictionnary): void;
|
||||
fatal(msg: string, data?: LogDictionnary): void;
|
||||
}
|
||||
}
|
||||
|
|
37
index.js
37
index.js
|
@ -1,5 +1,4 @@
|
|||
const API = require('./lib/api.js');
|
||||
const stderrUtils = require('./lib/stderrUtils');
|
||||
|
||||
/*
|
||||
* For convenience purposes, we provide an already instanciated API; so that
|
||||
|
@ -12,40 +11,4 @@ module.exports = {
|
|||
Logger: werelogs.Logger,
|
||||
configure: werelogs.reconfigure.bind(werelogs),
|
||||
Werelogs: API,
|
||||
/**
|
||||
* Timestamp logs going to stderr
|
||||
*
|
||||
* @example <caption>Simplest usage</caption>
|
||||
* ```
|
||||
* const { stderrUtils } = require('werelogs');
|
||||
* stderrUtils.catchAndTimestampStderr();
|
||||
* ```
|
||||
*
|
||||
* @example <caption>Manage process exit</caption>
|
||||
* ```
|
||||
* const { stderrUtils } = require('werelogs');
|
||||
* // set exitCode to null to keep process running on uncaughtException
|
||||
* stderrUtils.catchAndTimestampStderr(undefined, null);
|
||||
* // application init
|
||||
* process.on('uncaughtException', (err) => {
|
||||
* // custom handling, close connections, files
|
||||
* this.worker.kill(); // or process.exit(1);
|
||||
* });
|
||||
* // Note you could use prependListener to execute your callback first
|
||||
* // and then let stderrUtils exit the process.
|
||||
* ```
|
||||
*
|
||||
* @example <caption>Custom listener</caption>
|
||||
* ```
|
||||
* const { stderrUtils } = require('werelogs');
|
||||
* stderrUtils.catchAndTimestampWarning();
|
||||
* // application init
|
||||
* process.on('uncaughtException', (err, origin) => {
|
||||
* stderrUtils.printErrorWithTimestamp(err, origin);
|
||||
* // close and stop everything
|
||||
* process.exit(1);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
stderrUtils,
|
||||
};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
// eslint-disable-line strict
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const LogLevel = require('./LogLevel.js');
|
||||
const SimpleLogger = require('./SimpleLogger.js');
|
||||
|
@ -98,11 +97,10 @@ class Config {
|
|||
// for check log level vs. log dump level
|
||||
const newLogLevel = checkedConfig.level || this.logLevel;
|
||||
const newLogDumpLevel = checkedConfig.dump || this.dumpThreshold;
|
||||
if (newLogDumpLevel
|
||||
&& !LogLevel.shouldLog(newLogDumpLevel, newLogLevel)) {
|
||||
if (newLogDumpLevel &&
|
||||
!LogLevel.shouldLog(newLogDumpLevel, newLogLevel)) {
|
||||
throw new Error(
|
||||
'Logging level should be at most logging dump level',
|
||||
);
|
||||
'Logging level should be at most logging dump level');
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(checkedConfig, 'level')) {
|
||||
|
@ -124,8 +122,8 @@ class Config {
|
|||
+ 'of Writeable Streams.');
|
||||
}
|
||||
if (!checkedConfig.streams.length) {
|
||||
throw new Error('Werelogs config.streams must contain at '
|
||||
+ 'least one stream.');
|
||||
throw new Error('Werelogs config.streams must contain at ' +
|
||||
'least one stream.');
|
||||
}
|
||||
this.streams = checkedConfig.streams.map(stream => {
|
||||
stream.level = 'trace'; // eslint-disable-line no-param-reassign
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
// eslint-disable-line strict
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const logLevels = [
|
||||
'trace',
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
|
||||
// eslint-disable-line strict
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const LogLevel = require('./LogLevel.js');
|
||||
const RequestLogger = require('./RequestLogger.js');
|
||||
const { unserializeUids } = require('./Utils.js');
|
||||
const unserializeUids = require('./Utils.js').unserializeUids;
|
||||
const Config = require('./Config.js');
|
||||
|
||||
class Logger {
|
||||
|
||||
/**
|
||||
* This is the constructor of the Logger class. It takes optional
|
||||
* configuration parameters, that allow to modify its behavior.
|
||||
|
@ -39,8 +39,8 @@ class Logger {
|
|||
*/
|
||||
newRequestLogger(uids) {
|
||||
const rLog = new RequestLogger(this.config.logger,
|
||||
this.config.level, this.config.dump,
|
||||
this.config.end, uids);
|
||||
this.config.level, this.config.dump,
|
||||
this.config.end, uids);
|
||||
rLog.addDefaultFields({ name: this.name });
|
||||
return rLog;
|
||||
}
|
||||
|
@ -55,9 +55,9 @@ class Logger {
|
|||
*/
|
||||
newRequestLoggerFromSerializedUids(serializedUids) {
|
||||
const rLog = new RequestLogger(this.config.logger,
|
||||
this.config.level, this.config.dump,
|
||||
this.config.end,
|
||||
unserializeUids(serializedUids));
|
||||
this.config.level, this.config.dump,
|
||||
this.config.end,
|
||||
unserializeUids(serializedUids));
|
||||
rLog.addDefaultFields({ name: this.name });
|
||||
return rLog;
|
||||
}
|
||||
|
@ -74,8 +74,7 @@ class Logger {
|
|||
callparams: [msg, data],
|
||||
},
|
||||
'Werelogs API was mis-used.'
|
||||
+ ' This development error should be fixed ASAP.',
|
||||
);
|
||||
+ ' This development error should be fixed ASAP.');
|
||||
return;
|
||||
}
|
||||
if (data) {
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
// eslint-disable-line strict
|
||||
const assert = require('assert');
|
||||
|
||||
const LogLevel = require('./LogLevel.js');
|
||||
const Utils = require('./Utils.js');
|
||||
|
||||
const { serializeUids, generateUid, objectCopy } = Utils;
|
||||
const serializeUids = Utils.serializeUids;
|
||||
const generateUid = Utils.generateUid;
|
||||
const objectCopy = Utils.objectCopy;
|
||||
|
||||
function ensureUidValidity(uid) {
|
||||
if (uid.indexOf(':') !== -1) {
|
||||
throw new Error(`RequestLogger UID "${uid}" contains an illegal `
|
||||
+ 'character: \':\'.');
|
||||
throw new Error(`RequestLogger UID "${uid}" contains an illegal ` +
|
||||
'character: \':\'.');
|
||||
}
|
||||
return uid;
|
||||
}
|
||||
|
@ -21,6 +23,9 @@ class EndLogger {
|
|||
}
|
||||
|
||||
augmentedLog(level, msg, data) {
|
||||
assert.strictEqual(this.logger.elapsedTime, null, 'The logger\'s'
|
||||
+ 'end() wrapper should not be called more than'
|
||||
+ ' once.');
|
||||
// We can alter current instance, as it won't be usable after this
|
||||
// call.
|
||||
this.fields = objectCopy(this.fields, data || {});
|
||||
|
@ -138,6 +143,7 @@ class EndLogger {
|
|||
* request.
|
||||
*/
|
||||
class RequestLogger {
|
||||
|
||||
/**
|
||||
* Constructor of the WereLogs Request Logger.
|
||||
* This function takes a logger instance, a logging level, and a last
|
||||
|
@ -173,7 +179,7 @@ class RequestLogger {
|
|||
* @returns {undefined}
|
||||
*/
|
||||
constructor(logger, logLevel, dumpThreshold, endLevel, uids) {
|
||||
let uidList;
|
||||
let uidList = undefined;
|
||||
|
||||
if (!LogLevel.shouldLog(dumpThreshold, logLevel)) {
|
||||
throw new Error('Logging Dump level should be equal or'
|
||||
|
@ -365,6 +371,8 @@ class RequestLogger {
|
|||
if (msg === undefined && data === undefined) {
|
||||
return this.endLogger;
|
||||
}
|
||||
assert.strictEqual(this.elapsedTime, null, 'The "end()" logging method '
|
||||
+ 'should not be called more than once.');
|
||||
return this.log(this.endLevel, msg, data, true);
|
||||
}
|
||||
|
||||
|
@ -381,6 +389,8 @@ class RequestLogger {
|
|||
* @returns {undefined}
|
||||
*/
|
||||
errorEnd(msg, data) {
|
||||
assert.strictEqual(this.elapsedTime, null, 'The "end()" logging method '
|
||||
+ 'should not be called more than once.');
|
||||
return this.log('error', msg, data, true);
|
||||
}
|
||||
|
||||
|
@ -422,8 +432,7 @@ class RequestLogger {
|
|||
+ ' This development error should be fixed ASAP.',
|
||||
{
|
||||
callparams: [msg, logFields],
|
||||
},
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
const fields = objectCopy({}, this.fields, logFields || {});
|
||||
|
@ -440,12 +449,6 @@ class RequestLogger {
|
|||
// eslint-disable-next-line camelcase
|
||||
fields.req_id = serializeUids(this.uids);
|
||||
if (endFlag) {
|
||||
if (this.elapsedTime !== null) {
|
||||
// reset elapsedTime to avoid an infinite recursion
|
||||
// while logging the error
|
||||
this.elapsedTime = null;
|
||||
this.error('RequestLogger.end() has been called more than once');
|
||||
}
|
||||
this.elapsedTime = process.hrtime(this.startTime);
|
||||
// eslint-disable-next-line camelcase
|
||||
fields.elapsed_ms = this.elapsedTime[0] * 1000
|
||||
|
|
|
@ -1,17 +1,7 @@
|
|||
|
||||
// eslint-disable-line strict
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const os = require('os');
|
||||
const safeJSONStringify = require('safe-json-stringify');
|
||||
const fastJSONStringify = require('fast-safe-stringify')
|
||||
|
||||
function errorStackReplacer(key, value) {
|
||||
if (value instanceof Error) {
|
||||
return value.stack;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function safely stringifies JSON. If an exception occcurs (due to
|
||||
* circular references, exceptions thrown from object getters etc.), the module
|
||||
|
@ -23,13 +13,12 @@ function errorStackReplacer(key, value) {
|
|||
function safeStringify(obj) {
|
||||
let str;
|
||||
try {
|
||||
// Try to stringify the object (fast version)
|
||||
str = fastJSONStringify(obj, errorStackReplacer);
|
||||
str = JSON.stringify(obj);
|
||||
} catch (e) {
|
||||
// fallback to remove circular object references or other exceptions
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
obj.unsafeJSON = true;
|
||||
return safeJSONStringify(obj, errorStackReplacer);
|
||||
return safeJSONStringify(obj);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
@ -48,8 +37,8 @@ class SimpleLogger {
|
|||
this.streams = [{ level: 'trace', stream: process.stdout }];
|
||||
if (streams) {
|
||||
if (!Array.isArray(streams)) {
|
||||
throw new Error('Invalid streams. streams must be an array'
|
||||
+ ' list of writeable streams');
|
||||
throw new Error('Invalid streams. streams must be an array' +
|
||||
' list of writeable streams');
|
||||
}
|
||||
/*
|
||||
* This is for backwards compatibility. current config in projects
|
||||
|
@ -79,9 +68,8 @@ class SimpleLogger {
|
|||
logFields.hostname = this.hostname;
|
||||
logFields.pid = process.pid;
|
||||
|
||||
const safeString = safeStringify(logFields);
|
||||
this.streams.forEach(s => s.stream
|
||||
.write(`${safeString}\n`));
|
||||
.write(`${safeStringify(logFields)}\n`));
|
||||
}
|
||||
|
||||
info(fields, message) {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
// eslint-disable-line strict
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
/**
|
||||
* @constant
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
// eslint-disable-line strict
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const Config = require('./Config.js');
|
||||
const Logger = require('./Logger.js');
|
||||
|
||||
class API {
|
||||
|
||||
/**
|
||||
* This is the constructor of the Logger class. It takes optional
|
||||
* configuration parameters, that allow to modify its behavior.
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
/**
|
||||
* @returns {string} a timestamp in ISO format YYYY-MM-DDThh:mm:ss.sssZ
|
||||
*/
|
||||
const defaultTimestamp = () => new Date().toISOString();
|
||||
|
||||
/**
|
||||
* Prints on stderr a timestamp, the origin and the error
|
||||
*
|
||||
* If no other instructions are needed on uncaughtException,
|
||||
* consider using `catchAndTimestampStderr` directly.
|
||||
*
|
||||
* @example
|
||||
* process.on('uncaughtException', (err, origin) => {
|
||||
* printErrorWithTimestamp(err, origin);
|
||||
* // server.close();
|
||||
* // file.close();
|
||||
* process.nextTick(() => process.exit(1));
|
||||
* });
|
||||
* // Don't forget to timestamp warning
|
||||
* catchAndTimestampWarning();
|
||||
* @param {Error} err see process event uncaughtException
|
||||
* @param {uncaughtException|unhandledRejection} origin see process event
|
||||
* @param {string} [date=`defaultTimestamp()`] Date to print
|
||||
* @returns {boolean} see process.stderr.write
|
||||
*/
|
||||
function printErrorWithTimestamp(
|
||||
err, origin, date = defaultTimestamp(),
|
||||
) {
|
||||
return process.stderr.write(`${date}: ${origin}:\n${err.stack}\n`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer using `catchAndTimestampStderr` instead of this function.
|
||||
*
|
||||
* Adds listener for uncaughtException to print with timestamp.
|
||||
*
|
||||
* If you want to manage the end of the process, you can set exitCode to null.
|
||||
* Or use `printErrorWithTimestamp` in your own uncaughtException listener.
|
||||
*
|
||||
* @param {Function} [dateFct=`defaultTimestamp`] Fct returning a formatted date
|
||||
* @param {*} [exitCode=1] On uncaughtException, if not null, `process.exit`
|
||||
* will be called with this value
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function catchAndTimestampUncaughtException(
|
||||
dateFct = defaultTimestamp, exitCode = 1,
|
||||
) {
|
||||
process.on('uncaughtException', (err, origin) => {
|
||||
printErrorWithTimestamp(err, origin, dateFct());
|
||||
if (exitCode !== null) {
|
||||
process.nextTick(() => process.exit(exitCode));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the use of `--trace-warnings` and adds a date in warning.detail
|
||||
* The warning will be printed by the default `onWarning`
|
||||
*
|
||||
* @param {string} [dateFct=`defaultTimestamp`] Fct returning a formatted date
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function catchAndTimestampWarning(dateFct = defaultTimestamp) {
|
||||
process.traceProcessWarnings = true;
|
||||
// must be executed first, before the default `onWarning`
|
||||
process.prependListener('warning', warning => {
|
||||
if (warning.detail) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
warning.detail += `\nAbove Warning Date: ${dateFct()}`;
|
||||
} else {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
warning.detail = `Above Warning Date: ${dateFct()}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds listener for uncaughtException and warning to print them with timestamp.
|
||||
*
|
||||
* If you want to manage the end of the process, you can set exitCode to null.
|
||||
* Or use `printErrorWithTimestamp` in your own uncaughtException listener.
|
||||
*
|
||||
* @example
|
||||
* const { stderrUtils } = require('werelogs');
|
||||
* // first instruction in your index.js or entrypoint
|
||||
* stderrUtils.catchAndTimestampStderr();
|
||||
*
|
||||
* @param {Function} [dateFct=`defaultTimestamp`] Fct returning a formatted date
|
||||
* @param {*} [exitCode=1] On uncaughtException, if not null, `process.exit`
|
||||
* will be called with this value
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function catchAndTimestampStderr(
|
||||
dateFct = defaultTimestamp, exitCode = 1,
|
||||
) {
|
||||
catchAndTimestampUncaughtException(dateFct, exitCode);
|
||||
catchAndTimestampWarning(dateFct);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
defaultTimestamp,
|
||||
printErrorWithTimestamp,
|
||||
catchAndTimestampUncaughtException,
|
||||
catchAndTimestampWarning,
|
||||
catchAndTimestampStderr,
|
||||
};
|
31
package.json
31
package.json
|
@ -1,18 +1,18 @@
|
|||
{
|
||||
"name": "werelogs",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
"node": ">=6.9.5"
|
||||
},
|
||||
"version": "8.1.5",
|
||||
"version": "7.4.0",
|
||||
"description": "An efficient raw JSON logging library aimed at micro-services architectures.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"gendoc": "jsdoc $(git ls-files 'lib/*.js') -d doc",
|
||||
"lint": "eslint $(git ls-files '*.js')",
|
||||
"lint_md": "markdownlint $(git ls-files '*.md')",
|
||||
"lint_md": "mdlint $(git ls-files '*.md')",
|
||||
"test": "mocha tests/unit/",
|
||||
"ft_test": "(npm pack && cp werelogs-*.tgz tests/functional && cd tests/functional && cp -R ../../node_modules/ node_modules/ && npm install werelogs-*.tgz && ./node_modules/.bin/mocha . multi-modules/ && rm -rf tests/functional/node_modules tests/functional/werelogs-*.tgz tests/functional/*lock*)",
|
||||
"coverage": "nyc ./node_modules/.bin/_mocha tests/unit"
|
||||
"ft_test": "rm -rf tests/functional/node_modules && npm pack && cp -R node_modules werelogs-*.tgz tests/functional && cd tests/functional && npm install werelogs-*.tgz && ./node_modules/.bin/mocha . multi-modules/ && cd -",
|
||||
"coverage": "istanbul cover ./node_modules/.bin/_mocha tests/unit"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -33,20 +33,17 @@
|
|||
},
|
||||
"homepage": "https://github.com/scality/werelogs#readme",
|
||||
"dependencies": {
|
||||
"fast-safe-stringify": "^2.1.1",
|
||||
"safe-json-stringify": "^1.2.0"
|
||||
"safe-json-stringify": "1.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-airbnb": "^18.2.1",
|
||||
"eslint-config-scality": "git+https://git.yourcmc.ru/vitalif/zenko-eslint-config-scality.git",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-react": "^7.26.0",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-plugin-react": "^4.3.0",
|
||||
"eslint": "^2.13.1",
|
||||
"eslint-config-airbnb": "^6.2.0",
|
||||
"eslint-config-scality": "scality/Guidelines#71a059ad",
|
||||
"istanbul": "^1.0.0-alpha.2",
|
||||
"istanbul-api": "==1.0.0-alpha.9",
|
||||
"jsdoc": "^3.4.3",
|
||||
"markdownlint-cli": "^0.27.1",
|
||||
"mocha": ">=3.1.2",
|
||||
"nyc": "^15.1.0"
|
||||
"mdlint": "^0.1.0",
|
||||
"mocha": "^3.2.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
|
||||
// eslint-disable-line strict
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const LogLevel = require('../lib/LogLevel.js');
|
||||
|
||||
class DummyLogger {
|
||||
|
||||
constructor() {
|
||||
this.ops = [];
|
||||
this.counts = {
|
||||
|
@ -57,8 +57,8 @@ function computeBehavior(filterLevel, logLevel, testLevel) {
|
|||
|
||||
return {
|
||||
value,
|
||||
msg: `Expected ${logLevel} to be called ${value} times with `
|
||||
+ `filter level ${filterLevel}.`,
|
||||
msg: `Expected ${logLevel} to be called ${value} times with ` +
|
||||
`filter level ${filterLevel}.`,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -107,10 +107,9 @@ function loggingMisuseGenerator(test, createLogger) {
|
|||
logger.info.apply(logger, test.args);
|
||||
},
|
||||
Error,
|
||||
`Werelogs should not throw with ${test.desc}`,
|
||||
);
|
||||
`Werelogs should not throw with ${test.desc}`);
|
||||
assert(dummyLogger.ops[0][0], 'fatal',
|
||||
'Expected the Module Logger to have logged a fatal message.');
|
||||
'Expected the Module Logger to have logged a fatal message.');
|
||||
done();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
|
||||
// eslint-disable-line strict
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const assert = require('assert');
|
||||
const { PassThrough } = require('stream');
|
||||
|
||||
const pass = new PassThrough();
|
||||
const PassThrough = require('stream').PassThrough;
|
||||
const pass = new PassThrough;
|
||||
|
||||
const werelogs = require('werelogs'); // eslint-disable-line
|
||||
|
||||
|
@ -52,7 +50,7 @@ describe('Werelogs is usable as a dependency', () => {
|
|||
assert.doesNotThrow(
|
||||
createModuleLogger,
|
||||
Error,
|
||||
'Werelogs threw an exception trying to create a ModuleLogger.',
|
||||
'Werelogs threw an exception trying to create a ModuleLogger.'
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
@ -68,11 +66,8 @@ describe('Werelogs is usable as a dependency', () => {
|
|||
it('Should be able to log a message and additional fields', done => {
|
||||
const logger = createModuleLogger();
|
||||
const msg = 'This is a message with added fields';
|
||||
const fields = {
|
||||
errorCode: 9,
|
||||
description: 'TestError',
|
||||
options: { dump: false },
|
||||
};
|
||||
const fields = { errorCode: 9, description: 'TestError',
|
||||
options: { dump: false } };
|
||||
logger.info(msg, fields);
|
||||
assert.strictEqual(parseLogEntry().message, msg);
|
||||
checkFields(fields);
|
||||
|
@ -88,7 +83,7 @@ describe('Werelogs is usable as a dependency', () => {
|
|||
assert.doesNotThrow(
|
||||
() => createModuleLogger().newRequestLogger(),
|
||||
Error,
|
||||
'Werelogs threw an exception trying to create a ModuleLogger.',
|
||||
'Werelogs threw an exception trying to create a ModuleLogger.'
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
@ -104,11 +99,8 @@ describe('Werelogs is usable as a dependency', () => {
|
|||
it('Should be able to log a message and additional fields', done => {
|
||||
const logger = createModuleLogger().newRequestLogger();
|
||||
const msg = 'This is a message with added fields';
|
||||
const fields = {
|
||||
errorCode: 9,
|
||||
description: 'TestError',
|
||||
options: { dump: false },
|
||||
};
|
||||
const fields = { errorCode: 9, description: 'TestError',
|
||||
options: { dump: false } };
|
||||
logger.info(msg, fields);
|
||||
assert.strictEqual(parseLogEntry().message, msg);
|
||||
checkFields(fields);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const assert = require('assert');
|
||||
const { PassThrough } = require('stream');
|
||||
const PassThrough = require('stream').PassThrough;
|
||||
|
||||
const Werelogs = require('werelogs'); // eslint-disable-line
|
||||
const modules = [
|
||||
|
@ -7,8 +7,7 @@ const modules = [
|
|||
require('./module2.js'),
|
||||
require('./module3.js'),
|
||||
];
|
||||
|
||||
const pass = new PassThrough();
|
||||
const pass = new PassThrough;
|
||||
|
||||
const logBuffer = {
|
||||
records: [],
|
||||
|
@ -18,8 +17,8 @@ pass.on('data', data => {
|
|||
});
|
||||
|
||||
describe('Config is shared and unique within one API', () => {
|
||||
it('should find all log entries in the RingBuffer with the right '
|
||||
+ 'module name', done => {
|
||||
it('should find all log entries in the RingBuffer with the right ' +
|
||||
'module name', done => {
|
||||
Werelogs.configure({
|
||||
level: 'debug',
|
||||
dump: 'fatal',
|
||||
|
|
|
@ -16,8 +16,7 @@ describe('Config', () => {
|
|||
() => {
|
||||
config.logger.info('test message');
|
||||
},
|
||||
Error,
|
||||
);
|
||||
Error);
|
||||
done();
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
// eslint-disable-line strict
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
|
@ -13,8 +12,7 @@ function generateValidThrowTest(level) {
|
|||
},
|
||||
Error,
|
||||
'Expected level to be valid and '
|
||||
+ 'the function not to throw an Error.',
|
||||
);
|
||||
+ 'the function not to throw an Error.');
|
||||
done();
|
||||
};
|
||||
}
|
||||
|
@ -28,28 +26,27 @@ describe('LogLevel', () => {
|
|||
},
|
||||
RangeError,
|
||||
'Expected function to throw an Error instance due to '
|
||||
+ 'invalid log level.',
|
||||
);
|
||||
+ 'invalid log level.');
|
||||
done();
|
||||
});
|
||||
|
||||
it('should not throw on "trace" level',
|
||||
generateValidThrowTest('trace'));
|
||||
generateValidThrowTest('trace'));
|
||||
|
||||
it('should not throw on "debug" level',
|
||||
generateValidThrowTest('debug'));
|
||||
generateValidThrowTest('debug'));
|
||||
|
||||
it('should not throw on "info" level',
|
||||
generateValidThrowTest('info'));
|
||||
generateValidThrowTest('info'));
|
||||
|
||||
it('should not throw on "warn" level',
|
||||
generateValidThrowTest('warn'));
|
||||
generateValidThrowTest('warn'));
|
||||
|
||||
it('should not throw on "error" level',
|
||||
generateValidThrowTest('error'));
|
||||
generateValidThrowTest('error'));
|
||||
|
||||
it('should not throw on "fatal" level',
|
||||
generateValidThrowTest('fatal'));
|
||||
generateValidThrowTest('fatal'));
|
||||
});
|
||||
|
||||
describe('shouldLog(level, floor)', () => {
|
||||
|
@ -57,8 +54,7 @@ describe('LogLevel', () => {
|
|||
assert.strictEqual(
|
||||
LogLevel.shouldLog('trace', 'trace'),
|
||||
true,
|
||||
'Expected trace floor to allow logging trace level.',
|
||||
);
|
||||
'Expected trace floor to allow logging trace level.');
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -66,8 +62,7 @@ describe('LogLevel', () => {
|
|||
assert.strictEqual(
|
||||
LogLevel.shouldLog('debug', 'debug'),
|
||||
true,
|
||||
'Expected debug floor to allow logging debug level.',
|
||||
);
|
||||
'Expected debug floor to allow logging debug level.');
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -75,8 +70,7 @@ describe('LogLevel', () => {
|
|||
assert.strictEqual(
|
||||
LogLevel.shouldLog('info', 'info'),
|
||||
true,
|
||||
'Expected info floor to allow logging info level.',
|
||||
);
|
||||
'Expected info floor to allow logging info level.');
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -84,8 +78,7 @@ describe('LogLevel', () => {
|
|||
assert.strictEqual(
|
||||
LogLevel.shouldLog('warn', 'warn'),
|
||||
true,
|
||||
'Expected warn floor to allow logging warn level.',
|
||||
);
|
||||
'Expected warn floor to allow logging warn level.');
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -93,8 +86,7 @@ describe('LogLevel', () => {
|
|||
assert.strictEqual(
|
||||
LogLevel.shouldLog('error', 'error'),
|
||||
true,
|
||||
'Expected error floor to allow logging error level.',
|
||||
);
|
||||
'Expected error floor to allow logging error level.');
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -102,8 +94,7 @@ describe('LogLevel', () => {
|
|||
assert.strictEqual(
|
||||
LogLevel.shouldLog('fatal', 'fatal'),
|
||||
true,
|
||||
'Expected fatal floor to allow logging fatal level.',
|
||||
);
|
||||
'Expected fatal floor to allow logging fatal level.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
|
||||
// eslint-disable-line strict
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const { genericFilterGenerator, loggingMisuseGenerator, DummyLogger } = require('../Utils');
|
||||
const Utils = require('../Utils.js');
|
||||
const genericFilterGenerator = Utils.genericFilterGenerator;
|
||||
const loggingMisuseGenerator = Utils.loggingMisuseGenerator;
|
||||
const DummyLogger = Utils.DummyLogger;
|
||||
|
||||
const Config = require('../../lib/Config.js');
|
||||
const RequestLogger = require('../../lib/RequestLogger.js');
|
||||
|
@ -52,8 +54,7 @@ describe('Logger is usable:', () => {
|
|||
assert.throws(
|
||||
() => new Logger(),
|
||||
TypeError,
|
||||
'Logger Instanciation should not succeed without parameter.',
|
||||
);
|
||||
'Logger Instanciation should not succeed without parameter.');
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -61,8 +62,7 @@ describe('Logger is usable:', () => {
|
|||
assert.throws(
|
||||
() => new Logger(config),
|
||||
TypeError,
|
||||
'Logger Instanciation should not be succeed without a name.',
|
||||
);
|
||||
'Logger Instanciation should not be succeed without a name.');
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -70,8 +70,7 @@ describe('Logger is usable:', () => {
|
|||
assert.throws(
|
||||
() => new Logger({ level: 'info' }, 'WereLogsTest'),
|
||||
TypeError,
|
||||
'Logger Instanciation should not succeed with a bad config type.',
|
||||
);
|
||||
'Logger Instanciation should not succeed with a bad config type.');
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -79,8 +78,7 @@ describe('Logger is usable:', () => {
|
|||
assert.throws(
|
||||
() => new Logger('WereLogsTest'),
|
||||
TypeError,
|
||||
'Logger Instanciation should not succeed with only a name.',
|
||||
);
|
||||
'Logger Instanciation should not succeed with only a name.');
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -91,8 +89,7 @@ describe('Logger is usable:', () => {
|
|||
logger.newRequestLogger();
|
||||
},
|
||||
Error,
|
||||
'Werelogs should not throw when creating a request logger.',
|
||||
);
|
||||
'Werelogs should not throw when creating a request logger.');
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -104,14 +101,12 @@ describe('Logger is usable:', () => {
|
|||
},
|
||||
Error,
|
||||
// eslint-disable-next-line max-len
|
||||
'Werelogs should not throw when creating a request logger from a Serialized UID Array.',
|
||||
);
|
||||
'Werelogs should not throw when creating a request logger from a Serialized UID Array.');
|
||||
const reqLogger = logger.newRequestLoggerFromSerializedUids(
|
||||
'OneUID:SecondUID:TestUID:YouWinUID',
|
||||
);
|
||||
'OneUID:SecondUID:TestUID:YouWinUID');
|
||||
assert(reqLogger instanceof RequestLogger, 'RequestLogger');
|
||||
assert.deepStrictEqual(reqLogger.getUids().slice(0, -1),
|
||||
['OneUID', 'SecondUID', 'TestUID', 'YouWinUID']);
|
||||
['OneUID', 'SecondUID', 'TestUID', 'YouWinUID']);
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -134,7 +129,7 @@ describe('Logger is usable:', () => {
|
|||
describe('Does not crash and logs a fatal message when mis-using its logging API', () => {
|
||||
const testValues = [
|
||||
{ desc: 'a string as second argument', args: ['test', 'second-param-string'] },
|
||||
{ desc: 'a function as second argument', args: ['test', () => { }] }, // eslint-disable-line arrow-body-style
|
||||
{ desc: 'a function as second argument', args: ['test', () => { return; }] }, // eslint-disable-line arrow-body-style
|
||||
{ desc: 'a Number as second argument', args: ['test', 1] },
|
||||
{ desc: 'more than 2 arguments', args: ['test', 2, 3, 4] },
|
||||
];
|
||||
|
@ -148,7 +143,7 @@ describe('Logger is usable:', () => {
|
|||
for (let i = 0; i < testValues.length; ++i) {
|
||||
const test = testValues[i];
|
||||
it(`Does not crash with ${test.desc}`,
|
||||
loggingMisuseGenerator(test, createMisusableLogger));
|
||||
loggingMisuseGenerator(test, createMisusableLogger));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
|
||||
// eslint-disable-line strict
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const { DummyLogger, genericFilterGenerator, loggingMisuseGenerator } = require('../Utils.js');
|
||||
const Utils = require('../Utils.js');
|
||||
const DummyLogger = Utils.DummyLogger;
|
||||
const genericFilterGenerator = Utils.genericFilterGenerator;
|
||||
const loggingMisuseGenerator = Utils.loggingMisuseGenerator;
|
||||
|
||||
const RequestLogger = require('../../lib/RequestLogger.js');
|
||||
|
||||
|
@ -22,7 +24,7 @@ function filterGenerator(logLevel, callLevel) {
|
|||
|
||||
|
||||
function runLoggingDumpTest(commandHistory, expectedHistory, expectedCounts,
|
||||
done) {
|
||||
done) {
|
||||
const dummyLogger = new DummyLogger();
|
||||
const reqLogger = new RequestLogger(dummyLogger, 'trace', 'error', 'info');
|
||||
|
||||
|
@ -44,9 +46,9 @@ function runLoggingDumpTest(commandHistory, expectedHistory, expectedCounts,
|
|||
|
||||
expectedHistory.every((val, index) => {
|
||||
assert.strictEqual(dummyLogger.ops[index][0], val[0],
|
||||
'Expected log entry levels to match.');
|
||||
'Expected log entry levels to match.');
|
||||
assert.strictEqual(dummyLogger.ops[index][1][1], val[1],
|
||||
'Expected log entry values to match.');
|
||||
'Expected log entry values to match.');
|
||||
return true;
|
||||
});
|
||||
assert.deepEqual(dummyLogger.counts, expectedCounts);
|
||||
|
@ -60,8 +62,7 @@ describe('RequestLogger', () => {
|
|||
assert.throws(
|
||||
() => new RequestLogger(undefined, 'fatal', 'debug', 'info'),
|
||||
Error,
|
||||
'Dump level "debug" should not be valid with logging level "fatal".',
|
||||
);
|
||||
'Dump level "debug" should not be valid with logging level "fatal".');
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -69,8 +70,7 @@ describe('RequestLogger', () => {
|
|||
assert.doesNotThrow(
|
||||
() => new RequestLogger(undefined, 'debug', 'fatal', 'info'),
|
||||
Error,
|
||||
'Dump level "fatal" should be valid with logging level "debug".',
|
||||
);
|
||||
'Dump level "fatal" should be valid with logging level "debug".');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -98,8 +98,7 @@ describe('RequestLogger', () => {
|
|||
assert.throws(
|
||||
() => new RequestLogger(undefined, 'debug', 'fatal', 'info', 'pouet:tata'),
|
||||
Error,
|
||||
'UID string "pouet:tata" should be rejected by the RequestLogger constructor.',
|
||||
);
|
||||
'UID string "pouet:tata" should be rejected by the RequestLogger constructor.');
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -117,8 +116,7 @@ describe('RequestLogger', () => {
|
|||
assert.throws(
|
||||
() => new RequestLogger(undefined, 'debug', 'fatal', 'info', ['OneUID', 'SecondUID', 'Test:DashUID']),
|
||||
Error,
|
||||
'UID string "Test:DashUID" should be rejected by the RequestLogger constructor.',
|
||||
);
|
||||
'UID string "Test:DashUID" should be rejected by the RequestLogger constructor.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -188,7 +186,7 @@ describe('RequestLogger', () => {
|
|||
describe('Does not crash when mis-using its logging API', () => {
|
||||
const testValues = [
|
||||
{ desc: 'a string as second argument', args: ['test', 'second-param-string'] },
|
||||
{ desc: 'a function as second argument', args: ['test', function f() { }] },
|
||||
{ desc: 'a function as second argument', args: ['test', function f() { return; }] },
|
||||
{ desc: 'a Number as second argument', args: ['test', 1] },
|
||||
{ desc: 'more than 2 arguments', args: ['test', 2, 3, 4] },
|
||||
];
|
||||
|
@ -199,7 +197,7 @@ describe('RequestLogger', () => {
|
|||
for (let i = 0; i < testValues.length; ++i) {
|
||||
const test = testValues[i];
|
||||
it(`Does not crash with ${test.desc}`,
|
||||
loggingMisuseGenerator(test, createMisusableRequestLogger));
|
||||
loggingMisuseGenerator(test, createMisusableRequestLogger));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -250,35 +248,35 @@ describe('RequestLogger', () => {
|
|||
|
||||
describe('Logging API regression testing', () => {
|
||||
it('Should not alter the input fields when not actually logging',
|
||||
done => {
|
||||
const dummyLogger = new DummyLogger();
|
||||
const reqLogger = new RequestLogger(dummyLogger,
|
||||
'info', 'fatal', 'info');
|
||||
const refFields = { hits: 45, count: 32 };
|
||||
const usedFields = { ...refFields };
|
||||
reqLogger.debug('test', usedFields);
|
||||
assert.deepStrictEqual(usedFields, refFields);
|
||||
done();
|
||||
});
|
||||
done => {
|
||||
const dummyLogger = new DummyLogger();
|
||||
const reqLogger = new RequestLogger(dummyLogger,
|
||||
'info', 'fatal', 'info');
|
||||
const refFields = { hits: 45, count: 32 };
|
||||
const usedFields = Object.assign({}, refFields);
|
||||
reqLogger.debug('test', usedFields);
|
||||
assert.deepStrictEqual(usedFields, refFields);
|
||||
done();
|
||||
});
|
||||
|
||||
it('Should not alter the input fields when actually logging',
|
||||
done => {
|
||||
const dummyLogger = new DummyLogger();
|
||||
const reqLogger = new RequestLogger(dummyLogger,
|
||||
'info', 'fatal', 'info');
|
||||
const refFields = { hits: 45, count: 32 };
|
||||
const usedFields = { ...refFields };
|
||||
reqLogger.info('test', usedFields);
|
||||
assert.deepStrictEqual(usedFields, refFields);
|
||||
done();
|
||||
});
|
||||
done => {
|
||||
const dummyLogger = new DummyLogger();
|
||||
const reqLogger = new RequestLogger(dummyLogger,
|
||||
'info', 'fatal', 'info');
|
||||
const refFields = { hits: 45, count: 32 };
|
||||
const usedFields = Object.assign({}, refFields);
|
||||
reqLogger.info('test', usedFields);
|
||||
assert.deepStrictEqual(usedFields, refFields);
|
||||
done();
|
||||
});
|
||||
|
||||
it('Should not alter the input fields when dumping', done => {
|
||||
const dummyLogger = new DummyLogger();
|
||||
const reqLogger = new RequestLogger(dummyLogger,
|
||||
'info', 'fatal', 'info');
|
||||
'info', 'fatal', 'info');
|
||||
const refFields = { hits: 45, count: 32 };
|
||||
const usedFields = { ...refFields };
|
||||
const usedFields = Object.assign({}, refFields);
|
||||
reqLogger.error('test', usedFields);
|
||||
assert.deepStrictEqual(usedFields, refFields);
|
||||
done();
|
||||
|
@ -295,7 +293,7 @@ describe('RequestLogger', () => {
|
|||
};
|
||||
const dummyLogger = new DummyLogger();
|
||||
const reqLogger = new RequestLogger(dummyLogger,
|
||||
'info', 'fatal', 'info');
|
||||
'info', 'fatal', 'info');
|
||||
reqLogger.addDefaultFields(add1);
|
||||
reqLogger.addDefaultFields(add2);
|
||||
assert.deepStrictEqual(add1, { attr1: 0 });
|
||||
|
@ -309,70 +307,70 @@ describe('RequestLogger', () => {
|
|||
};
|
||||
const dummyLogger = new DummyLogger();
|
||||
const reqLogger = new RequestLogger(dummyLogger,
|
||||
'info', 'fatal', 'info');
|
||||
'info', 'fatal', 'info');
|
||||
reqLogger.addDefaultFields(clientInfo);
|
||||
reqLogger.info('test message');
|
||||
assert.strictEqual(clientInfo.clientIP,
|
||||
dummyLogger.ops[0][1][0].clientIP);
|
||||
dummyLogger.ops[0][1][0].clientIP);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should add multiple added default fields to the log entries',
|
||||
done => {
|
||||
const clientInfo = {
|
||||
clientIP: '127.0.0.1',
|
||||
clientPort: '1337',
|
||||
};
|
||||
const requestInfo = {
|
||||
object: '/tata/self.txt',
|
||||
creator: 'Joddy',
|
||||
};
|
||||
const dummyLogger = new DummyLogger();
|
||||
const reqLogger = new RequestLogger(dummyLogger,
|
||||
'info', 'fatal', 'info');
|
||||
reqLogger.addDefaultFields(clientInfo);
|
||||
reqLogger.addDefaultFields(requestInfo);
|
||||
reqLogger.info('test message');
|
||||
assert.strictEqual(clientInfo.clientIP,
|
||||
dummyLogger.ops[0][1][0].clientIP);
|
||||
assert.strictEqual(clientInfo.clientPort,
|
||||
dummyLogger.ops[0][1][0].clientPort);
|
||||
assert.strictEqual(requestInfo.object,
|
||||
dummyLogger.ops[0][1][0].object);
|
||||
assert.strictEqual(requestInfo.creator,
|
||||
dummyLogger.ops[0][1][0].creator);
|
||||
done();
|
||||
});
|
||||
done => {
|
||||
const clientInfo = {
|
||||
clientIP: '127.0.0.1',
|
||||
clientPort: '1337',
|
||||
};
|
||||
const requestInfo = {
|
||||
object: '/tata/self.txt',
|
||||
creator: 'Joddy',
|
||||
};
|
||||
const dummyLogger = new DummyLogger();
|
||||
const reqLogger = new RequestLogger(dummyLogger,
|
||||
'info', 'fatal', 'info');
|
||||
reqLogger.addDefaultFields(clientInfo);
|
||||
reqLogger.addDefaultFields(requestInfo);
|
||||
reqLogger.info('test message');
|
||||
assert.strictEqual(clientInfo.clientIP,
|
||||
dummyLogger.ops[0][1][0].clientIP);
|
||||
assert.strictEqual(clientInfo.clientPort,
|
||||
dummyLogger.ops[0][1][0].clientPort);
|
||||
assert.strictEqual(requestInfo.object,
|
||||
dummyLogger.ops[0][1][0].object);
|
||||
assert.strictEqual(requestInfo.creator,
|
||||
dummyLogger.ops[0][1][0].creator);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Automatic Elapsed Time computation', () => {
|
||||
describe('Deprecated API:', () => {
|
||||
it('should include an "elapsed_ms" field in the last log entry',
|
||||
done => {
|
||||
const dummyLogger = new DummyLogger();
|
||||
const reqLogger = new RequestLogger(dummyLogger,
|
||||
'info', 'fatal', 'info');
|
||||
reqLogger.end('Last message');
|
||||
assert.strictEqual(dummyLogger.ops[0][1][1], 'Last message');
|
||||
assert.notStrictEqual(dummyLogger.ops[0][1][0].elapsed_ms,
|
||||
undefined);
|
||||
assert.strictEqual(typeof dummyLogger.ops[0][1][0]
|
||||
.elapsed_ms, 'number');
|
||||
done();
|
||||
});
|
||||
done => {
|
||||
const dummyLogger = new DummyLogger();
|
||||
const reqLogger = new RequestLogger(dummyLogger,
|
||||
'info', 'fatal', 'info');
|
||||
reqLogger.end('Last message');
|
||||
assert.strictEqual(dummyLogger.ops[0][1][1], 'Last message');
|
||||
assert.notStrictEqual(dummyLogger.ops[0][1][0].elapsed_ms,
|
||||
undefined);
|
||||
assert.strictEqual(typeof dummyLogger.ops[0][1][0]
|
||||
.elapsed_ms, 'number');
|
||||
done();
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
it('should include an "elapsed_ms" field in the last log entry and be error level', () => {
|
||||
const dummyLogger = new DummyLogger();
|
||||
const reqLogger = new RequestLogger(dummyLogger,
|
||||
'info', 'fatal', 'info');
|
||||
'info', 'fatal', 'info');
|
||||
reqLogger.errorEnd('Last message failed');
|
||||
assert.strictEqual(dummyLogger.ops[0][1][1],
|
||||
'Last message failed');
|
||||
'Last message failed');
|
||||
assert.notStrictEqual(dummyLogger.ops[0][1][0].elapsed_ms,
|
||||
undefined);
|
||||
undefined);
|
||||
assert.strictEqual(typeof dummyLogger.ops[0][1][0].elapsed_ms,
|
||||
'number');
|
||||
'number');
|
||||
assert.strictEqual(dummyLogger.ops[0][0], 'error');
|
||||
});
|
||||
});
|
||||
|
@ -409,86 +407,53 @@ describe('RequestLogger', () => {
|
|||
reqLogger.end().error('Test Augmented END', { endValue: 42 });
|
||||
assert.strictEqual(dummyLogger.ops[0][1][1], 'Test Augmented END');
|
||||
assert.strictEqual(typeof dummyLogger.ops[0][1][0].elapsed_ms,
|
||||
'number');
|
||||
'number');
|
||||
assert.strictEqual(dummyLogger.ops[0][1][0].endFlag, true);
|
||||
assert.strictEqual(dummyLogger.ops[0][1][0].endValue, 42);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should log an error in addition to request logs when end() called more than once',
|
||||
done => {
|
||||
const dummyLogger = new DummyLogger();
|
||||
const reqLogger = new RequestLogger(dummyLogger, 'trace', 'fatal');
|
||||
reqLogger.end().info('after first call to end()');
|
||||
reqLogger.end().debug('after second call to end()');
|
||||
assert.strictEqual(dummyLogger.ops.length, 3);
|
||||
assert.strictEqual(dummyLogger.ops[0][0], 'info');
|
||||
assert.strictEqual(dummyLogger.ops[0][1][1], 'after first call to end()');
|
||||
assert.strictEqual(dummyLogger.ops[1][0], 'error');
|
||||
assert.strictEqual(dummyLogger.ops[2][0], 'debug');
|
||||
assert.strictEqual(dummyLogger.ops[2][1][1], 'after second call to end()');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Log History dumped when logging floor level reached', () => {
|
||||
it('Dumping duplicates log entries', done => {
|
||||
const commandHistory = ['info', 'error'];
|
||||
const expectedHistory = [['info', 0], ['info', 0], ['error', 1]];
|
||||
const expectedCounts = {
|
||||
trace: 0,
|
||||
debug: 0,
|
||||
info: 2,
|
||||
warn: 0,
|
||||
error: 1,
|
||||
fatal: 0,
|
||||
};
|
||||
const expectedCounts = { trace: 0, debug: 0, info: 2, warn: 0,
|
||||
error: 1, fatal: 0 };
|
||||
|
||||
runLoggingDumpTest(commandHistory, expectedHistory, expectedCounts,
|
||||
done);
|
||||
done);
|
||||
done();
|
||||
});
|
||||
|
||||
it('Dumping Keeps logging history order', done => {
|
||||
const commandHistory = ['trace', 'info', 'debug', 'error'];
|
||||
const expectedHistory = [['trace', 0], ['info', 1], ['debug', 2],
|
||||
['trace', 0], ['info', 1], ['debug', 2],
|
||||
['error', 3]];
|
||||
const expectedCounts = {
|
||||
trace: 2,
|
||||
debug: 2,
|
||||
info: 2,
|
||||
warn: 0,
|
||||
error: 1,
|
||||
fatal: 0,
|
||||
};
|
||||
['trace', 0], ['info', 1], ['debug', 2],
|
||||
['error', 3]];
|
||||
const expectedCounts = { trace: 2, debug: 2, info: 2, warn: 0,
|
||||
error: 1, fatal: 0 };
|
||||
|
||||
runLoggingDumpTest(commandHistory, expectedHistory, expectedCounts,
|
||||
done);
|
||||
done);
|
||||
done();
|
||||
});
|
||||
|
||||
it('Dumping multiple times does not re-dump already-dumped entries',
|
||||
done => {
|
||||
const commandHistory = ['trace', 'info', 'debug', 'error',
|
||||
'warn', 'debug', 'fatal'];
|
||||
const expectedHistory = [['trace', 0], ['info', 1], ['debug', 2],
|
||||
['trace', 0], ['info', 1], ['debug', 2],
|
||||
['error', 3], ['warn', 4], ['debug', 5],
|
||||
['warn', 4], ['debug', 5],
|
||||
['fatal', 6]];
|
||||
const expectedCounts = {
|
||||
trace: 2,
|
||||
debug: 4,
|
||||
info: 2,
|
||||
warn: 2,
|
||||
error: 1,
|
||||
fatal: 1,
|
||||
};
|
||||
done => {
|
||||
const commandHistory = ['trace', 'info', 'debug', 'error',
|
||||
'warn', 'debug', 'fatal'];
|
||||
const expectedHistory = [['trace', 0], ['info', 1], ['debug', 2],
|
||||
['trace', 0], ['info', 1], ['debug', 2],
|
||||
['error', 3], ['warn', 4], ['debug', 5],
|
||||
['warn', 4], ['debug', 5],
|
||||
['fatal', 6]];
|
||||
const expectedCounts = { trace: 2, debug: 4, info: 2, warn: 2,
|
||||
error: 1, fatal: 1 };
|
||||
|
||||
runLoggingDumpTest(commandHistory, expectedHistory,
|
||||
expectedCounts, done);
|
||||
done();
|
||||
});
|
||||
runLoggingDumpTest(commandHistory, expectedHistory,
|
||||
expectedCounts, done);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
|
||||
// eslint-disable-line strict
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const {
|
||||
generateUid, serializeUids, unserializeUids, objectCopy,
|
||||
} = require('../../lib/Utils');
|
||||
const Utils = require('../../lib/Utils.js');
|
||||
const generateUid = Utils.generateUid;
|
||||
const serializeUids = Utils.serializeUids;
|
||||
const unserializeUids = Utils.unserializeUids;
|
||||
const objectCopy = Utils.objectCopy;
|
||||
|
||||
describe('Utils: generateUid', () => {
|
||||
it('generates a string-typed ID', done => {
|
||||
const uid = generateUid();
|
||||
assert.strictEqual(typeof uid, 'string',
|
||||
`The generated ID is not a String (${typeof uid})`);
|
||||
`The generated ID is not a String (${typeof uid})`);
|
||||
done();
|
||||
});
|
||||
it('generate roughly unique IDs', done => {
|
||||
|
@ -24,8 +24,8 @@ describe('Utils: generateUid', () => {
|
|||
}
|
||||
Object.keys(generated).every(uid => {
|
||||
assert.strictEqual(generated[uid], 1,
|
||||
`Uid ${uid} was generated ${generated[uid]} `
|
||||
+ 'times: It is not even remotely unique.');
|
||||
`Uid ${uid} was generated ${generated[uid]} ` +
|
||||
'times: It is not even remotely unique.');
|
||||
return {};
|
||||
});
|
||||
done();
|
||||
|
@ -37,7 +37,7 @@ describe('Utils: serializeUids', () => {
|
|||
const uidList = ['FirstUID', 'SecondUID', 'ThirdUID'];
|
||||
const serializedUIDs = serializeUids(uidList);
|
||||
assert.strictEqual(serializedUIDs, 'FirstUID:SecondUID:ThirdUID',
|
||||
'Serialized UID List should match expected value.');
|
||||
'Serialized UID List should match expected value.');
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -54,12 +54,8 @@ describe('Utils: objectCopy', () => {
|
|||
it('copies all the properties from source to target object', done => {
|
||||
const target = { foo: 'bar' };
|
||||
const source = { id: 1, name: 'demo', value: { a: 1, b: 2, c: 3 } };
|
||||
const result = {
|
||||
foo: 'bar',
|
||||
id: 1,
|
||||
name: 'demo',
|
||||
value: { a: 1, b: 2, c: 3 },
|
||||
};
|
||||
const result = { foo: 'bar', id: 1, name: 'demo',
|
||||
value: { a: 1, b: 2, c: 3 } };
|
||||
objectCopy(target, source);
|
||||
assert.deepStrictEqual(target, result,
|
||||
'target should have the same properties as source');
|
||||
|
@ -67,32 +63,21 @@ describe('Utils: objectCopy', () => {
|
|||
});
|
||||
|
||||
it('copies all the properties from multiple sources to target object',
|
||||
done => {
|
||||
const target = { foo: 'bar' };
|
||||
const source1 = {
|
||||
id: 1,
|
||||
name: 'demo1',
|
||||
value: { a: 1, b: 2, c: 3 },
|
||||
};
|
||||
// eslint-disable-next-line camelcase
|
||||
const source2 = {
|
||||
req_id: 2,
|
||||
method: 'test',
|
||||
err: { code: 'error', msg: 'test' },
|
||||
};
|
||||
const result = {
|
||||
foo: 'bar',
|
||||
id: 1,
|
||||
name: 'demo1',
|
||||
value: { a: 1, b: 2, c: 3 },
|
||||
// eslint-disable-next-line camelcase
|
||||
req_id: 2,
|
||||
method: 'test',
|
||||
err: { code: 'error', msg: 'test' },
|
||||
};
|
||||
objectCopy(target, source1, source2);
|
||||
assert.deepStrictEqual(target, result,
|
||||
'target should have the same properties as source');
|
||||
done();
|
||||
});
|
||||
done => {
|
||||
const target = { foo: 'bar' };
|
||||
const source1 = { id: 1, name: 'demo1',
|
||||
value: { a: 1, b: 2, c: 3 } };
|
||||
// eslint-disable-next-line camelcase
|
||||
const source2 = { req_id: 2, method: 'test',
|
||||
err: { code: 'error', msg: 'test' } };
|
||||
const result = { foo: 'bar', id: 1, name: 'demo1',
|
||||
value: { a: 1, b: 2, c: 3 },
|
||||
// eslint-disable-next-line camelcase
|
||||
req_id: 2, method: 'test',
|
||||
err: { code: 'error', msg: 'test' } };
|
||||
objectCopy(target, source1, source2);
|
||||
assert.deepStrictEqual(target, result,
|
||||
'target should have the same properties as source');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
// Convert string args into primitive value
|
||||
const fromStr = (str, primitive) => (str === `${primitive}` ? primitive : str);
|
||||
const date = fromStr(process.argv[2], undefined);
|
||||
const exitCode = fromStr(fromStr(process.argv[3], null), undefined);
|
||||
|
||||
const { stderrUtils } = require('../../../../index');
|
||||
|
||||
stderrUtils.catchAndTimestampStderr(
|
||||
date ? () => date : undefined,
|
||||
exitCode,
|
||||
);
|
||||
|
||||
process.emitWarning('TestWarningMessage');
|
||||
// This will print warning after printing error before exit
|
||||
throw new Error('TestingError');
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
// Convert string args into primitive value
|
||||
const fromStr = (str, primitive) => (str === `${primitive}` ? primitive : str);
|
||||
const date = fromStr(process.argv[2], undefined);
|
||||
const exitCode = fromStr(fromStr(process.argv[3], null), undefined);
|
||||
const promise = fromStr(process.argv[4], true);
|
||||
|
||||
const { stderrUtils } = require('../../../../index');
|
||||
|
||||
stderrUtils.catchAndTimestampUncaughtException(
|
||||
date ? () => date : undefined,
|
||||
exitCode,
|
||||
);
|
||||
|
||||
// Executed if process does not exit, process is in undefined behavior (bad)
|
||||
// eslint-disable-next-line no-console
|
||||
setTimeout(() => console.log('EXECUTED AFTER UNCAUGHT EXCEPTION'), 1);
|
||||
|
||||
if (promise === true) {
|
||||
Promise.reject();
|
||||
} else {
|
||||
throw new Error('TestingError');
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
// Convert string args into primitive value
|
||||
const fromStr = (str, primitive) => (str === `${primitive}` ? primitive : str);
|
||||
const date = fromStr(process.argv[2], undefined);
|
||||
const name = fromStr(process.argv[3], undefined);
|
||||
const code = fromStr(process.argv[4], undefined);
|
||||
const detail = fromStr(process.argv[5], undefined);
|
||||
|
||||
const { stderrUtils } = require('../../../../index');
|
||||
|
||||
stderrUtils.catchAndTimestampWarning(
|
||||
date ? () => date : undefined,
|
||||
);
|
||||
|
||||
const warning = new Error('TestWarningMessage');
|
||||
|
||||
if (name) warning.name = name;
|
||||
if (code) warning.code = code;
|
||||
if (detail) warning.detail = detail;
|
||||
|
||||
process.emitWarning(warning);
|
||||
|
||||
/*
|
||||
Examples:
|
||||
|
||||
(node:203831) Error: TestWarningMessage
|
||||
at Object.<anonymous> (catchWarning.js:15:17)
|
||||
...
|
||||
at node:internal/main/run_main_module:22:47
|
||||
Above Warning Date: 2024-06-26T16:32:55.505Z
|
||||
|
||||
(node:205151) [TEST01] CUSTOM: TestWarningMessage
|
||||
at Object.<anonymous> (catchWarning.js:15:17)
|
||||
...
|
||||
at node:internal/main/run_main_module:22:47
|
||||
Some additional detail
|
||||
Above Warning Date: Tue, 31 Dec 2024 10:20:30 GMT
|
||||
*/
|
|
@ -1,309 +0,0 @@
|
|||
const assert = require('assert');
|
||||
const { execFile } = require('child_process');
|
||||
|
||||
const stderrUtils = require('../../lib/stderrUtils');
|
||||
|
||||
/** Simple regex for ISO YYYY-MM-DDThh:mm:ss.sssZ */
|
||||
// eslint-disable-next-line max-len
|
||||
const defaultDateRegex = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)/;
|
||||
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/** another format: Tue, 31 Dec 2024 10:20:30 GMT */
|
||||
const customDate = () => new Date('2024-12-31T10:20:30.444Z').toUTCString();
|
||||
|
||||
describe('stderrUtils', () => {
|
||||
const errStackRegex = /Error: TestingError\n(?:.*\sat\s.*\n)+/;
|
||||
|
||||
describe('defaultTimestamp', () => {
|
||||
it('should match ISO format', () => {
|
||||
assert.match(stderrUtils.defaultTimestamp(), defaultDateRegex);
|
||||
});
|
||||
});
|
||||
|
||||
describe('printErrorWithTimestamp', () => {
|
||||
let stderrText;
|
||||
const originalStderrWrite = process.stderr.write;
|
||||
const mockedStderrWrite = text => { stderrText = text; return true; };
|
||||
const err = new Error('TestingError');
|
||||
const origin = 'uncaughtException';
|
||||
|
||||
beforeEach(() => {
|
||||
stderrText = undefined;
|
||||
process.stderr.write = mockedStderrWrite;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.stderr.write = originalStderrWrite;
|
||||
stderrText = undefined;
|
||||
});
|
||||
|
||||
it(
|
||||
'should write to stderr with current date, origin and stacktrace',
|
||||
() => {
|
||||
const written = stderrUtils
|
||||
.printErrorWithTimestamp(err, origin);
|
||||
|
||||
assert.strictEqual(written, true);
|
||||
const [firstLine, errStack] = stderrText.split(':\n');
|
||||
const [errDate, errOrigin] = firstLine.split(': ');
|
||||
|
||||
assert.match(errDate, defaultDateRegex);
|
||||
assert.strictEqual(errOrigin, origin);
|
||||
assert.strictEqual(errStack, `${err.stack}\n`);
|
||||
},
|
||||
);
|
||||
|
||||
it(
|
||||
'should write to stderr with custom date, origin and stacktrace',
|
||||
() => {
|
||||
const written = stderrUtils
|
||||
.printErrorWithTimestamp(err, origin, customDate());
|
||||
|
||||
assert.strictEqual(written, true);
|
||||
const [firstLine, errStack] = stderrText.split(':\n');
|
||||
const [errDate, errOrigin] = firstLine.split(': ');
|
||||
|
||||
assert.strictEqual(errDate, customDate());
|
||||
assert.strictEqual(errOrigin, origin);
|
||||
assert.strictEqual(errStack, `${err.stack}\n`);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const execOptions = {
|
||||
cwd: __dirname,
|
||||
// Subprocess should always stop alone
|
||||
// But just in case, kill subprocess after 500ms.
|
||||
// Leave enough time for `nyc` that runs slower.
|
||||
timeout: 500,
|
||||
};
|
||||
|
||||
// Execute in another process to notice the process exit
|
||||
// Therefore, looks more like a functional test
|
||||
const timeoutHint = (ms, retries) =>
|
||||
`Test fixture process timed out after ${ms}ms with ${retries} retries.\n` +
|
||||
'Due to nyc coverage first run slowing down process.\nIncrease execOptions.timeout to fix';
|
||||
|
||||
describe('catchAndTimestampUncaughtException', () => {
|
||||
[
|
||||
{ desc: 'with default date' },
|
||||
{ desc: 'with custom date', date: customDate() },
|
||||
{ desc: 'with custom exitCode 42', exitCode: 42 },
|
||||
{ desc: 'without exit on uncaught exception', exitCode: null },
|
||||
{ desc: 'for unhandled promise', promise: true },
|
||||
].forEach(({
|
||||
desc, date, exitCode, promise,
|
||||
}) => describe(desc, () => {
|
||||
/** for before all hook that doesn't support this.retries */
|
||||
let retries = 4;
|
||||
let err;
|
||||
let stdout;
|
||||
let stderr;
|
||||
let errStack;
|
||||
let errDate;
|
||||
let errOrigin;
|
||||
|
||||
before('run process catchUncaughtException', function beforeAllHook(done) {
|
||||
execFile(
|
||||
'./fixtures/stderrUtils/catchUncaughtException.js',
|
||||
[`${date}`, `${exitCode}`, `${promise}`],
|
||||
execOptions,
|
||||
(subErr, subStdout, subStderr) => {
|
||||
if (subErr?.killed) {
|
||||
retries--;
|
||||
if (retries <= 0) {
|
||||
assert.fail(timeoutHint(execOptions.timeout, retries));
|
||||
}
|
||||
execOptions.timeout *= 2;
|
||||
return beforeAllHook(done);
|
||||
}
|
||||
err = subErr;
|
||||
stdout = subStdout;
|
||||
stderr = subStderr;
|
||||
let firstLine;
|
||||
[firstLine, errStack] = stderr.split(':\n');
|
||||
[errDate, errOrigin] = firstLine.split(': ');
|
||||
done();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
if (exitCode === null) {
|
||||
it('should not be an error (or timeout)',
|
||||
() => assert.ifError(err));
|
||||
it('should have stdout (printed after uncaught exception)',
|
||||
() => assert.match(stdout,
|
||||
/^.*EXECUTED AFTER UNCAUGHT EXCEPTION(?:.|\n)*$/));
|
||||
} else {
|
||||
it('should be an error',
|
||||
() => assert.ok(err));
|
||||
it(`should have exitCode ${exitCode || 1}`,
|
||||
() => assert.strictEqual(err.code, exitCode || 1));
|
||||
it('should have empty stdout',
|
||||
() => assert.strictEqual(stdout, ''));
|
||||
}
|
||||
|
||||
it('should have stderr',
|
||||
() => assert.ok(stderr));
|
||||
it('should have date in stderr first line',
|
||||
() => (date
|
||||
? assert.strictEqual(errDate, date)
|
||||
: assert.match(errDate, defaultDateRegex)));
|
||||
|
||||
it('should have origin in stderr first line',
|
||||
() => (promise === true
|
||||
? assert.strictEqual(errOrigin, 'unhandledRejection')
|
||||
: assert.strictEqual(errOrigin, 'uncaughtException')));
|
||||
|
||||
if (!promise) {
|
||||
it('should have stack trace on stderr',
|
||||
() => assert.match(errStack, errStackRegex));
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
describe('catchAndTimestampWarning (also tests node onWarning)', () => {
|
||||
[
|
||||
{ desc: 'with default date' },
|
||||
{ desc: 'with custom date', date: customDate() },
|
||||
{ desc: 'with deprecation warning', name: 'DeprecationWarning' },
|
||||
{
|
||||
desc: 'with custom warning',
|
||||
name: 'CUSTOM',
|
||||
code: 'TEST01',
|
||||
detail: 'Some additional detail',
|
||||
},
|
||||
].forEach(({
|
||||
desc, date, name, code, detail,
|
||||
}) => describe(desc, () => {
|
||||
/** for before all hook that doesn't support this.retries */
|
||||
let retries = 4;
|
||||
let err;
|
||||
let stdout;
|
||||
let stderr;
|
||||
|
||||
before('run process catchWarning', function beforeAllHook(done) {
|
||||
execFile(
|
||||
'./fixtures/stderrUtils/catchWarning.js',
|
||||
[`${date}`, `${name}`, `${code}`, `${detail}`],
|
||||
execOptions,
|
||||
(subErr, subStdout, subStderr) => {
|
||||
if (subErr?.killed) {
|
||||
retries--;
|
||||
if (retries <= 0) {
|
||||
assert.fail(timeoutHint(execOptions.timeout, retries));
|
||||
}
|
||||
execOptions.timeout *= 2;
|
||||
return beforeAllHook(done);
|
||||
}
|
||||
err = subErr;
|
||||
stdout = subStdout;
|
||||
stderr = subStderr;
|
||||
done();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should not be an error (or timeout)',
|
||||
() => assert.ifError(err));
|
||||
it('should have empty stdout',
|
||||
() => assert.strictEqual(stdout, ''));
|
||||
it('should have stderr',
|
||||
() => assert.ok(stderr));
|
||||
it('should have message on stderr first line, then stack trace',
|
||||
() => assert.match(stderr,
|
||||
/^.*TestWarningMessage\n(?:\s+at\s.*\n)+/));
|
||||
|
||||
if (code) {
|
||||
it('should have code on stderr first line',
|
||||
() => assert.match(stderr, new RegExp(`^.*[${code}]`)));
|
||||
}
|
||||
|
||||
if (name) {
|
||||
it('should have name on stderr first line',
|
||||
() => assert.match(stderr, new RegExp(`^.*${name}:`)));
|
||||
}
|
||||
|
||||
if (detail) {
|
||||
it('should have detail on stderr',
|
||||
() => assert.match(stderr, new RegExp(`.*${detail}.*`)));
|
||||
}
|
||||
|
||||
it(`should have ${date ? 'custom' : 'default'} date on stderr`,
|
||||
() => assert.match(stderr, new RegExp(
|
||||
`\nAbove Warning Date: ${
|
||||
date || defaultDateRegex.source}\n`,
|
||||
)));
|
||||
}));
|
||||
});
|
||||
|
||||
describe('catchAndTimestampStderr', () => {
|
||||
[
|
||||
{ desc: 'with default date' },
|
||||
{ desc: 'with custom date', date: customDate() },
|
||||
{ desc: 'with exit code', exitCode: 42 },
|
||||
|
||||
].forEach(({
|
||||
desc, date, exitCode,
|
||||
}) => describe(desc, () => {
|
||||
/** for before all hook that doesn't support this.retries */
|
||||
let retries = 4;
|
||||
let err;
|
||||
let stdout;
|
||||
let stderr;
|
||||
|
||||
before('run process catchStderr', function beforeAllHook(done) {
|
||||
execFile(
|
||||
'./fixtures/stderrUtils/catchStderr.js',
|
||||
[`${date}`, `${exitCode}`],
|
||||
execOptions,
|
||||
(subErr, subStdout, subStderr) => {
|
||||
if (subErr?.killed) {
|
||||
retries--;
|
||||
if (retries <= 0) {
|
||||
assert.fail(timeoutHint(execOptions.timeout, retries));
|
||||
}
|
||||
execOptions.timeout *= 2;
|
||||
return beforeAllHook(done);
|
||||
}
|
||||
err = subErr;
|
||||
stdout = subStdout;
|
||||
stderr = subStderr;
|
||||
done();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should be an error',
|
||||
() => assert.ok(err));
|
||||
it(`should have exitCode ${exitCode || 1}`,
|
||||
() => assert.strictEqual(err.code, exitCode || 1));
|
||||
it('should have empty stdout',
|
||||
() => assert.strictEqual(stdout, ''));
|
||||
|
||||
it('should have stderr',
|
||||
() => assert.ok(stderr));
|
||||
|
||||
// 2024-06-26T15:04:55.364Z: uncaughtException:
|
||||
// Error: TestingError
|
||||
// at Object.<anonymous> (catchStderr.js:16:7)
|
||||
// at node:internal/main/run_main_module:22:47
|
||||
it('should have error date, origin and stacktrace in stderr',
|
||||
() => assert.match(stderr,
|
||||
new RegExp(`${date || defaultDateRegex.source
|
||||
}: uncaughtException:\n${errStackRegex.source}`)));
|
||||
|
||||
// (node:171245) Warning: TestWarningMessage
|
||||
// at Object.<anonymous> (catchStderr.js:14:9)
|
||||
// at node:internal/main/run_main_module:22:47
|
||||
// Above Warning Date: 2024-06-26T15:04:55.365Z
|
||||
it('should have warning with stacktrace in stderr', () => {
|
||||
const trace = 'Warning: TestWarningMessage\n(?:\\s+at\\s.*\n)+';
|
||||
const detail = `(?:.|\n)*?(?<=\n)Above Warning Date: ${
|
||||
date || defaultDateRegex.source}\n`;
|
||||
assert.match(stderr,
|
||||
new RegExp(`${trace}${detail}`));
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue