Skip to content

Instantly share code, notes, and snippets.

@leoroos
Created August 23, 2012 21:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leoroos/3441763 to your computer and use it in GitHub Desktop.
Save leoroos/3441763 to your computer and use it in GitHub Desktop.
Addition of file location and line number output for a log statement of log4javascript. The addition is made in the Patternlayout.format function.
PatternLayout.prototype.format = function(loggingEvent) {
var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([acdflmMnpr%])(\{([^\}]+)\})?|([^%]+)/;
var formattedString = "";
var result;
var searchString = this.pattern;
// Cannot use regex global flag since it doesn't work with exec in IE5
while ((result = regex.exec(searchString))) {
var matchedString = result[0];
var padding = result[1];
var truncation = result[2];
var conversionCharacter = result[3];
var specifier = result[5];
var text = result[6];
// Check if the pattern matched was just normal text
if (text) {
formattedString += "" + text;
} else {
// Create a raw replacement string based on the conversion
// character and specifier
var replacement = "";
switch(conversionCharacter) {
//BEGIN -----------------------Additional Option to output the calling file name and line number of the logging call.
case "l": //Location
var isChrome = navigator.userAgent.indexOf("Chrome") !== -1;
if(isChrome){
//do someting else
var stack = new Error().stack;
var lineAccessingLogger = stack.split("\n")[8];
var funcBegin = lineAccessingLogger.indexOf("at ") + 3;
var resourceBegin = lineAccessingLogger.indexOf(" (") + 2;
var functionName = funcBegin < resourceBegin ? lineAccessingLogger.substring(funcBegin,resourceBegin-2) : null;
var resourceLoc;
if(functionName){
resourceLoc = lineAccessingLogger.substring(resourceBegin,lineAccessingLogger.length-1);
}else{
functionName = "(anonymous)";
resourceLoc = lineAccessingLogger.substring(funcBegin);
}
var colIdx = resourceLoc.lastIndexOf(":");
var column = parseInt(resourceLoc.substring(colIdx+1),10);
var lineIdx = resourceLoc.lastIndexOf(":",colIdx-1);
var line = parseInt(resourceLoc.substring(lineIdx+1,colIdx),10);
var resource = resourceLoc.substring(0,lineIdx);
var lastSegmentIdx = resource.lastIndexOf("/");
var lastSegment = resource.substring(lastSegmentIdx+1);
/*
var resultObject = {
r : resource,
l : line,
c : column,
f : functionName,
s : lastSegment
};
*/
var spec = "s:l";
if(specifier)spec = specifier;
var specresult = [];
var priorNum = "";
for ( var int = 0; int < spec.length; int++) {
var l = spec[int];
var num = parseInt(l,10);
if(num > -1 ){
priorNum += l;
continue;
}else{
if(priorNum.length >0){
specresult.push(parseInt(priorNum,10));
priorNum = "";
}
specresult.push(l);
}
}
if(priorNum.length >0)
specresult.push(parseInt(priorNum,10));
spec = specresult;
for ( var int = 0; int < spec.length; int++) {
var optNum = spec[int+1];
switch(spec[int]){
case "s":
replacement += lastSegment;
break;
case "r":
var string = resource;
if(typeof optNum === "number"){
string = string.substring(string.length-optNum);
spec.splice(int+1,1);
}
replacement += string;
break;
case "l":
replacement += line;
break;
case "c":
replacement += column;
break;
case "f":
var string = functionName;
if(typeof optNum === "number"){
string = string.substring(string.length-optNum);
spec.splice(int+1,1);
}
replacement += string;
break;
break;
default:
replacement += spec[int];
};
}
}else{
throw "can only use this method on google chrome";
}
break;
//END -----------------------Additional Option to output the calling file name and line number of the logging call.
case "a": // Array of messages
case "m": // Message
var depth = 0;
if (specifier) {
depth = parseInt(specifier, 10);
if (isNaN(depth)) {
handleError("PatternLayout.format: invalid specifier '" +
specifier + "' for conversion character '" + conversionCharacter +
"' - should be a number");
depth = 0;
}
}
var messages = (conversionCharacter === "a") ? loggingEvent.messages[0] : loggingEvent.messages;
for (var i = 0, len = messages.length; i < len; i++) {
if (i > 0 && (replacement.charAt(replacement.length - 1) !== " ")) {
replacement += " ";
}
if (depth === 0) {
replacement += messages[i];
} else {
replacement += formatObjectExpansion(messages[i], depth);
}
}
break;
case "c": // Logger name
var loggerName = loggingEvent.logger.name;
if (specifier) {
var precision = parseInt(specifier, 10);
var loggerNameBits = loggingEvent.logger.name.split(".");
if (precision >= loggerNameBits.length) {
replacement = loggerName;
} else {
replacement = loggerNameBits.slice(loggerNameBits.length - precision).join(".");
}
} else {
replacement = loggerName;
}
break;
case "d": // Date
var dateFormat = PatternLayout.ISO8601_DATEFORMAT;
if (specifier) {
dateFormat = specifier;
// Pick up special cases
if (dateFormat == "ISO8601") {
dateFormat = PatternLayout.ISO8601_DATEFORMAT;
} else if (dateFormat == "ABSOLUTE") {
dateFormat = PatternLayout.ABSOLUTETIME_DATEFORMAT;
} else if (dateFormat == "DATE") {
dateFormat = PatternLayout.DATETIME_DATEFORMAT;
}
}
// Format the date
replacement = (new SimpleDateFormat(dateFormat)).format(loggingEvent.timeStamp);
break;
case "f": // Custom field
if (this.hasCustomFields()) {
var fieldIndex = 0;
if (specifier) {
fieldIndex = parseInt(specifier, 10);
if (isNaN(fieldIndex)) {
handleError("PatternLayout.format: invalid specifier '" +
specifier + "' for conversion character 'f' - should be a number");
} else if (fieldIndex === 0) {
handleError("PatternLayout.format: invalid specifier '" +
specifier + "' for conversion character 'f' - must be greater than zero");
} else if (fieldIndex > this.customFields.length) {
handleError("PatternLayout.format: invalid specifier '" +
specifier + "' for conversion character 'f' - there aren't that many custom fields");
} else {
fieldIndex = fieldIndex - 1;
}
}
replacement = this.customFields[fieldIndex].value;
}
break;
case "n": // New line
replacement = newLine;
break;
case "p": // Level
replacement = loggingEvent.level.name;
break;
case "r": // Milliseconds since log4javascript startup
replacement = "" + loggingEvent.timeStamp.getDifference(applicationStartDate);
break;
case "%": // Literal % sign
replacement = "%";
break;
default:
replacement = matchedString;
break;
}
// Format the replacement according to any padding or
// truncation specified
var l;
// First, truncation
if (truncation) {
l = parseInt(truncation.substr(1), 10);
var strLen = replacement.length;
if (l < strLen) {
replacement = replacement.substring(strLen - l, strLen);
}
}
// Next, padding
if (padding) {
if (padding.charAt(0) == "-") {
l = parseInt(padding.substr(1), 10);
// Right pad with spaces
while (replacement.length < l) {
replacement += " ";
}
} else {
l = parseInt(padding, 10);
// Left pad with spaces
while (replacement.length < l) {
replacement = " " + replacement;
}
}
}
formattedString += replacement;
}
searchString = searchString.substr(result.index + result[0].length);
}
return formattedString;
};
var consoleAppender = new log4javascript.BrowserConsoleAppender();
var patternLayout = new log4javascript.PatternLayout("%d{HH:mm:ss,SSS} %l{s:l} %-5p - %m{1}%n");
consoleAppender.setLayout(patternLayout);
var rootLogger = log4javascript.getRootLogger();
rootLogger.addAppender(consoleAppender);
rootLogger.setLevel(log4javascript.Level.ALL);
// The additional pattern %l{s:l} will output the last segment of the calling resource and the line of the call:
22:46:18,501 callingfile.js:212 DEBUG - Response SUCCESS:
// The pattern "%d{HH:mm:ss} %l{r40:l:c} %-5p - %m{1}%n" will output the last 40 characters of the calling resource, the line and the column each divided by a colon.
23:14:00 8085/sapdebug/localDebugTabConnection.js:212:15 DEBUG - Response S
@asafMasa
Copy link

First very nice and elegant solution :)
But there is a bug.
when using both popup and browserConsole appenders the row "var lineAccessingLogger = stack.split("\n")[8];" causes a bug because the stack length is increased...
can you fix it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment