mirror of
https://github.com/appium/appium.git
synced 2026-04-29 06:50:56 -05:00
70449cd077
* 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
311 lines
7.9 KiB
TypeScript
311 lines
7.9 KiB
TypeScript
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;
|