console.log包装,保持行号,并支持大多数方法?
我如何编写一个控制台日志包装:
- 保持logging语句的logging行号和文件名不变
- 提供对所有日志严重性方法(错误,日志,debugging等)的访问权限,并将其显示在控制台中,并与日志logging一起显示
- 确实提供了一些回退(例如,当浏览器不支持错误时调用日志方法)
- 可以在中央位置closures,所以我可以closures伐木生产
- 处理不存在控制台的情况,并且不会抛出错误
由于loginJava脚本是如此不一致,必须有一些解决scheme。 自己实现它有点乏味,但似乎没有好的库。
我目前发现这个logging器提供了所有的function,但它确实弄乱了行号。 http://benalman.com/projects/javascript-debug-console-log/
有我自己的log4javascript ,它有自己的日志控制台,但也提供了一个包装console.log
。 它满足所有的标准,除了保持行号完整,如果你在另一个函数中打包调用console.log()
等是不可能实现的。
var log = log4javascript.getLogger("main"); var appender = new log4javascript.BrowserConsoleAppender(); log.addAppender(appender); log.debug("Hello world");
我还build议log4javascript,并解释如何仍然保持关于打印的文件名和行的信息,至less在Chrome中。
我不是在谈论改变由Chrome打印的文件名和行,但你可以得到你感兴趣的信息,并将其附加到日志声明。 我的解决scheme是一个快速的黑客攻击,但我认为,多一点工作,你可以得到格式良好的日志语句。 这可能也会对性能产生重大影响,但是由于您不会在生产环境中激活日志,因此这不会成为太大的问题。
这个概念
在Chrome中,您可以创build一个Error对象,该对象提供一个堆栈属性,显示您当前的堆栈位置以及堆栈string中的某个位置,您可以find调用脚本的文件和行号。
> new Error().stack "Error at eval at <anonymous> (eval at evaluate (unknown source)) at eval at evaluate (unknown source) at FrameMirror.evaluate (native) at Object.evaluate (unknown source) at Object._evaluateOn (unknown source) at Object._evaluateAndWrap (unknown source) at Object.evaluateOnCallFrame (unknown source) at meinAjaxAufruf (http://localhost:8080/numberajax.js:21:9) at HTMLInputElement.onkeyup (http://localhost:8080/numberajax.html:15:188)"
对于一个log4javascript调用,堆栈跟踪可能如下所示:
"Error at Object.append (http://localhost:8080/log4javascript_uncompressed.js:1921:17) at Object.doAppend (http://localhost:8080/log4javascript_uncompressed.js:1047:9) at Object.callAppenders (http://localhost:8080/log4javascript_uncompressed.js:647:27) at Object.log (http://localhost:8080/log4javascript_uncompressed.js:640:10) at Object.debug (http://localhost:8080/log4javascript_uncompressed.js:748:9) at meinAjaxAufruf (http://localhost:8080/numberajax.js:36:16) at HTMLInputElement.onkeyup (http://localhost:8080/numberajax.html:16:188)"
而使log4javascript调用的文件和行,我感兴趣的是
at meinAjaxAufruf (http://localhost:8080/numberajax.js:36:16)
解决scheme
我猜测,从你感兴趣的脚本到实际console
调用发生的地方,栈的深度总是相同的。 所以现在你只需要找出BrowserConsoleAppender
的window.console
访问权限,并将你感兴趣的行添加到格式化的string中。 我做了以下更改log4javascript_uncompressed.js
(版本1.4.2行1913年):
} else if (window.console && window.console.log) { // Safari and Firebug var formattedMesage = getFormattedMessage(); //---my additions var isChrome = navigator.userAgent.indexOf("Chrome") !== -1; if(isChrome){ var stack = new Error().stack; var lineAccessingLogger = stack.split("\n")[6]; formattedMesage += "\n" + lineAccessingLogger; } //--- // Log to Firebug using its logging methods or revert to the console.log // method in Safari if (window.console.debug && Level.DEBUG.isGreaterOrEqual(loggingEvent.level)) { window.console.debug(formattedMesage); } else if (window.console.info && Level.INFO.equals(loggingEvent.level)) { ...
现在,而不是
17:53:22,872 DEBUG - sending /NumberServlet?zahl=1&text= log4javascript.js:154
我明白了
17:55:53,008 DEBUG - sending /NumberServlet?zahl=1&text= at meinAjaxAufruf (http://localhost:8080/numberajax.js:36:16) log4javascript_uncompressed.js:1930
这当然不是一个很好的解决scheme:),但我得到了我所需要的。
有了更多的框架知识,我想可以改变PatternLayout的方式,你可以定义如何打印文件名/位置和行号。
编辑而不是我以前的解决scheme,我对PatternLayout.prototype.format函数做了一些修改,所以现在我可以使用额外的选项%l来定义我想要输出调用文件及其行的位置和方式。 我发表了我的变化和一个使用的例子作为一个Gist 。
我们也有这个问题与我们的日志包装,事实certificate,有一个奇妙的,简单的解决方法,使用部分function的应用程序:
if(DEBUG_ENABLED && (typeof console != 'undefined')) { this.debug = console.log.bind(console); } else { this.debug = function(message) {}; }
有了这个,您的浏览器将检测到您想要logging的来源的正确行号和文件。
从相关的问题交叉发布( 一个适当的包装console.log与正确的行号? ),但更新的解决scheme,以解决多个方法。
我喜欢@fredrik的回答 ,所以我把它分解成另一个分解Webkit 堆栈跟踪的答案 ,并将其与@ PaulIrish的安全console.log包装器合并。 “标准化”的filename:line
到一个“特殊的对象”,所以它脱颖而出,在FF和Chrome看起来大致相同。
在小提琴中testing: http : //jsfiddle.net/drzaus/pWe6W/9/
_log = (function (methods, undefined) { var Log = Error; // does this do anything? proper inheritance...? Log.prototype.write = function (args, method) { /// <summary> /// Paulirish-like console.log wrapper. Includes stack trace via @fredrik SO suggestion (see remarks for sources). /// </summary> /// <param name="args" type="Array">list of details to log, as provided by `arguments`</param> /// <param name="method" type="string">the console method to use: debug, log, warn, info, error</param> /// <remarks>Includes line numbers by calling Error object -- see /// * http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/ /// * https://stackoverflow.com/questions/13815640/a-proper-wrapper-for-console-log-with-correct-line-number /// * https://stackoverflow.com/a/3806596/1037948 /// </remarks> // via @fredrik SO trace suggestion; wrapping in special construct so it stands out var suffix = { "@": (this.lineNumber ? this.fileName + ':' + this.lineNumber + ":1" // add arbitrary column value for chrome linking : extractLineNumberFromStack(this.stack) ) }; args = args.concat([suffix]); // via @paulirish console wrapper if (console && console[method]) { if (console[method].apply) { console[method].apply(console, args); } else { console[method](args); } // nicer display in some browsers } }; var extractLineNumberFromStack = function (stack) { /// <summary> /// Get the line/filename detail from a Webkit stack trace. See https://stackoverflow.com/a/3806596/1037948 /// </summary> /// <param name="stack" type="String">the stack string</param> // correct line number according to how Log().write implemented var line = stack.split('\n')[3]; // fix for various display text line = (line.indexOf(' (') >= 0 ? line.split(' (')[1].substring(0, line.length - 1) : line.split('at ')[1] ); return line; }; // method builder var logMethod = function(method) { return function (params) { /// <summary> /// Paulirish-like console.log wrapper /// </summary> /// <param name="params" type="[...]">list your logging parameters</param> // only if explicitly true somewhere if (typeof DEBUGMODE === typeof undefined || !DEBUGMODE) return; // call handler extension which provides stack trace Log().write(Array.prototype.slice.call(arguments, 0), method); // turn into proper array & declare method to use };//-- fn logMethod }; var result = logMethod('log'); // base for backwards compatibility, simplicity // add some extra juice for(var i in methods) result[methods[i]] = logMethod(methods[i]); return result; // expose })(['error', 'debug', 'info', 'warn']);//--- _log
为了简单起见,我有下面的控制台方法的包装:
var noop = function () {}; window.consolex = { debug : window.console && window.console.debug && console.debug.bind(console) || noop, log : window.console && window.console.log && console.log.bind(console) || noop, warn: window.WARN = window.console && window.console.warn && console.warn.bind(console) || noop, error: window.ERROR = window.console && window.console.error && console.error.bind(console) || noop };
此外,为了更好地在IE和旧版本的浏览器中login,请阅读: 详细的控制台日志logging
谷歌浏览器即将有一个function,这将是感兴趣的线程。
您现在可以通过
- 启用chrome:// flags /#enable-devtools-experiments
- 点击开发工具中的cog
- 转到实验标签
- 检查“Javascript框架debugging”
- 转到常规选项卡
- 在来源部分
- 选中“跳过特定名称的源代码”
- 在模式input框中:input您现在看到的文件名(app.log.js)
重新开始并享受:)
参考文献:
从铬devtoolstesting
devtools问题线程
devtools代码审查
我在这里回答了这个问题,但总之看到完全实现的代码。 但是,这可以做任何你想要的,跨浏览器,没有错误,正确的行号,所有可用的控制台方法,全局和本地控制:
var Debugger = function(gState, klass) { this.debug = {} if (!window.console) return function(){} if (gState && klass.isDebug) { for (var m in console) if (typeof console[m] == 'function') this.debug[m] = console[m].bind(window.console, klass.toString()+": ") }else{ for (var m in console) if (typeof console[m] == 'function') this.debug[m] = function(){} } return this.debug }
像这样使用它:
isDebug = true //global debug state debug = Debugger(isDebug, this) debug.log('Hello Log!')
我在网上发现了一个解决scheme(需要jQuery),但是它在大多数浏览器中都不起作用。 我改变了它,它可以在Firefox(Mac,Linux,Android),Chrome(Mac,Linux,Android)以及Safari和其他Android webkit浏览器中使用。
只需将以下代码写入一个名为debug.js的文件中,并在包含“jquery.js”之后将其包含在网页的<head>部分中,并在加载页面 (document.ready) 后生效。 在加载所有东西之前 (例如只有<head> … </ head>),我仍然需要debugging。 在URL中使用?d = 1来调用网页,在使用Safari?d = 1s时,我无法区分Safari和用户代理中的另一个Webkit浏览器,Safari在行号和文件名中有不同的行为处理比其他Webkit浏览器。
函数p_r(expression)以文件名和行号login到id #js_debug窗口和控制台(如果打开)。
var g_d = null; function sortObj(theObj) { var sortable = []; for (var i in theObj) { sortable.push(i); } sortable.sort(); var copy = new Object; for (var i in sortable) { var ind = sortable[i]; copy[ind] = theObj[ind]; } return copy; } function p_r(s, comment, level) { if (!g_d) return; var res = s; var pre = new Array(""," " , " ", " ", " "); if (comment) comment += ' : '; if (arguments.length<2) comment=''; if (arguments.length<3) level = 0; // if (console) console.log(s); if (typeof(s) == 'object') { var copy = sortObj(s); comment += '\n'; res = '[object]\n'; if (level < 2) { for (var i in copy) { if (typeof(copy[i]) != "function") res += pre[level] + (i) + " : " + p_r(copy[i], '', level+1) + " : " + typeof(copy[i]) + "\n"; } res += pre[level] + "[/object]\n"; } } else if (typeof(s) == 'function') res = 'function'; else if (typeof(s) != 'string') res = '' + s; res = res.replace(/&/g, '&'); res = res.replace(/\x3C/g, '<'); res = res.replace(/>/g, '>'); if (level == 0) { window.LOG=res; console.log(window.LOG + comment + res); g_d.innerHTML += (window.LOG + comment + res + '\n'); } return res; } if (location.href.match(/d\=[1-9]/)) { $(document).ready(function() { $("body").prepend("<div id=\"js_debugclick\" onclick=\"$('#js_debug').toggle();\">JS DEBUG</div>\ <pre onclick=\"$('#js_debug').toggle();\" id='js_debug'></pre>\ "); $("head").append("<style type=\"text/css\">\ pre#js_debug {\ border: solid black 1px; background-color: #1CF; color: #000; display:none; position:absolute; top: 20px;\ font-family: Lucida Console, monospace; font-size: 9pt; height: 400px; overflow:scroll; width:100%;\ z-index:100;\ } \ #js_debugclick { \ color:red; font-weight:bold; \ } \ </style>\ "); g_d = document.getElementById('js_debug'); }); var __moredebug = location.href.match(/d\=[2-9]/); var __issafari = /safari/.test(navigator.userAgent.toLowerCase()) && location.href.match(/d\=[1-9]s/); var __iswebkit = /webkit/.test(navigator.userAgent.toLowerCase()); var __isopera = /opera/.test(navigator.userAgent.toLowerCase()); if (__moredebug) console.log(__issafari, __iswebkit); /*@const*/ //for closure-compiler //DEBUG=2 // 0=off, 1=msg:file:line:column, 2=msg:stack-trace /*@const @constructor*/ Object.defineProperty(window,'__stack__',{get:function(){ try{i.dont.exist()}catch(e){ if (__moredebug) var x=e.stack.split(":"); for (i in x){console.log(i,x[i]);} // console.log(e.stack.split(":")[13].match(/(\d+)/)[1]); return e.stack.split(":")} }}) /*@const @constructor*/ Object.defineProperty(window,'__file__',{get:function(){ var s=__stack__,l=s.length var f= __issafari ? s[9] : (__isopera ? s[12] : (__iswebkit ? s[14] : s[9])); return f.replace(/^.+?\/([^\/]+?)\?.+?$/, "$1"); }}) /*@const @constructor*/ Object.defineProperty(window,'__line__',{get:function(){ var s=__stack__,l=s.length return __issafari ? s[10].match(/(\d+)/)[1] :(__isopera ? s[13].match(/(\d+)/)[1] : (__iswebkit ? s[15] : s[10].replace(/\n/, " ").replace(/(\d+).+?$/, "$1"))); }}) /*@const @constructor*/ Object.defineProperty(window,'__col__',{get:function(){ var s=__stack__,l=s.length return (isNaN(s[l-2]))?"NA":s[l-1] }}) /*@const @constructor*/ Object.defineProperty(window,'LOG',{ get:function(){return out}, set:function(msg){if(0)out=msg+"\t-\t"+__stack__ else out=__file__+" "+__line__+": "; } }) }//end if(DEBUG)