Comment puis-je écrire un wrapper de journal de console qui:
Comme la connexion à Java Script est si incohérente, il doit y avoir une solution. La mettre en œuvre moi-même est un peu fastidieux, mais il ne semble pas y avoir de bonne bibliothèque.
J'ai actuellement trouvé cet enregistreur qui fournit toutes les fonctionnalités, mais il gâche les numéros de ligne. http://benalman.com/projects/javascript-debug-console-log/
Il existe mon propre log4javascript , qui a sa propre console de journalisation mais fournit également un wrapper autour de console.log
. Il répond à tous vos critères, à l'exception du maintien des numéros de ligne, ce qui est impossible si vous intégrez des appels à console.log()
etc. dans une autre fonction.
var log = log4javascript.getLogger("main");
var appender = new log4javascript.BrowserConsoleAppender();
log.addAppender(appender);
log.debug("Hello world");
Je recommanderais également log4javascript et expliquerais comment conserver les informations relatives au nom de fichier et à la ligne imprimés, du moins sous Chrome.
Je ne parle pas de la modification du nom de fichier et de la ligne imprimée par Chrome, mais vous pouvez obtenir les informations qui vous intéressent et les ajouter à l’instruction du journal . Ma solution a été très rapide, mais je pense qu’un peu plus de travail est nécessaire vous pouvez obtenir des instructions de journal bien formatées . Cela a probablement aussi un impact important sur les performances, mais comme vous ne laisserez pas vos journaux activés en production, cela ne devrait pas poser trop de problème.
Dans Chrome, vous pouvez créer un objet Error qui fournit une propriété de pile qui vous indique l'emplacement actuel de votre pile. Et quelque part dans la chaîne de pile, vous trouverez le fichier et le numéro de ligne de votre script d'appel.
> 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)"
Pour un appel log4javascript, la trace de la pile pourrait ressembler à ceci:
"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)"
Et le fichier et la ligne qui a fait l'appel log4javascript et qui m'intéressent est
at meinAjaxAufruf (http://localhost:8080/numberajax.js:36:16)
Je suppose que la profondeur de la pile depuis le script qui vous intéresse jusqu’à l’appel console
est toujours la même. Alors maintenant, vous devez simplement savoir où la BrowserConsoleAppender
crée son accès window.console
et ajouter la ligne qui vous intéresse à la chaîne mise en forme. J'ai apporté les modifications suivantes à log4javascript_uncompressed.js
(version 1.4.2, ligne 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)) {
...
Maintenant au lieu de
17:53:22,872 DEBUG - sending /NumberServlet?zahl=1&text=
log4javascript.js:154
Je reçois
17:55:53,008 DEBUG - sending /NumberServlet?zahl=1&text=
at meinAjaxAufruf (http://localhost:8080/numberajax.js:36:16) log4javascript_uncompressed.js:1930
Ce n'est certainement pas une solution intéressante :), mais j'obtiens ce dont j'ai besoin.
Avec un peu plus de connaissances sur le framework, je suppose que l’on pourrait changer le PatternLayout de manière à ce que vous puissiez définir comment imprimer le nom/l’emplacement du fichier et le numéro de ligne.
edit Au lieu de ma solution précédente, j'ai apporté quelques modifications à la fonction PatternLayout.prototype.format. Je peux donc maintenant utiliser l'option supplémentaire% l pour définir où et comment je veux générer le fichier appelant et sa ligne. J'ai publié mes modifications et un exemple d'utilisation en tant que Gist .
Nous avons eu ce problème avec notre wrapper de journal également et il s'avère qu'il existe une solution de contournement fantastique et simple utilisant une application de fonction partielle:
if(DEBUG_ENABLED && (typeof console != 'undefined')) {
this.debug = console.log.bind(console);
}
else {
this.debug = function(message) {};
}
Avec cela, votre navigateur détectera le numéro de ligne et le fichier corrects de la source que vous souhaitez enregistrer.
Crossposting from related question ( Un wrapper approprié pour console.log avec le numéro de ligne correct? ) mais avec une solution mise à jour pour traiter plusieurs méthodes.
J'ai aimé la réponse de @ fredrik , donc je l'ai mise en place avec une autre réponse qui scinde la pile de Webkit et l'a fusionnée avec @ console de @ PaulIrish . "Standardise" le filename:line
en "objet spécial" afin qu'il se démarque et ait le même aspect dans FF et Chrome.
Essais au violon: 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
Pour rester simple, j'ai le wrapper ci-dessous pour les méthodes de la console:
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
};
Aussi, pour de meilleurs journaux dans IE et les navigateurs plus anciens, veuillez lire: Journalisation détaillée de la console
J'ai répondu à cette question ici , mais en bref, voyez le codepen pour une mise en œuvre complète. Cependant, cela fait tout ce que vous voulez, navigateur croisé, pas d'erreur, numéros de ligne corrects, toutes les méthodes de console disponibles, contrôle global et local:
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
}
Et utilisez-le comme ceci:
isDebug = true //global debug state
debug = Debugger(isDebug, this)
debug.log('Hello Log!')
Google Chrome aura bientôt une fonctionnalité qui intéressera ce sujet.
Vous pouvez l'activer maintenant par:
Redémarrez et profitez :)
Références:
J'ai trouvé une solution (nécessite jquery) sur le Web, mais elle ne fonctionne pas dans la plupart des navigateurs . Je l'ai changée et elle fonctionne dans Firefox (Mac, Linux. Android), Chrome (Mac, Linux. Android) et Safari. et d'autres navigateurs Webkit Android.
Il suffit d’écrire le code suivant dans un fichier appelé, par exemple. debug.js et l'inclure après l'inclusion de 'jquery.js' dans la section <head> de votre page Web et cela fonctionnera après le chargement de la page (document.ready). Il me reste à découvrir pour permettre le débogage avant que tout soit chargé (par exemple, seul <<head> ... </ head>) . La page Web doit être appelée avec? D = 1 dans l'URL et lorsque j'utilise Safari? d = 1s, je ne peux pas faire la distinction entre Safari et un autre navigateur Webkit dans l'agent utilisateur et Safari a un comportement différent en ce qui concerne le traitement du numéro de ligne et du nom de fichier par rapport aux autres navigateurs Webkit.
La fonction p_r (expression) se connecte à la fenêtre de l'id #js_debug et à la console (si ouverte) avec le nom du fichier et le numéro de ligne.
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)