blob: b1f32625fec986c83c63e23a8a52d4e679ecb0cb [file] [log] [blame]
/*
* 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);
}
};