diff --git a/README.md b/README.md new file mode 100644 index 0000000..03905a6 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +This program and the accompanying materials are +made available under the terms of the Eclipse Public License v2.0 which accompanies +this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + +SPDX-License-Identifier: EPL-2.0 + +Copyright Contributors to the Zowe Project. +# zlux-shared +This repository contains Zowe App Framework components that are utilized both by the server and in the web browser, such as the [Logger](https://github.com/zowe/zlux/wiki/Logging) and HTML parsing code. + +**To request features or report bugs, please use the issues page at the [zlux repo](https://github.com/zowe/zlux/issues) with the client or server infrastructure tags** diff --git a/src/dts/logger.d.ts b/src/dts/logger.d.ts index c88738d..1269792 100644 --- a/src/dts/logger.d.ts +++ b/src/dts/logger.d.ts @@ -1,70 +1,92 @@ -/* - This program and the accompanying materials are - made available under the terms of the Eclipse Public License v2.0 which accompanies - this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html - - SPDX-License-Identifier: EPL-2.0 - - Copyright Contributors to the Zowe Project. + /* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. */ + +type MessageTable = any; export declare enum LogLevel { - SEVERE = 0, - WARNING = 1, + CRITICAL = 0, + WARN = 1, INFO = 2, - FINE = 3, + DEBUG = 3, FINER = 4, - FINEST = 5 + TRACE = 5 } export declare class ComponentLogger implements ZLUX.ComponentLogger { private parentLogger; private componentName; + private _messages: MessageTable; SEVERE: number; + CRITICAL: number; + WARN: number; WARNING: number; INFO: number; FINE: number; + DEBUG: number; FINER: number; FINEST: number; - constructor(parentLogger: Logger, componentName: string); + TRACE: number; + constructor(parentLogger: Logger, componentName: string, messages?: MessageTable); makeSublogger(componentNameSuffix: string): ComponentLogger; log(minimumLevel: number, ...loggableItems: any[]): void; severe(...loggableItems: any[]): void; + critical(...loggableItems: any[]): void; info(...loggableItems: any[]): void; warn(...loggableItems: any[]): void; debug(...loggableItems: any[]): void; + trace(...loggableItems: any[]): void; } export declare class Logger implements ZLUX.Logger { private destinations; private configuration; - private componentLoggers; private previousPatterns; private knownComponentNames; static SEVERE: number; + static CRITICAL: number; static WARNING: number; + static WARN: number; static INFO: number; + static DEBUG: number; static FINE: number; static FINER: number; static FINEST: number; - constructor(); + static TRACE: number; + private static processString; + private static username; + private static euid?; + private static os?; + private static offsetMs; + private static seperator; + private static useV8Tracing; + constructor(offsetMs?: number); + toggleV8Tracing(): boolean; + _setBrowserUsername(username: string): void; addDestination(destinationCallback: (componentName: string, minimumLevel: LogLevel, ...loggableItems: any[]) => void): void; private shouldLogInternal; + private static createPrependingStrings; private consoleLogInternal; - makeDefaultDestination(prependDate?: boolean, prependName?: boolean, prependLevel?: boolean): (x: string, y: LogLevel, z: string) => void; + makeDefaultDestination(prependDate?: boolean, prependName?: boolean, prependLevel?: boolean, prependProcess?: boolean, prependUser?: boolean): (x: string, y: LogLevel, z: string) => void; log(componentName: string, minimumLevel: LogLevel, ...loggableItems: any[]): void; setLogLevelForComponentPattern(componentNamePattern: string, level: LogLevel): void; setLogLevelForComponentName(componentName: string, level: LogLevel | number): void; getComponentLevel(componentName: string): LogLevel; + getConfig(): any; private noteComponentNameInternal; private replayPatternsOnLogger; - makeComponentLogger(componentName: string): ComponentLogger; + makeComponentLogger(componentName: string, messages?: MessageTable): ComponentLogger; } - -/* - This program and the accompanying materials are - made available under the terms of the Eclipse Public License v2.0 which accompanies - this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html - - SPDX-License-Identifier: EPL-2.0 - - Copyright Contributors to the Zowe Project. + /* + This program and the accompanying materials are + made available under the terms of the Eclipse Public License v2.0 which accompanies + this distribution, and is available at https://www.eclipse.org/legal/epl-v20.html + + SPDX-License-Identifier: EPL-2.0 + + Copyright Contributors to the Zowe Project. */ diff --git a/src/logging/logger.ts b/src/logging/logger.ts index 723b7bf..9d31351 100644 --- a/src/logging/logger.ts +++ b/src/logging/logger.ts @@ -17,60 +17,113 @@ // time/data/functionname/linenumber // maybe polyfill from https://www.stacktracejs.com/#!/docs/stacktrace-js +declare var process: {pid: number, + geteuid: any}; +process; /* get rid of TS error */ +var componentLoggers: Map; // Because each componentLogger is accessible through its parent logger at run-time, and componentLoggers +// are not truly private with 'private', we use a local variable here to store each of them privately +type MessageTable = any; + export enum LogLevel { - SEVERE, - WARNING, + CRITICAL, + WARN, INFO, - FINE, + DEBUG, FINER, - FINEST, + TRACE, } export class ComponentLogger implements ZLUX.ComponentLogger { private parentLogger:Logger; private componentName:string; + private _messages: MessageTable; public SEVERE: number; + public CRITICAL: number; + public WARN: number; public WARNING: number; public INFO: number; public FINE: number; + public DEBUG: number; public FINER: number; public FINEST: number; + public TRACE: number; - constructor(parentLogger:Logger,componentName:string){ + constructor(parentLogger:Logger, componentName:string, messages?: MessageTable){ this.parentLogger = parentLogger; this.componentName = componentName; - this.SEVERE = LogLevel.SEVERE; - this.WARNING = LogLevel.WARNING; + this.CRITICAL = LogLevel.CRITICAL; + this.SEVERE = LogLevel.CRITICAL; + this.WARNING = LogLevel.WARN; + this.WARN = LogLevel.WARN; this.INFO = LogLevel.INFO; - this.FINE = LogLevel.FINE; + this.FINE = LogLevel.DEBUG; + this.DEBUG = LogLevel.DEBUG; this.FINER = LogLevel.FINER; - this.FINEST = LogLevel.FINEST; + this.FINEST = LogLevel.TRACE; + this.TRACE = LogLevel.TRACE; + this._messages = messages; } makeSublogger(componentNameSuffix:string): ComponentLogger { - return new ComponentLogger(this.parentLogger,this.componentName+':'+componentNameSuffix); + return new ComponentLogger(this.parentLogger, this.componentName+':'+componentNameSuffix); } - log(minimumLevel:number, ...loggableItems:any[]):void { + log(minimumLevel:number, ...loggableItems:any[]):void { + let firstLoggableItem = loggableItems[0]; + if (this._messages && this._messages[firstLoggableItem]) + { loggableItems[0] = firstLoggableItem + " - " + this._messages[firstLoggableItem]; } + this.parentLogger.log(this.componentName, minimumLevel, ...loggableItems); } severe(...loggableItems:any[]):void { - this.parentLogger.log(this.componentName, LogLevel.SEVERE, ...loggableItems); + let firstLoggableItem = loggableItems[0]; + if (this._messages && this._messages[firstLoggableItem]) + { loggableItems[0] = firstLoggableItem + " - " + this._messages[firstLoggableItem]; } + + this.parentLogger.log(this.componentName, LogLevel.CRITICAL, ...loggableItems); + } + + critical(...loggableItems:any[]):void { + let firstLoggableItem = loggableItems[0]; + if (this._messages && this._messages[firstLoggableItem]) + { loggableItems[0] = firstLoggableItem + " - " + this._messages[firstLoggableItem]; } + + this.parentLogger.log(this.componentName, LogLevel.CRITICAL, ...loggableItems); } - + info(...loggableItems:any[]):void { + let firstLoggableItem = loggableItems[0]; + if (this._messages && this._messages[firstLoggableItem]) + { loggableItems[0] = firstLoggableItem + " - " + this._messages[firstLoggableItem]; } + this.parentLogger.log(this.componentName, Logger.INFO, ...loggableItems); } warn(...loggableItems:any[]):void { - this.parentLogger.log(this.componentName, Logger.WARNING, ...loggableItems); + let firstLoggableItem = loggableItems[0]; + if (this._messages && this._messages[firstLoggableItem]) + { loggableItems[0] = firstLoggableItem + " - " + this._messages[firstLoggableItem]; } + + this.parentLogger.log(this.componentName, Logger.WARN, ...loggableItems); } debug(...loggableItems:any[]):void { - this.parentLogger.log(this.componentName, Logger.FINE, ...loggableItems); + let firstLoggableItem = loggableItems[0]; + if (this._messages && this._messages[firstLoggableItem]) + { loggableItems[0] = firstLoggableItem + " - " + this._messages[firstLoggableItem]; } + + this.parentLogger.log(this.componentName, Logger.DEBUG, ...loggableItems); } + trace(...loggableItems:any[]):void { + let firstLoggableItem = loggableItems[0]; + if (this._messages && this._messages[firstLoggableItem]) + { loggableItems[0] = firstLoggableItem + " - " + this._messages[firstLoggableItem]; } + + this.parentLogger.log(this.componentName, Logger.TRACE, ...loggableItems); + } + } class RegExpLevel { @@ -84,20 +137,66 @@ class RegExpLevel { export class Logger implements ZLUX.Logger { private destinations: Array<(componentName:string, minimumLevel: LogLevel, ...loggableItems:any[])=>void>; private configuration: {[key:string]:LogLevel}; - private componentLoggers:Map = new Map(); private previousPatterns: RegExpLevel[]; private knownComponentNames:string[] = []; - public static SEVERE: number = LogLevel.SEVERE; - public static WARNING: number = LogLevel.WARNING; + public static SEVERE: number = LogLevel.CRITICAL; + public static CRITICAL: number = LogLevel.CRITICAL; + public static WARNING: number = LogLevel.WARN; + public static WARN: number = LogLevel.WARN; public static INFO: number = LogLevel.INFO; - public static FINE: number = LogLevel.FINE; + public static DEBUG: number = LogLevel.DEBUG; + public static FINE: number = LogLevel.DEBUG; public static FINER: number = LogLevel.FINER; - public static FINEST: number = LogLevel.FINEST; + public static FINEST: number = LogLevel.TRACE; + public static TRACE: number = LogLevel.TRACE; + private static processString: string; + private static username: string = 'N/A'; + private static euid?: number; + private static os?: any; + private static offsetMs: number = 0; + private static seperator: string = '/'; + private static useV8Tracing: boolean = false; - constructor(){ + constructor(offsetMs: number = 0){ + componentLoggers = new Map(); this.configuration = {}; + Logger.offsetMs = offsetMs; this.destinations = new Array<(componentName:string, minimumLevel: LogLevel, ...loggableItems:any[])=>void>(); - this.previousPatterns = new Array(); + this.previousPatterns = new Array(); + if (!Logger.processString) { + let runningInNode = new Function(`try { return this === global; } catch (error) { return false; }`); + if (runningInNode()) { + Logger.useV8Tracing = true; + Logger.processString = ` `; + Logger.os = require('os'); + if (Logger.os.platform() == 'win32') { + Logger.seperator = '\\'; + } + try { + Logger.username = Logger.os.userInfo().username; + } catch (e) { + //OK + let platform = Logger.os.platform(); + if (platform != 'win32' && platform != 'android') { + Logger.euid = process.geteuid(); + } + } + } else { + Logger.processString = ` `; + } + } + } + + toggleV8Tracing() { + Logger.useV8Tracing = !Logger.useV8Tracing; + return Logger.useV8Tracing; + } + + _setBrowserUsername(username:string) { + //browser check + if (!Logger.os && (username.length > 0)) { + Logger.username = username; + } } addDestination(destinationCallback:(componentName:string, minimumLevel: LogLevel, ...loggableItems:any[])=>void):void { @@ -112,38 +211,115 @@ export class Logger implements ZLUX.Logger { return configuredLevel >= level; }; + private static createPrependingStrings(prependLevel?: boolean, + prependProcess?: boolean, + prependUser?: boolean): string[] { + let formatting = ''; + if (prependProcess) { + formatting += Logger.processString; + } + if (prependUser) { + if (Logger.username) { + formatting += `${Logger.username} `; + } else { + formatting += `${Logger.euid} `; + } + } + if (prependLevel) { + return [ + `${formatting}${LogLevel[0]} `, + `${formatting}${LogLevel[1]} `, + `${formatting}${LogLevel[2]} `, + `${formatting}${LogLevel[3]} `, + `${formatting}${LogLevel[4]} `, + `${formatting}${LogLevel[5]} `, + ]; + } else { + return [ + `${formatting} `, + `${formatting} `, + `${formatting} `, + `${formatting} `, + `${formatting} `, + `${formatting} `, + ]; + } + } + private consoleLogInternal(componentName:string, - minimumLevel:LogLevel, - prependDate?:boolean, - prependName?:boolean, - prependLevel?:boolean, - ...loggableItems:any[]):void { - var formatting = '['; + minimumLevel:LogLevel, + prependingString:string, + prependDate?:boolean, + prependName?:boolean, + ...loggableItems:any[]):void { + var formatting = ''; if (prependDate) { var d = new Date(); - var msOffset = d.getTimezoneOffset()*60000; - d.setTime(d.getTime()-msOffset); + d.setTime(d.getTime()-Logger.offsetMs); var dateString = d.toISOString(); dateString = dateString.substring(0,dateString.length-1).replace('T',' '); - formatting += dateString+ ' '; + formatting += `${dateString} `; } - if (prependName) { - formatting += componentName+ ' '; + formatting+=prependingString; + //v8 tracing only intended for v8 browsers & nodejs. Not likely to work elsewhere, so defaults to off for web code. + //Inspired from https://stackoverflow.com/questions/16697791/nodejs-get-filename-of-caller-function + //API def: https://v8.dev/docs/stack-trace-api + if (prependName && Logger.useV8Tracing) { + let originalFunc = (Error as any).prepareStackTrace; + let callerFunction = ''; + let callerLine = ''; + try { + let err:any = new Error(); + + (Error as any).prepareStackTrace = function (err: Error, stack: any) { + err; + return stack; + }; + if (err.stack.shift){ + let thisFile = err.stack.shift().getFileName(); + while (err.stack.length) { + let stackEntry = err.stack.shift(); + callerFunction = stackEntry.getFileName(); + if (callerFunction && (callerFunction != thisFile)) { + callerFunction=callerFunction.substring(callerFunction.lastIndexOf(Logger.seperator)+1); + callerLine=stackEntry.getLineNumber(); + break; + } + } + } + } catch (e) { + console.warn(`Error on stack analysis, ${e}`); + } + (Error as any).prepareStackTrace = originalFunc; + formatting+=`(${componentName},${callerFunction}:${callerLine}) `; + } else if (prependName) { + formatting+=`(${componentName},:) `; } - if (prependLevel) { - formatting += LogLevel[minimumLevel]; + if (loggableItems && (typeof loggableItems[0] == 'string')) { + formatting += loggableItems[0]; + loggableItems.shift(); } - formatting += "] -"; - console.log(formatting, ...loggableItems); + if (minimumLevel === LogLevel.CRITICAL) { + console.error(formatting, ...loggableItems); + } else if (minimumLevel === LogLevel.WARN) { + console.warn(formatting, ...loggableItems); + } else { + console.log(formatting, ...loggableItems); + } + }; makeDefaultDestination(prependDate?:boolean, - prependName?:boolean, - prependLevel?:boolean): (x:string,y:LogLevel,z:string) => void { + prependName?:boolean, + prependLevel?:boolean, + prependProcess?:boolean, + prependUser?:boolean): (x:string,y:LogLevel,z:string) => void { let theLogger:Logger = this; return function(componentName:string, minimumLevel:LogLevel, ...loggableItems:any[]){ + let prependingStrings: string[] = Logger.createPrependingStrings(prependLevel, prependProcess, prependUser); if (theLogger.shouldLogInternal(componentName, minimumLevel)){ - theLogger.consoleLogInternal(componentName,minimumLevel,prependDate,prependName,prependLevel,...loggableItems); + theLogger.consoleLogInternal(componentName,minimumLevel,prependingStrings[minimumLevel],prependDate,prependName, + ...loggableItems); } }; }; @@ -168,7 +344,7 @@ export class Logger implements ZLUX.Logger { }; setLogLevelForComponentName(componentName:string, level:LogLevel|number):void{ - if (level >= LogLevel.SEVERE && level <= LogLevel.FINEST) { + if (level >= LogLevel.CRITICAL && level <= LogLevel.TRACE) { this.configuration[componentName] = level; } } @@ -177,6 +353,10 @@ export class Logger implements ZLUX.Logger { return this.configuration[componentName]; } + getConfig():any{ + return this.configuration; + } + private noteComponentNameInternal(componentName:string):void{ if (!this.knownComponentNames.find( (name) => name == componentName)){ this.knownComponentNames.push(componentName); @@ -194,19 +374,26 @@ export class Logger implements ZLUX.Logger { return false; } - makeComponentLogger(componentName:string):ComponentLogger{ - let componentLogger:ComponentLogger|undefined = this.componentLoggers.get(componentName); + makeComponentLogger(componentName:string, _messages?: MessageTable):ComponentLogger{ + let componentLogger:ComponentLogger|undefined = componentLoggers.get(componentName); if (componentLogger){ - this.consoleLogInternal("",LogLevel.WARNING,true,false,true,'Logger created with identical component name to pre-existing logger. Messages overlap may occur.'); + this.consoleLogInternal("_internal",LogLevel.WARN, + `${Logger.processString}${Logger.username} ${LogLevel[1]}`, + true,true, + 'Logger created with identical component name to pre-existing logger. _messages overlap may occur.'); } else { - componentLogger = new ComponentLogger(this,componentName); + if (_messages) { + componentLogger = new ComponentLogger(this, componentName, _messages); + } else { + componentLogger = new ComponentLogger(this, componentName); + } this.configuration[componentName] = LogLevel.INFO; - this.componentLoggers.set(componentName,componentLogger as ComponentLogger); + componentLoggers.set(componentName, componentLogger as ComponentLogger); this.replayPatternsOnLogger(componentName); } return componentLogger; } - + } diff --git a/src/logging/package-lock.json b/src/logging/package-lock.json index 1ee5d16..9a16961 100644 --- a/src/logging/package-lock.json +++ b/src/logging/package-lock.json @@ -4,6 +4,15 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@angular/http": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/@angular/http/-/http-6.0.9.tgz", + "integrity": "sha512-JaYvBQQ+hJ7SKqZ+zw4C20lc7b6U5kK50nSkams10tzhITke6L/+wK8g3kiNu4XcqE5nqcIN8S95UkMGPMsa7Q==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "@types/core-js": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@types/core-js/-/core-js-2.5.0.tgz", diff --git a/src/logging/package.json b/src/logging/package.json index 5b3e2a0..dce8eab 100644 --- a/src/logging/package.json +++ b/src/logging/package.json @@ -12,6 +12,7 @@ "devDependencies": { "@types/core-js": "~2.5.0", "@types/node": "~8.10.23", + "@angular/http": "~6.0.9", "core-js": "~2.5.7", "rxjs": "~6.2.2", "rxjs-compat": "~6.2.2",