web-dev-qa-db-fra.com

console.log qui garde les numéros de ligne et supporte la plupart des méthodes?

Comment puis-je écrire un wrapper de journal de console qui:

  • Maintien intact du numéro de ligne enregistré et du nom de fichier de l'instruction de journal
  • Fournit un accès à toutes les méthodes de gravité des journaux (erreur, journal, débogage, ...) et les affiche dans la console lorsqu
  • fournit une solution de secours (par exemple, appelle la méthode log lorsque le navigateur ne prend pas en charge les erreurs)
  • peut être désactivé dans un emplacement central, afin que je puisse désactiver la journalisation pour la production
  • ne gère pas le cas qu'aucune console n'existe et ne génère pas d'erreurs

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/

47
Paul Weber

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");
15
Tim Down

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.

Le concept

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)

La solution

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 .

8
LeoR

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.

7
mpr

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
5
drzaus

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

2
manikanta

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!')
1
arctelix

Google Chrome aura bientôt une fonctionnalité qui intéressera ce sujet. 

Vous pouvez l'activer maintenant par: 

  1. Activer chrome: // flags/# enable-devtools-experiment
  2. Cliquez sur cog dans les outils de développement
  3. Aller à l'onglet Expériences
  4. Cochez "débogage des frameworks Javascript"
  5. Aller à l'onglet général
  6. Sous la section Sources
  7. Cochez "Passer en revue les sources avec des noms particuliers"
  8. Dans la zone de saisie du modèle: tapez le nom du fichier que vous voyez maintenant (app.log.js)

Redémarrez et profitez :)

Références: 

Tests de chrom devtools

devtools Issues thread

révision du code de devtools

1
Frison Alexander

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, '&amp;');
  res = res.replace(/\x3C/g, '&lt;');
  res = res.replace(/>/g, '&gt;');
  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)
0
web site maker