knative-build/runtimes/javascript/utils/debug.js (175 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const FG_RED = "\x1b[31m"; const FG_GREEN = "\x1b[32m"; const FG_YELLOW = "\x1b[33m"; //const FG_BLUE = "\x1b[34m"; const FG_MAGENTA = "\x1b[35m"; const FG_CYAN = "\x1b[36m"; const FG_LTGRAY = "\x1b[37m"; //const FG_WHITE = "\x1b[97m"; const FG_SUCCESS = FG_GREEN; const FG_INFO = FG_LTGRAY; const FG_WARN = FG_YELLOW; const FG_ERROR = FG_RED; let config = { prefixFGColor: FG_CYAN, postfixFGColor: FG_MAGENTA, bodyFGColor: FG_INFO, defaultFGColor: FG_INFO, functionStartMarker: ">>> START: ", functionEndMarker: "<<< END: " }; /** * Formats Hours, Minutes, Seconds and Milliseconds as HH:MM:SS:mm */ function _getTimeFormatted(){ let date = new Date(); let ftime = date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + ":" + date.getMilliseconds(); return ftime; } /** * Formats and colorizes the prefix of the message consisting of the: * [moduleName] [functionName]() * * @param color optional (non-default) color for the prefix string */ function _formatMessagePrefix(color){ let prefixColor = config.prefixFGColor; // If color arg is defined, use it // TODO: validate color is an actual valid color string if( color !== undefined) { prefixColor = color; } let prefix = prefixColor + "[" + this.moduleInfo + "]["+this.functionName+"()] "; return prefix; } /** * Formats and colorizes the postfix of the message consisting of the: * [(formattedTime)] * * @param color optional (non-default) color for the prefix string */ function _formatMessagePostfix(color){ let postfixColor = config.postfixFGColor; // If color arg is defined, use it // TODO: validate color is an actual valid color string if( color !== undefined) { postfixColor = color; } let postfix = postfixColor + " (" + _getTimeFormatted() + ")"; return postfix; } /** * Formats and colorizes the body of the message. * * @param message that comprises the body of the output * @param color optional (non-default) color for the prefix string */ function _formatBody(message, color){ let bodyColor = config.bodyFGColor; // If color arg is defined, use it // TODO: validate color is an actual valid color string if(color !== undefined) bodyColor = color; return bodyColor + message + config.bodyFGColor; } /** * Formats the entirety of the message comprised of the message prefix + body + postfix. * * @param message that comprises the body of the formatted output * @param label (optional) labels (identifies) code block where message was generated; * typically used to identify anon. functions. * @param color overrides the default message color (this does not affect prefix or postfix) */ function _formatMessage(message, color ){ // Reset to default color at end of formatted message let fmsg = _formatMessagePrefix() + _formatBody(message, color) + _formatMessagePostfix() + config.defaultFGColor; return fmsg; } /** * Initialize the debug context including: * - Calling module * - Calling function (if anonymous, identify by signature) * * @param callerFunctionLabel (optional) label for the function (or block) to use * instead of pulling from the call stack. Typically used to identify anon. functions * or blocks */ function _updateContext(callerFunctionLabel){ try{ let obj = {}; Error.stackTraceLimit = 2; Error.captureStackTrace(obj, _updateContext); let fullStackInfo = obj.stack.split(")\n"); let rawFunctionInfo = fullStackInfo[1]; let entryInfo = rawFunctionInfo.split("at ")[1]; // TODO: if there is no '(' separator, we have no function name; do not split // e.g., value would look like: /openwhisk-knative-build/runtimes/javascript/src/service.js:180:19 if( entryInfo.indexOf(" (") !== -1) { let fm = entryInfo.split(" ("); this.functionName = fm[0]; this.fullModuleInfo = fm[1]; } else { // assume the entry has the full module name and path (function is a anonymous) this.functionName = "anonymous"; this.fullModuleInfo = entryInfo; } if( typeof this.fullModuleInfo !== "undefined") this.moduleInfo = this.fullModuleInfo.substring( this.fullModuleInfo.lastIndexOf("/") +1); // if explicit label provided, use it over one from stack trace... if(typeof(callerFunctionLabel) !== 'undefined') { if( this.functionName === "anonymous") this.functionName = callerFunctionLabel + "." + this.functionName; else this.functionName = callerFunctionLabel; } } catch(e){ console.error("Unable to parse stack trace: " + e.message); } } module.exports = class DEBUG { constructor() { // Empty constructor } /** * Used to mark the start of a function block (i.e., via console.info()) * * @param message (optional) message displayed with function start marker * @param functionName (optional) name of the function; typically used to better * identify anon. functions. */ functionStart(message, functionName) { _updateContext(functionName); let msg = ""; if(message !== undefined){ msg = message; } let formattedMessage = _formatMessage( config.functionStartMarker + msg ); console.info(formattedMessage); }; /** * Used to mark the end of a function block (i.e., console.info()) * * @param message (optional) message displayed with function end marker * @param functionName (optional) name of the function; typically used to better * identify anon. functions. */ functionEnd(message, functionName) { _updateContext(functionName); let msg = ""; if(message !== undefined){ msg = message; } let formattedMessage = _formatMessage( config.functionEndMarker + msg); console.info(formattedMessage); }; /** * Used to mark the end of a successful function block (i.e., console.info()) * * @param message (optional) message displayed with function end marker * @param functionName (optional) name of the function; typically used to better * identify anon. functions. */ functionEndSuccess(message, functionName) { _updateContext(functionName); let msg = ""; if(message !== undefined){ msg = message; } let formattedMessage = _formatMessage( config.functionEndMarker + msg, FG_SUCCESS ); console.info(formattedMessage); }; /** * Used to mark the end function block that has failed, but did not result in * an error to the user. In other words, a soft-failure that was recoverable. * * @param message (optional) message displayed with function end marker * @param functionName (optional) name of the function; typically used to better * identify anon. functions. */ functionEndFailure(message, functionName) { _updateContext(functionName); let msg = ""; if(message !== undefined){ msg = message; } let formattedMessage = _formatMessage( config.functionEndMarker + msg, FG_WARN ); console.info(formattedMessage); }; /** * Used to mark the end function block that errored * * @param message (optional) message displayed with function end marker * @param functionName (optional) name of the function; typically used to better * identify anon. functions. */ functionEndError(message, functionName) { _updateContext(functionName); let msg = ""; if(message !== undefined){ msg = message; } let formattedMessage = _formatMessage( config.functionEndMarker + msg, FG_ERROR ); console.info(formattedMessage); }; /** * Used to output informational message strings (i.e., console.info()) * * @param msg message to display to console as trace information * @param functionName (optional) name of the function; typically used to better * identify anon. functions. */ trace(message, functionName) { _updateContext(functionName); let formattedMessage = _formatMessage(message); console.info(formattedMessage); }; /** * Used to output the type and contents of Javascript types and Objects * This method attempts to display Objects as JSON strings where cyclical references * do not occur; otherwise, it attempts to display a 1st level (shallow) contents * of the Object. * * @param obj Javascript Object (or type) to dump its information to console.info() * @param label optional string label to display with object dump * @param functionName (optional) name of the function; typically used to better * identify anon. functions. */ dumpObject(obj, label, functionName){ _updateContext(functionName); let otype = typeof(obj); if( otype !== "undefined") { try{ let jsonFormatted = JSON.stringify(obj,null,4); let formattedMessage = _formatMessage("[" + label + " (" + otype + ")] = "+ jsonFormatted); console.info(formattedMessage); } catch (e) { // try manually dumping a shallow (string-friendly) copy of the Object try { console.log("{"); Object.keys(obj).forEach( function (key) { if(typeof obj[key] === 'string' && typeof(obj[key].toString()) !== "undefined"){ console.info(" \"" + key + "\": \"" + obj[key].toString() +"\"" ); } } ); console.log("}"); } catch(e2) { console.error("[" + label + " (" + otype + ")] : " + e2.message); let formattedMessage = _formatMessage(_ + "[" + label + " (" + otype + ")] : " + e2.message); console.error(formattedMessage); } } } else { let formattedMessage = _formatMessage( FG_YELLOW + "[" + label + " (" + otype + ")] is undefined." + FG_LTGRAY); console.info(formattedMessage); } } /** * Inject an error into code with additional information around it. * * @param label Javascript Object (or type) to dump its information to console.info() */ throw(label) { _updateContext(label); // intentionally throw an error and be clear to log this fact let formattedMessage = _formatMessage(FG_RED + "[" + label + "] Intentionally throwing error." + FG_LTGRAY); throw Error(formattedMessage); } };