mirror of
https://github.com/appium/appium.git
synced 2026-01-28 21:28:57 -06:00
feat(logger): add packages/logger package from npmlog (#20161)
* feat: move logger into appium/logger * add logger in tsconfig.json * add smoke with loading the index.js * modify files to fit other modules * refer to @appium/logger * update with npm run sync-pkgs and a few * cleanup changelog and package-lock.json * update smoke * pin deps * adjust tsconfig.json with others * Update README.md * update license * update license * Update LICENSE * fix review
This commit is contained in:
@@ -183,3 +183,5 @@ documentation on [How Does Appium Work?](https://appium.io/docs/en/latest/intro/
|
||||
[Apache-2.0](./LICENSE)
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fappium%2Fappium?ref=badge_large)
|
||||
|
||||
`@appium/logger` package is under [ISC](./packages/logger/LICENSE) License.
|
||||
|
||||
24
package-lock.json
generated
24
package-lock.json
generated
@@ -195,6 +195,10 @@
|
||||
"resolved": "packages/universal-xml-plugin",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@apppium/logger": {
|
||||
"resolved": "packages/logger",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.23.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
|
||||
@@ -22525,6 +22529,19 @@
|
||||
"@img/sharp-win32-x64": "0.33.0"
|
||||
}
|
||||
},
|
||||
"packages/logger": {
|
||||
"name": "@apppium/logger",
|
||||
"version": "1.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"console-control-strings": "1.1.0",
|
||||
"set-blocking": "2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
|
||||
"npm": ">=8"
|
||||
}
|
||||
},
|
||||
"packages/opencv": {
|
||||
"name": "@appium/opencv",
|
||||
"version": "3.0.4",
|
||||
@@ -23967,6 +23984,13 @@
|
||||
"xpath": "0.0.34"
|
||||
}
|
||||
},
|
||||
"@apppium/logger": {
|
||||
"version": "file:packages/logger",
|
||||
"requires": {
|
||||
"console-control-strings": "1.1.0",
|
||||
"set-blocking": "2.0.0"
|
||||
}
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.23.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
|
||||
|
||||
0
packages/logger/CHANGELOG.md
Normal file
0
packages/logger/CHANGELOG.md
Normal file
18
packages/logger/LICENSE
Normal file
18
packages/logger/LICENSE
Normal file
@@ -0,0 +1,18 @@
|
||||
ISC License
|
||||
|
||||
Copyright npm, Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this
|
||||
software for any purpose with or without fee is hereby
|
||||
granted, provided that the above copyright notice and this
|
||||
permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND NPM DISCLAIMS ALL
|
||||
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
|
||||
EVENT SHALL NPM BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
||||
USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
31
packages/logger/README.md
Normal file
31
packages/logger/README.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Appium Logger
|
||||
|
||||
The logger util that Appium uses.
|
||||
|
||||
# Installation
|
||||
|
||||
```console
|
||||
npm install @appium/logger --save
|
||||
```
|
||||
|
||||
# Basic Usage
|
||||
|
||||
```js
|
||||
import log from '@appium/logger';
|
||||
|
||||
// additional stuff ---------------------------+
|
||||
// message ----------+ |
|
||||
// prefix ----+ | |
|
||||
// level -+ | | |
|
||||
// v v v v
|
||||
log.info('fyi', 'I have a kitty cat: %j', myKittyCat);
|
||||
```
|
||||
|
||||
# History
|
||||
|
||||
This module is forked from [npmlog](https://github.com/npm/npmlog) under ISC License because the original project has been archived.
|
||||
Please check [the npmlog changelog](https://github.com/npm/npmlog/blob/main/CHANGELOG.md) to see the list of former module updates before it was forked.
|
||||
|
||||
# License
|
||||
|
||||
ISC License
|
||||
5
packages/logger/index.ts
Normal file
5
packages/logger/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import log from './lib/log';
|
||||
export type * from './lib/types';
|
||||
|
||||
export {log};
|
||||
export default log;
|
||||
310
packages/logger/lib/log.ts
Normal file
310
packages/logger/lib/log.ts
Normal file
@@ -0,0 +1,310 @@
|
||||
import {EventEmitter} from 'node:events';
|
||||
import setBlocking from 'set-blocking';
|
||||
import consoleControl from 'console-control-strings';
|
||||
import * as util from 'node:util';
|
||||
import type {MessageObject, StyleObject, Logger, LogLevel} from './types';
|
||||
import type {Writable} from 'node:stream';
|
||||
|
||||
const DEFAULT_LOG_LEVELS: any[][] = [
|
||||
['silly', -Infinity, {inverse: true}, 'sill'],
|
||||
['verbose', 1000, {fg: 'cyan', bg: 'black'}, 'verb'],
|
||||
['info', 2000, {fg: 'green'}],
|
||||
['timing', 2500, {fg: 'green', bg: 'black'}],
|
||||
['http', 3000, {fg: 'green', bg: 'black'}],
|
||||
['notice', 3500, {fg: 'cyan', bg: 'black'}],
|
||||
['warn', 4000, {fg: 'black', bg: 'yellow'}, 'WARN'],
|
||||
['error', 5000, {fg: 'red', bg: 'black'}, 'ERR!'],
|
||||
['silent', Infinity],
|
||||
] as const;
|
||||
|
||||
setBlocking(true);
|
||||
|
||||
export class Log extends EventEmitter implements Logger {
|
||||
level: LogLevel | string;
|
||||
record: MessageObject[];
|
||||
maxRecordSize: number;
|
||||
prefixStyle: StyleObject;
|
||||
headingStyle: StyleObject;
|
||||
heading: string;
|
||||
stream: Writable; // Defaults to process.stderr
|
||||
|
||||
_colorEnabled?: boolean;
|
||||
_buffer: MessageObject[];
|
||||
_style: Record<LogLevel | string, StyleObject | undefined>;
|
||||
_levels: Record<LogLevel | string, number>;
|
||||
_disp: Record<LogLevel | string, number | string>;
|
||||
_id: number;
|
||||
_paused: boolean;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.level = 'info';
|
||||
this._buffer = [];
|
||||
this.record = [];
|
||||
this.maxRecordSize = 10000;
|
||||
this.stream = process.stderr;
|
||||
this.prefixStyle = {fg: 'magenta'};
|
||||
this.headingStyle = {fg: 'white', bg: 'black'};
|
||||
this._id = 0;
|
||||
this._paused = false;
|
||||
|
||||
this._style = {};
|
||||
this._levels = {};
|
||||
this._disp = {};
|
||||
this.initDefaultLevels();
|
||||
|
||||
// allow 'error' prefix
|
||||
this.on('error', () => {});
|
||||
}
|
||||
|
||||
private useColor(): boolean {
|
||||
// by default, decide based on tty-ness.
|
||||
return (
|
||||
this._colorEnabled ?? Boolean(this.stream && 'isTTY' in this.stream && this.stream.isTTY)
|
||||
);
|
||||
}
|
||||
|
||||
enableColor(): void {
|
||||
this._colorEnabled = true;
|
||||
}
|
||||
|
||||
disableColor(): void {
|
||||
this._colorEnabled = false;
|
||||
}
|
||||
|
||||
// this functionality has been deliberately disabled
|
||||
enableUnicode(): void {}
|
||||
disableUnicode(): void {}
|
||||
enableProgress(): void {}
|
||||
disableProgress(): void {}
|
||||
progressEnabled(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily stop emitting, but don't drop
|
||||
*/
|
||||
pause(): void {
|
||||
this._paused = true;
|
||||
}
|
||||
|
||||
resume(): void {
|
||||
if (!this._paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._paused = false;
|
||||
|
||||
const b = this._buffer;
|
||||
this._buffer = [];
|
||||
for (const m of b) {
|
||||
this.emitLog(m);
|
||||
}
|
||||
}
|
||||
|
||||
silly(prefix: string, message: any, ...args: any[]): void {
|
||||
this.log('silly', prefix, message, ...args);
|
||||
}
|
||||
|
||||
verbose(prefix: string, message: any, ...args: any[]): void {
|
||||
this.log('verbose', prefix, message, ...args);
|
||||
}
|
||||
|
||||
info(prefix: string, message: any, ...args: any[]): void {
|
||||
this.log('info', prefix, message, ...args);
|
||||
}
|
||||
|
||||
timing(prefix: string, message: any, ...args: any[]): void {
|
||||
this.log('timing', prefix, message, ...args);
|
||||
}
|
||||
|
||||
http(prefix: string, message: any, ...args: any[]): void {
|
||||
this.log('http', prefix, message, ...args);
|
||||
}
|
||||
|
||||
notice(prefix: string, message: any, ...args: any[]): void {
|
||||
this.log('notice', prefix, message, ...args);
|
||||
}
|
||||
|
||||
warn(prefix: string, message: any, ...args: any[]): void {
|
||||
this.log('warn', prefix, message, ...args);
|
||||
}
|
||||
|
||||
error(prefix: string, message: any, ...args: any[]): void {
|
||||
this.log('error', prefix, message, ...args);
|
||||
}
|
||||
|
||||
silent(prefix: string, message: any, ...args: any[]): void {
|
||||
this.log('silent', prefix, message, ...args);
|
||||
}
|
||||
|
||||
addLevel(level: string, n: number, style?: StyleObject, disp?: string): void {
|
||||
this._levels[level] = n;
|
||||
this._style[level] = style;
|
||||
if (!this[level]) {
|
||||
this[level] = (prefix: string, message: any, ...args: any[]) => {
|
||||
this.log(level, prefix, message, ...args);
|
||||
};
|
||||
}
|
||||
// If 'disp' is null or undefined, use the level as a default
|
||||
this._disp[level] = disp ?? level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a log message
|
||||
* @param level
|
||||
* @param prefix
|
||||
* @param message message of the log which will be formatted using utils.format()
|
||||
* @param args additional arguments appended to the log message also formatted using utils.format()
|
||||
*/
|
||||
log(level: LogLevel | string, prefix: string, message: any, ...args: any[]): void {
|
||||
const l = this._levels[level];
|
||||
if (l === undefined) {
|
||||
this.emit('error', new Error(util.format('Undefined log level: %j', level)));
|
||||
return;
|
||||
}
|
||||
|
||||
const messageArguments: any[] = [];
|
||||
let stack: string | null = null;
|
||||
for (const formatArg of [message, ...args]) {
|
||||
messageArguments.push(formatArg);
|
||||
// resolve stack traces to a plain string.
|
||||
if (typeof formatArg === 'object' && formatArg instanceof Error && formatArg.stack) {
|
||||
Object.defineProperty(formatArg, 'stack', {
|
||||
value: (stack = formatArg.stack + ''),
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (stack) {
|
||||
messageArguments.unshift(`${stack}\n`);
|
||||
}
|
||||
const formattedMessage: string = util.format(...messageArguments);
|
||||
|
||||
const m: MessageObject = {
|
||||
id: this._id++,
|
||||
timestamp: Date.now(),
|
||||
level,
|
||||
prefix: String(prefix || ''),
|
||||
message: formattedMessage,
|
||||
};
|
||||
|
||||
this.emit('log', m);
|
||||
this.emit('log.' + level, m);
|
||||
if (m.prefix) {
|
||||
this.emit(m.prefix, m);
|
||||
}
|
||||
|
||||
this.record.push(m);
|
||||
const mrs = this.maxRecordSize;
|
||||
if (this.record.length > mrs) {
|
||||
this.record.shift();
|
||||
}
|
||||
|
||||
this.emitLog(m);
|
||||
}
|
||||
|
||||
private emitLog(m: MessageObject): void {
|
||||
if (this._paused) {
|
||||
this._buffer.push(m);
|
||||
return;
|
||||
}
|
||||
|
||||
const l = this._levels[m.level];
|
||||
if (l === undefined) {
|
||||
return;
|
||||
}
|
||||
if (l < this._levels[this.level]) {
|
||||
return;
|
||||
}
|
||||
if (l > 0 && !isFinite(l)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If 'disp' is null or undefined, use the lvl as a default
|
||||
// Allows: '', 0 as valid disp
|
||||
const disp = this._disp[m.level];
|
||||
this.clearProgress();
|
||||
for (const line of m.message.split(/\r?\n/)) {
|
||||
const heading = this.heading;
|
||||
if (heading) {
|
||||
this.write(heading, this.headingStyle);
|
||||
this.write(' ');
|
||||
}
|
||||
this.write(String(disp), this._style[m.level]);
|
||||
const p = m.prefix || '';
|
||||
if (p) {
|
||||
this.write(' ');
|
||||
}
|
||||
|
||||
this.write(p, this.prefixStyle);
|
||||
this.write(` ${line}\n`);
|
||||
}
|
||||
this.showProgress();
|
||||
}
|
||||
|
||||
private _format(msg: string, style: StyleObject = {}): string | undefined {
|
||||
if (!this.stream) {
|
||||
return;
|
||||
}
|
||||
|
||||
let output = '';
|
||||
if (this.useColor()) {
|
||||
const settings: string[] = [];
|
||||
if (style.fg) {
|
||||
settings.push(style.fg);
|
||||
}
|
||||
if (style.bg) {
|
||||
settings.push('bg' + style.bg[0].toUpperCase() + style.bg.slice(1));
|
||||
}
|
||||
if (style.bold) {
|
||||
settings.push('bold');
|
||||
}
|
||||
if (style.underline) {
|
||||
settings.push('underline');
|
||||
}
|
||||
if (style.inverse) {
|
||||
settings.push('inverse');
|
||||
}
|
||||
if (settings.length) {
|
||||
output += consoleControl.color(settings);
|
||||
}
|
||||
if (style.bell) {
|
||||
output += consoleControl.beep();
|
||||
}
|
||||
}
|
||||
output += msg;
|
||||
if (this.useColor()) {
|
||||
output += consoleControl.color('reset');
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
private write(msg: string, style: StyleObject = {}): void {
|
||||
if (!this.stream) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formatted = this._format(msg, style);
|
||||
if (formatted !== undefined) {
|
||||
this.stream.write(formatted);
|
||||
}
|
||||
}
|
||||
|
||||
private initDefaultLevels(): void {
|
||||
for (const [level, index, style, disp] of DEFAULT_LOG_LEVELS) {
|
||||
this._levels[level] = index;
|
||||
this._style[level] = style;
|
||||
this._disp[level] = disp ?? level;
|
||||
}
|
||||
}
|
||||
|
||||
// this functionality has been deliberately disabled
|
||||
private clearProgress(): void {}
|
||||
private showProgress(): void {}
|
||||
}
|
||||
|
||||
export const GLOBAL_LOG = new Log();
|
||||
export default GLOBAL_LOG;
|
||||
83
packages/logger/lib/types.ts
Normal file
83
packages/logger/lib/types.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import type {EventEmitter} from 'node:events';
|
||||
|
||||
export interface Logger extends EventEmitter {
|
||||
level: string;
|
||||
record: MessageObject[];
|
||||
maxRecordSize: number;
|
||||
prefixStyle: StyleObject;
|
||||
headingStyle: StyleObject;
|
||||
heading: string;
|
||||
stream: any; // Defaults to process.stderr
|
||||
|
||||
/**
|
||||
* Creates a log message
|
||||
* @param level
|
||||
* @param prefix
|
||||
* @param message message of the log which will be formatted using utils.format()
|
||||
* @param args additional arguments appended to the log message also formatted using utils.format()
|
||||
*/
|
||||
log(level: LogLevel | string, prefix: string, message: any, ...args: any[]): void;
|
||||
|
||||
/**
|
||||
* @param prefix
|
||||
* @param message message of the log which will be formatted using utils.format()
|
||||
* @param args additional arguments appended to the log message also formatted using utils.format()
|
||||
*/
|
||||
silly(prefix: string, message: any, ...args: any[]): void;
|
||||
verbose(prefix: string, message: any, ...args: any[]): void;
|
||||
info(prefix: string, message: any, ...args: any[]): void;
|
||||
timing(prefix: string, message: any, ...args: any[]): void;
|
||||
http(prefix: string, message: any, ...args: any[]): void;
|
||||
notice(prefix: string, message: any, ...args: any[]): void;
|
||||
warn(prefix: string, message: any, ...args: any[]): void;
|
||||
error(prefix: string, message: any, ...args: any[]): void;
|
||||
silent(prefix: string, message: any, ...args: any[]): void;
|
||||
|
||||
enableColor(): void;
|
||||
disableColor(): void;
|
||||
|
||||
enableProgress(): void;
|
||||
disableProgress(): void;
|
||||
progressEnabled(): boolean;
|
||||
|
||||
enableUnicode(): void;
|
||||
disableUnicode(): void;
|
||||
|
||||
pause(): void;
|
||||
resume(): void;
|
||||
|
||||
addLevel(level: string, n: number, style?: StyleObject, disp?: string): void;
|
||||
|
||||
// Allows for custom log levels
|
||||
// log.addLevel("custom", level)
|
||||
// log.custom(prefix, message)
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export type LogLevel =
|
||||
| 'silly'
|
||||
| 'verbose'
|
||||
| 'info'
|
||||
| 'timing'
|
||||
| 'http'
|
||||
| 'notice'
|
||||
| 'warn'
|
||||
| 'error'
|
||||
| 'silent';
|
||||
|
||||
export interface StyleObject {
|
||||
fg?: string | undefined;
|
||||
bg?: string | undefined;
|
||||
bold?: boolean | undefined;
|
||||
inverse?: boolean | undefined;
|
||||
underline?: boolean | undefined;
|
||||
bell?: boolean | undefined;
|
||||
}
|
||||
|
||||
export interface MessageObject {
|
||||
id: number;
|
||||
timestamp: number;
|
||||
level: string;
|
||||
prefix: string;
|
||||
message: string;
|
||||
}
|
||||
56
packages/logger/package.json
Normal file
56
packages/logger/package.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "@apppium/logger",
|
||||
"version": "1.0.0",
|
||||
"author": "https://github.com/appium",
|
||||
"description": "A Universal Logger For The Appium Ecosystem",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appium/appium.git",
|
||||
"directory": "packages/logger"
|
||||
},
|
||||
"main": "build/index.js",
|
||||
"types": "index.ts",
|
||||
"files": [
|
||||
"index.ts",
|
||||
"lib",
|
||||
"build",
|
||||
"tsconfig.json",
|
||||
"!build/tsconfig.tsbuildinfo",
|
||||
"CHANGELOG.md"
|
||||
],
|
||||
"directories": {
|
||||
"lib": "./lib"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "npm run test:unit",
|
||||
"test:smoke": "node ./build/index.js",
|
||||
"test:unit": "mocha --exit --timeout 1m \"./test/unit/**/*-specs.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"console-control-strings": "1.1.0",
|
||||
"set-blocking": "2.0.0"
|
||||
},
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
|
||||
"npm": ">=8"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/appium/appium/issues"
|
||||
},
|
||||
"homepage": "https://appium.io",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "8480a85ce2fa466360e0fb1a7f66628331907f02",
|
||||
"keywords": [
|
||||
"automation",
|
||||
"javascript",
|
||||
"selenium",
|
||||
"webdriver",
|
||||
"ios",
|
||||
"android",
|
||||
"firefoxos",
|
||||
"testing"
|
||||
]
|
||||
}
|
||||
425
packages/logger/test/unit/basic-specs.js
Normal file
425
packages/logger/test/unit/basic-specs.js
Normal file
@@ -0,0 +1,425 @@
|
||||
/* eslint-disable no-console */
|
||||
import { Log } from '../../lib/log';
|
||||
import {Stream} from 'node:stream';
|
||||
|
||||
describe('basic', async function () {
|
||||
const chai = await import('chai');
|
||||
chai.should();
|
||||
|
||||
let log;
|
||||
|
||||
describe('logging', function () {
|
||||
let s;
|
||||
let result = [];
|
||||
let logEvents = [];
|
||||
let logInfoEvents = [];
|
||||
let logPrefixEvents = [];
|
||||
const resultExpect = [
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[7msill\u001b[0m \u001b[0m\u001b[35msilly prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[36;40mverb\u001b[0m \u001b[0m\u001b[35mverbose prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[32minfo\u001b[0m \u001b[0m\u001b[35minfo prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[32;40mtiming\u001b[0m \u001b[0m\u001b[35mtiming prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[32;40mhttp\u001b[0m \u001b[0m\u001b[35mhttp prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[36;40mnotice\u001b[0m \u001b[0m\u001b[35mnotice prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[30;43mWARN\u001b[0m \u001b[0m\u001b[35mwarn prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[31;40mERR!\u001b[0m \u001b[0m\u001b[35merror prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[32minfo\u001b[0m \u001b[0m\u001b[35minfo prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[32;40mtiming\u001b[0m \u001b[0m\u001b[35mtiming prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[32;40mhttp\u001b[0m \u001b[0m\u001b[35mhttp prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[36;40mnotice\u001b[0m \u001b[0m\u001b[35mnotice prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[30;43mWARN\u001b[0m \u001b[0m\u001b[35mwarn prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[31;40mERR!\u001b[0m \u001b[0m\u001b[35merror prefix\u001b[0m x = {"foo":{"bar":"baz"}}\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[31;40mERR!\u001b[0m \u001b[0m\u001b[35m404\u001b[0m This is a longer\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[31;40mERR!\u001b[0m \u001b[0m\u001b[35m404\u001b[0m message, with some details\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[31;40mERR!\u001b[0m \u001b[0m\u001b[35m404\u001b[0m and maybe a stack.\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u001b[31;40mERR!\u001b[0m \u001b[0m\u001b[35m404\u001b[0m \n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u0007noise\u001b[0m\u001b[35m\u001b[0m LOUD NOISES\n',
|
||||
// eslint-disable-next-line max-len
|
||||
'\u001b[0m\u001b[37;40mnpm\u001b[0m \u001b[0m\u0007noise\u001b[0m \u001b[0m\u001b[35merror\u001b[0m erroring\n',
|
||||
'\u001b[0m',
|
||||
];
|
||||
const logPrefixEventsExpect = [
|
||||
{ id: 2,
|
||||
level: 'info',
|
||||
prefix: 'info prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 11,
|
||||
level: 'info',
|
||||
prefix: 'info prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 20,
|
||||
level: 'info',
|
||||
prefix: 'info prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
];
|
||||
// should be the same.
|
||||
const logInfoEventsExpect = logPrefixEventsExpect;
|
||||
const logEventsExpect = [
|
||||
{ id: 0,
|
||||
level: 'silly',
|
||||
prefix: 'silly prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 1,
|
||||
level: 'verbose',
|
||||
prefix: 'verbose prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 2,
|
||||
level: 'info',
|
||||
prefix: 'info prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 3,
|
||||
level: 'timing',
|
||||
prefix: 'timing prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 4,
|
||||
level: 'http',
|
||||
prefix: 'http prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 5,
|
||||
level: 'notice',
|
||||
prefix: 'notice prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 6,
|
||||
level: 'warn',
|
||||
prefix: 'warn prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 7,
|
||||
level: 'error',
|
||||
prefix: 'error prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 8,
|
||||
level: 'silent',
|
||||
prefix: 'silent prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 9,
|
||||
level: 'silly',
|
||||
prefix: 'silly prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 10,
|
||||
level: 'verbose',
|
||||
prefix: 'verbose prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 11,
|
||||
level: 'info',
|
||||
prefix: 'info prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 12,
|
||||
level: 'timing',
|
||||
prefix: 'timing prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 13,
|
||||
level: 'http',
|
||||
prefix: 'http prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 14,
|
||||
level: 'notice',
|
||||
prefix: 'notice prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 15,
|
||||
level: 'warn',
|
||||
prefix: 'warn prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 16,
|
||||
level: 'error',
|
||||
prefix: 'error prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 17,
|
||||
level: 'silent',
|
||||
prefix: 'silent prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 18,
|
||||
level: 'silly',
|
||||
prefix: 'silly prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 19,
|
||||
level: 'verbose',
|
||||
prefix: 'verbose prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 20,
|
||||
level: 'info',
|
||||
prefix: 'info prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 21,
|
||||
level: 'timing',
|
||||
prefix: 'timing prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 22,
|
||||
level: 'http',
|
||||
prefix: 'http prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 23,
|
||||
level: 'notice',
|
||||
prefix: 'notice prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 24,
|
||||
level: 'warn',
|
||||
prefix: 'warn prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 25,
|
||||
level: 'error',
|
||||
prefix: 'error prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 26,
|
||||
level: 'silent',
|
||||
prefix: 'silent prefix',
|
||||
message: 'x = {"foo":{"bar":"baz"}}',
|
||||
},
|
||||
{ id: 27,
|
||||
level: 'error',
|
||||
prefix: '404',
|
||||
message: 'This is a longer\nmessage, with some details\nand maybe a stack.\n',
|
||||
},
|
||||
{ id: 28,
|
||||
level: 'noise',
|
||||
prefix: '',
|
||||
message: 'LOUD NOISES',
|
||||
},
|
||||
{ id: 29,
|
||||
level: 'noise',
|
||||
prefix: 'error',
|
||||
message: 'erroring',
|
||||
},
|
||||
];
|
||||
|
||||
this.beforeEach(function () {
|
||||
result = [];
|
||||
logEvents = [];
|
||||
logInfoEvents = [];
|
||||
logPrefixEvents = [];
|
||||
|
||||
log = new Log();
|
||||
s = new Stream();
|
||||
s.write = (m) => result.push(m);
|
||||
s.writable = true;
|
||||
s.isTTY = true;
|
||||
s.end = () => {};
|
||||
log.stream = s;
|
||||
log.heading = 'npm';
|
||||
});
|
||||
|
||||
it('should work', function () {
|
||||
log.stream.should.equal(s);
|
||||
log.on('log', logEvents.push.bind(logEvents));
|
||||
log.on('log.info', logInfoEvents.push.bind(logInfoEvents));
|
||||
log.on('info prefix', logPrefixEvents.push.bind(logPrefixEvents));
|
||||
|
||||
console.error('log.level=silly');
|
||||
log.level = 'silly';
|
||||
log.silly('silly prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.verbose('verbose prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.info('info prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.timing('timing prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.http('http prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.notice('notice prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.warn('warn prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.error('error prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.silent('silent prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
|
||||
console.error('log.level=silent');
|
||||
log.level = 'silent';
|
||||
log.silly('silly prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.verbose('verbose prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.info('info prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.timing('timing prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.http('http prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.notice('notice prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.warn('warn prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.error('error prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.silent('silent prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
|
||||
console.error('log.level=info');
|
||||
log.level = 'info';
|
||||
log.silly('silly prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.verbose('verbose prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.info('info prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.timing('timing prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.http('http prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.notice('notice prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.warn('warn prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.error('error prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.silent('silent prefix', 'x = %j', { foo: { bar: 'baz' } });
|
||||
log.error('404', 'This is a longer\n' +
|
||||
'message, with some details\n' +
|
||||
'and maybe a stack.\n');
|
||||
log.addLevel('noise', 10000, { bell: true });
|
||||
log.noise(false, 'LOUD NOISES');
|
||||
log.noise('error', 'erroring');
|
||||
|
||||
result.join('').trim().should.equal(resultExpect.join('').trim());
|
||||
const withoutTimestamps = (x) => x.map((m) => {
|
||||
Boolean(m.timestamp).should.be.true;
|
||||
const copy = JSON.parse(JSON.stringify(m));
|
||||
delete copy.timestamp;
|
||||
return copy;
|
||||
});
|
||||
withoutTimestamps(log.record).should.eql(logEventsExpect);
|
||||
withoutTimestamps(logEvents).should.eql(logEventsExpect);
|
||||
withoutTimestamps(logInfoEvents).should.eql(logInfoEventsExpect);
|
||||
withoutTimestamps(logPrefixEvents).should.eql(logPrefixEventsExpect);
|
||||
});
|
||||
});
|
||||
|
||||
describe('utils', function () {
|
||||
it('enableColor', function () {
|
||||
log.enableColor();
|
||||
log.useColor().should.be.true;
|
||||
});
|
||||
|
||||
it('disableColor', function () {
|
||||
log.disableColor();
|
||||
log.useColor().should.be.false;
|
||||
});
|
||||
|
||||
it('_buffer while paused', function () {
|
||||
log.pause();
|
||||
log.log('verbose', 'test', 'test log');
|
||||
log._buffer.length.should.equal(1);
|
||||
log.resume();
|
||||
log._buffer.length.should.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('log.log', function () {
|
||||
beforeEach(function () {
|
||||
log = new Log();
|
||||
});
|
||||
|
||||
it('emits error on bad loglevel', async function() {
|
||||
await new Promise((resolve, reject) => {
|
||||
log.once('error', (err) => {
|
||||
/Undefined log level: "asdf"/.test(err).should.be.true;
|
||||
resolve();
|
||||
});
|
||||
log.log('asdf', 'bad loglevel');
|
||||
setTimeout(reject, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves stack traces to a plain string', async function() {
|
||||
await new Promise((resolve, reject) => {
|
||||
log.once('log', (m) => {
|
||||
/Error: with a stack trace/.test(m.message).should.be.true;
|
||||
/at Test/.test(m.message).should.be.true;
|
||||
resolve();
|
||||
});
|
||||
const err = new Error('with a stack trace');
|
||||
log.log('verbose', 'oops', err);
|
||||
setTimeout(reject, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
it('max record size', function() {
|
||||
log.maxRecordSize = 3;
|
||||
log.log('verbose', 'test', 'log 1');
|
||||
log.log('verbose', 'test', 'log 2');
|
||||
log.log('verbose', 'test', 'log 3');
|
||||
log.log('verbose', 'test', 'log 4');
|
||||
log.record.length.should.equal(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('stream', function () {
|
||||
beforeEach(function () {
|
||||
log = new Log();
|
||||
});
|
||||
|
||||
it('write with no stream', function() {
|
||||
log.stream = null;
|
||||
log.write('message');
|
||||
});
|
||||
});
|
||||
|
||||
describe('emitLog', function () {
|
||||
beforeEach(function () {
|
||||
log = new Log();
|
||||
});
|
||||
|
||||
it('to nonexistant level', function() {
|
||||
log.emitLog({ prefix: 'test', level: 'asdf' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('format', function () {
|
||||
beforeEach(function () {
|
||||
log = new Log();
|
||||
});
|
||||
|
||||
it('with nonexistant stream', function() {
|
||||
log.stream = null;
|
||||
(log._format('message') === undefined).should.be.true;
|
||||
});
|
||||
it('fg', function () {
|
||||
log.enableColor();
|
||||
const o = log._format('test message', { bg: 'blue' });
|
||||
o.includes('\u001b[44mtest message\u001b[0m').should.be.true;
|
||||
});
|
||||
it('bg', function () {
|
||||
log.enableColor();
|
||||
const o = log._format('test message', { bg: 'white' });
|
||||
o.includes('\u001b[47mtest message\u001b[0m').should.be.true;
|
||||
});
|
||||
it('bold', function () {
|
||||
log.enableColor();
|
||||
const o = log._format('test message', { bold: true });
|
||||
o.includes('\u001b[1mtest message\u001b[0m').should.be.true;
|
||||
});
|
||||
it('underline', function () {
|
||||
log.enableColor();
|
||||
const o = log._format('test message', { underline: true });
|
||||
o.includes('\u001b[4mtest message\u001b[0m').should.be.true;
|
||||
});
|
||||
it('inverse', function () {
|
||||
log.enableColor();
|
||||
const o = log._format('test message', { inverse: true });
|
||||
o.includes('\u001b[7mtest message\u001b[0m').should.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
34
packages/logger/test/unit/display-specs.js
Normal file
34
packages/logger/test/unit/display-specs.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Log } from '../../lib/log';
|
||||
import { waitForCondition } from 'asyncbox';
|
||||
|
||||
describe('display', function () {
|
||||
let log;
|
||||
|
||||
describe('explicitly set new log level display to empty string', function () {
|
||||
let actual;
|
||||
|
||||
beforeEach(function () {
|
||||
actual = '';
|
||||
log = new Log();
|
||||
log.write = (msg) => {
|
||||
actual += msg;
|
||||
};
|
||||
});
|
||||
|
||||
it('explicitly set new log level display to empty string', async function () {
|
||||
log.addLevel('explicitNoLevelDisplayed', 20000, {}, '');
|
||||
log.explicitNoLevelDisplayed('1', '2');
|
||||
await waitForCondition(() => actual.trim() === '1 2', {waitMs: 1000, intervalMs: 50});
|
||||
|
||||
actual = '';
|
||||
log.explicitNoLevelDisplayed('', '1');
|
||||
await waitForCondition(() => actual.trim() === '1', {waitMs: 1000, intervalMs: 50});
|
||||
});
|
||||
|
||||
it('explicitly set new log level display to 0', async function () {
|
||||
log.addLevel('explicitNoLevelDisplayed', 20000, {}, 0);
|
||||
log.explicitNoLevelDisplayed('', '1');
|
||||
await waitForCondition(() => actual.trim() === '0 1', {waitMs: 1000, intervalMs: 50});
|
||||
});
|
||||
});
|
||||
});
|
||||
14
packages/logger/tsconfig.json
Normal file
14
packages/logger/tsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "@appium/tsconfig/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true,
|
||||
"strict": false,
|
||||
"outDir": "build",
|
||||
"types": ["node"],
|
||||
"checkJs": true
|
||||
},
|
||||
"include": [
|
||||
"index.ts",
|
||||
"lib"
|
||||
]
|
||||
}
|
||||
@@ -67,6 +67,9 @@
|
||||
},
|
||||
{
|
||||
"path": "packages/strongbox"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "packages/logger"
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user