Ein richtiger Wrapper für console.log mit der richtigen Zeilennummer?

132

Ich entwickle jetzt eine Anwendung und platziere einen globalen isDebugSchalter. Ich möchte console.logfür eine bequemere Verwendung wickeln .

//isDebug controls the entire site.
var isDebug = true;

//debug.js
function debug(msg, level){
    var Global = this;
    if(!(Global.isDebug && Global.console && Global.console.log)){
        return;
    }
    level = level||'info';
    Global.console.log(level + ': '+ msg);
}

//main.js
debug('Here is a msg.');

Dann bekomme ich dieses Ergebnis in der Firefox-Konsole.

info: Here is a msg.                       debug.js (line 8)

Was ist, wenn ich mich mit der Zeilennummer anmelden möchte, unter debug()der angerufen wird info: Here is a msg. main.js (line 2)?

Rufus
quelle
Sie können console.logInformationen, console.warnWarnungen und console.errorFehler verwenden, anstatt etwas console.logüber eine Wrapper-Funktion hinzuzufügen .
Alvin Wong
2
@AlvinWong Ja, das weiß ich, aber das Problem ist, dass ich einen globalen Debug-Switch benötige, der steuert, ob consoleer verwendet werden muss. Um ein solches Ziel zu erreichen, scheint ein Wrapper der einzige Weg zu sein?
Rufus
Für Google Chrome siehe stackoverflow.com/a/25729203/1429301 In Ihrem Fall wäre das Muster debug.js
Frison Alexander

Antworten:

117

Dies ist eine alte Frage, und alle Antworten sind übermäßig hektisch, haben große browserübergreifende Probleme und bieten nichts besonders Nützliches. Diese Lösung funktioniert in jedem Browser und meldet alle Konsolendaten genau so, wie sie sollten. Keine Hacks erforderlich und eine Codezeile Überprüfen Sie den Codepen .

var debug = console.log.bind(window.console)

Erstellen Sie den Schalter wie folgt:

isDebug = true // toggle this to turn on / off for global controll

if (isDebug) var debug = console.log.bind(window.console)
else var debug = function(){}

Dann rufen Sie einfach wie folgt an:

debug('This is happening.')

Sie können sogar die console.log mit einem Schalter wie diesem übernehmen:

if (!isDebug) console.log = function(){}

Wenn Sie damit etwas Nützliches tun möchten, können Sie alle Konsolenmethoden hinzufügen und in eine wiederverwendbare Funktion einbinden, die nicht nur globale Kontrolle, sondern auch Klassenebene bietet:

var Debugger = function(gState, klass) {

  this.debug = {}

  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!')
debug.trace('Hello trace!')

Jetzt können Sie es Ihren Klassen hinzufügen:

var MyClass = function() {
  this.isDebug = true //local state
  this.debug = Debugger(isDebug, this)
  this.debug.warn('It works in classses')
}
arctelix
quelle
16
Korrigieren Sie mich, wenn ich falsch liege, aber Sie können dadurch keine zusätzlichen Funktionen hinzufügen, richtig? Sie aliasen im Wesentlichen nur das Konsolenobjekt? Ein grobes Beispiel - gibt es keine Möglichkeit, das Ereignis für jedes debug.log () zweimal zu console.log ()?
AB Carroll
3
@ABCarroll Sie können console.logzweimal eine benutzerdefinierte log()Funktion binden, die zwei Aufrufe enthält. Die console.logZeilennummern geben jedoch die Leitung wieder, in der sie sich console.logtatsächlich befinden, und nicht die Stelle, an der sie debug.logaufgerufen wird. Sie können jedoch beispielsweise dynamische Präfixe / Suffixe usw. hinzufügen. Es gibt auch Möglichkeiten, das Problem mit der Zeilennummer zu kompensieren, aber das ist eine andere Frage, die ich denke. Schauen Sie sich dieses Projekt für ein Beispiel an: github.com/arctelix/iDebugConsole/blob/master/README.md
arctelix
2
Diese Methode funktioniert in Firefox von Version 47 bis einschließlich 49 nicht. Und wurde nur in Version 50.0a2 behoben. Nun, FF50 wird in 2 Wochen veröffentlicht, aber ich verbringe mehrere Stunden damit, zu erkennen, warum es nicht funktioniert. Daher denke ich, dass diese Informationen für jemanden hilfreich sein können. Link
Vladimir Liubimov
Ich glaube, was @ABCarroll bedeutete, ist, dass alles in der Instanz nicht zur Laufzeit verwendet werden kann. In einem anderen Fall kann der globale Status nur in der Instanziierung definiert werden. Wenn Sie also später zu wechseln this.isDebug, falsespielt dies keine Rolle. Ich weiß nur nicht, ob es einen Weg gibt, vielleicht ist es beabsichtigt. In diesem Sinne ist das isDebugziemlich irreführend varund sollte conststattdessen ein sein.
Cregox
2
Dies beantwortet nicht die Frage "Was ist, wenn ich mich mit der Zeilennummer anmelden möchte, unter der debug () aufgerufen wird?"
Technomage
24

Ich mochte die Antwort von @ fredrik , also habe ich sie mit einer anderen Antwort zusammengefasst, die den Webkit-Stacktrace aufteilt , und sie mit dem sicheren console.log-Wrapper von @ PaulIrish zusammengeführt . "Standardisiert" das filename:linezu einem "speziellen Objekt", so dass es auffällt und in FF und Chrome größtenteils gleich aussieht.

Testen in Geige: http://jsfiddle.net/drzaus/pWe6W/

_log = (function (undefined) {
    var Log = Error; // does this do anything?  proper inheritance...?
    Log.prototype.write = function (args) {
        /// <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>
        /// <remarks>Includes line numbers by calling Error object -- see
        /// * http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
        /// * /programming/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.log) {
            if (console.log.apply) { console.log.apply(console, args); } else { console.log(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>

        if(!stack) return '?'; // fix undefined issue reported by @sigod

        // correct line number according to how Log().write implemented
        var line = stack.split('\n')[2];
        // fix for various display text
        line = (line.indexOf(' (') >= 0
            ? line.split(' (')[1].substring(0, line.length - 1)
            : line.split('at ')[1]
            );
        return line;
    };

    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)); // turn into proper array
    };//--  fn  returned

})();//--- _log

Dies funktioniert auch im Knoten und Sie können es testen mit:

// no debug mode
_log('this should not appear');

// turn it on
DEBUGMODE = true;

_log('you should', 'see this', {a:1, b:2, c:3});
console.log('--- regular log ---');
_log('you should', 'also see this', {a:4, b:8, c:16});

// turn it off
DEBUGMODE = false;

_log('disabled, should not appear');
console.log('--- regular log2 ---');
drzaus
quelle
ein etwas erweiterte Antwort auf Rechnung für die zusätzliche consoleMethoden wie warn, errorusw. - stackoverflow.com/a/14842659/1037948
drzaus
1
var line = stack.split('\n')[2];-'undefined' is not an object
Sigod
@sigod - hängt wahrscheinlich entweder vom Browser ab oder davon, dass ich dies vor 2 Jahren geschrieben habe und die Browser geändert haben. Was ist dein Szenario?
Drzaus
1
Einer meiner Kollegen hat Ihren Code in unser Projekt eingefügt. In IE11 und Safari 5 wurde die Site beschädigt. Bei anderen Versionen dieses Browsers sind Sie sich nicht sicher. Vielleicht fügen Sie einen Scheck für zukünftige Copy-Paster hinzu?
Sigod
1
@sigod was ist jetzt? hinzugefügt if(!stack) return '?'zu der Methode, die fehlschlägt, anstatt wo sie aufgerufen wird (wenn also jemand die Methode selbst verwendet, ist er auch "geschützt")
drzaus
18

Sie können Zeilennummern pflegen und die Protokollebene ausgeben, indem Sie Folgendes verwenden Function.prototype.bind:

function setDebug(isDebug) {
  if (window.isDebug) {
    window.debug = window.console.log.bind(window.console, '%s: %s');
  } else {
    window.debug = function() {};
  }
}

setDebug(true);

// ...

debug('level', 'This is my message.'); // --> level: This is my message. (line X)

Wenn Sie noch einen Schritt weiter gehen, können Sie das nutzen console Fehler- / Warn- / Info-Unterscheidungen des Benutzers verwenden und dennoch benutzerdefinierte Ebenen verwenden. Versuch es!

function setDebug(isDebug) {
  if (isDebug) {
    window.debug = {
      log: window.console.log.bind(window.console, '%s: %s'),
      error: window.console.error.bind(window.console, 'error: %s'),
      info: window.console.info.bind(window.console, 'info: %s'),
      warn: window.console.warn.bind(window.console, 'warn: %s')
    };
  } else {
    var __no_op = function() {};

    window.debug = {
      log: __no_op,
      error: __no_op,
      warn: __no_op,
      info: __no_op
    }
  }
}

setDebug(true);

// ...

debug.log('wat', 'Yay custom levels.'); // -> wat: Yay custom levels.    (line X)
debug.info('This is info.');            // -> info: This is info.        (line Y)
debug.error('Bad stuff happened.');     // -> error: Bad stuff happened. (line Z)
namuol
quelle
1
Ich habe jetzt schon eine Weile versucht, der Ausgabe automatisch ein Präfix console.debug(...)mit function nameund arguments- irgendwelche Gedanken darüber, wie das geht?
Daniel Sokolowski
3
Ich habe mir die Vielzahl der Konsolenverpackungen / Unterlegscheiben / etc. und dies ist das erste, auf das ich gestoßen bin, das das Beibehalten von Zeilennummern mit dem Anpassen der Ausgabe kombiniert. Es ist eine clevere Verwendung der Tatsache, dass .bind auch Currying für Sie erledigt. Sie können zusätzlich zum Kontext ein oder mehrere Argumente binden . Sie können noch einen Schritt weiter gehen und eine Noop-Funktion mit einer .toString-Methode übergeben, die beim Ausführen der Protokollmethode Code ausführen kann! Siehe diese jsfiddle
Sam Hasler
2
Vielleicht nicht in allen Browsern (nicht in es sah), aber der Austausch %smit %oin Chrome werden die Parameter in der Art und Weise drucken Sie erwarten würden (Objekte sind erweiterbar, Zahlen und Strings sind farbig, etc).
Anson
liebe diese Lösung. Ich habe einige Änderungen vorgenommen, die für meine Anwendung besser funktionieren, aber der Großteil davon ist noch intakt und läuft einwandfrei. Danke
Ward
9

Von: Wie erhalte ich die Funktionszeilennummer des JavaScript-Anrufers? Wie erhalte ich die Quell-URL des JavaScript-Anrufers? Das ErrorObjekt hat eine Zeilennummer-Eigenschaft (in FF). So etwas sollte funktionieren:

var err = new Error();
Global.console.log(level + ': '+ msg + 'file: ' + err.fileName + ' line:' + err.lineNumber);

Im Webkit-Browser err.stackist dies eine Zeichenfolge, die den aktuellen Aufrufstapel darstellt. Es wird die aktuelle Zeilennummer und weitere Informationen angezeigt.

AKTUALISIEREN

Um die richtige Leinenzahl zu erhalten, müssen Sie den Fehler in dieser Zeile aufrufen. Etwas wie:

var Log = Error;
Log.prototype.write = function () {
    var args = Array.prototype.slice.call(arguments, 0),
        suffix = this.lineNumber ? 'line: '  + this.lineNumber : 'stack: ' + this.stack;

    console.log.apply(console, args.concat([suffix]));
};

var a = Log().write('monkey' + 1, 'test: ' + 2);

var b = Log().write('hello' + 3, 'test: ' + 4);
fredrik
quelle
1
new Error();gibt mir den Kontext, in dem es ausgeführt wird, wenn ich es einfüge debug.js, dann werde ich bekommen info: Here is a msg. file: http://localhost/js/debug.js line:7.
Rufus
1
Was ist der Sinn von Log = Error? Sie ändern immer noch die Fehlerklasse, oder?
Drzaus
Kombinierte deine Antwort mit ein paar anderen - siehe unten stackoverflow.com/a/14841411/1037948
drzaus
8

Eine Möglichkeit, die Zeilennummer beizubehalten, finden Sie hier: https://gist.github.com/bgrins/5108712 . Es läuft mehr oder weniger darauf hinaus:

if (Function.prototype.bind) {
    window.log = Function.prototype.bind.call(console.log, console);
}
else {
    window.log = function() { 
        Function.prototype.apply.call(console.log, console, arguments);
    };
}

Sie können dies umbrechen isDebugund festlegen window.log, function() { }wenn Sie nicht debuggen.

Brian Grinstead
quelle
7

Sie können die Zeilennummer wie folgt an Ihre Debug-Methode übergeben:

//main.js
debug('Here is a msg.', (new Error).lineNumber);

Hier (new Error).lineNumberwürde Ihnen die aktuelle Zeilennummer in Ihrem javascriptCode geben.

Subodh
quelle
2
Ein bisschen ausführlich, nicht wahr?
Rufus
2
Ich denke, es reicht aus, um Ihre Frage zu beantworten. :)
Subodh
1
Die lineNumber-Eigenschaft ist nicht Standard und funktioniert derzeit nur unter Firefox. Siehe hier
Matthias,
6

Mit Chrome Devtools können Sie dies mit Blackboxing erreichen . Sie können den console.log-Wrapper erstellen, der Nebenwirkungen haben kann, andere Funktionen aufrufen usw. und dennoch die Zeilennummer beibehalten, die die wrapper-Funktion aufgerufen hat.

Fügen Sie einfach einen kleinen console.log-Wrapper in eine separate Datei ein, z

(function() {
    var consolelog = console.log
    console.log = function() {
        // you may do something with side effects here.
        // log to a remote server, whatever you want. here
        // for example we append the log message to the DOM
        var p = document.createElement('p')
        var args = Array.prototype.slice.apply(arguments)
        p.innerText = JSON.stringify(args)
        document.body.appendChild(p)

        // call the original console.log function
        consolelog.apply(console,arguments)
    }
})()

Nennen Sie es so etwas wie log-blackbox.js

Gehen Sie dann zu den Chrome Devtools-Einstellungen und suchen Sie den Abschnitt "Blackboxing". Fügen Sie ein Muster für den Dateinamen hinzu, den Sie blackboxen möchten, in diesem Fall log-blackbox.js

kzahel
quelle
Hinweis: Stellen Sie sicher, dass Sie keinen Code haben, den Sie im Stack-Trace in derselben Datei anzeigen möchten, da dieser ebenfalls aus dem Trace entfernt wird.
Jamesthollowell
6

Ich habe eine einfache Lösung gefunden, um die akzeptierte Antwort (Bindung an console.log / error / etc) mit einer externen Logik zu kombinieren, um zu filtern, was tatsächlich protokolliert wird.

// or window.log = {...}
var log = {
  ASSERT: 1, ERROR: 2, WARN: 3, INFO: 4, DEBUG: 5, VERBOSE: 6,
  set level(level) {
    if (level >= this.ASSERT) this.a = console.assert.bind(window.console);
    else this.a = function() {};
    if (level >= this.ERROR) this.e = console.error.bind(window.console);
    else this.e = function() {};
    if (level >= this.WARN) this.w = console.warn.bind(window.console);
    else this.w = function() {};
    if (level >= this.INFO) this.i = console.info.bind(window.console);
    else this.i = function() {};
    if (level >= this.DEBUG) this.d = console.debug.bind(window.console);
    else this.d = function() {};
    if (level >= this.VERBOSE) this.v = console.log.bind(window.console);
    else this.v = function() {};
    this.loggingLevel = level;
  },
  get level() { return this.loggingLevel; }
};
log.level = log.DEBUG;

Verwendung:

log.e('Error doing the thing!', e); // console.error
log.w('Bonus feature failed to load.'); // console.warn
log.i('Signed in.'); // console.info
log.d('Is this working as expected?'); // console.debug
log.v('Old debug messages, output dominating messages'); // console.log; ignored because `log.level` is set to `DEBUG`
log.a(someVar == 2) // console.assert
  • Beachten Sie, dass console.assertdie bedingte Protokollierung verwendet wird.
  • Stellen Sie sicher, dass die Entwicklertools Ihres Browsers alle Nachrichtenebenen anzeigen!
Jacob Phillips
quelle
Weil es keine Zeilennummer oder Arbeitsbeispiele gibt, die die Protokollstufe anzeigen.
not2qubit
Die Zeilennummer entspricht der direkten Verwendung der Konsole. Ich habe die Antwort mit Anwendungsbeispielen aktualisiert. Es hat nicht viele Stimmen, weil ich es zwei Jahre später beantwortet habe :)
Jacob Phillips
4

Wenn Sie einfach steuern möchten, ob Debug verwendet wird und die richtige Zeilennummer haben, können Sie dies stattdessen tun:

if(isDebug && window.console && console.log && console.warn && console.error){
    window.debug = {
        'log': window.console.log,
        'warn': window.console.warn,
        'error': window.console.error
    };
}else{
    window.debug = {
        'log': function(){},
        'warn': function(){},
        'error': function(){}
    };
}

Wenn Sie Zugriff auf das Debuggen benötigen, können Sie dies tun:

debug.log("log");
debug.warn("warn");
debug.error("error");

Wenn isDebug == true, sind die in der Konsole angezeigten Zeilennummern und Dateinamen korrekt, da debug.logetc tatsächlich ein Alias ​​von console.logetc. ist.

Wenn isDebug == false keine Debug-Meldungen angezeigt werden, weil debug.logetc einfach nichts tut (eine leere Funktion).

Wie Sie bereits wissen, werden die Zeilennummern und Dateinamen durch eine Wrapper-Funktion durcheinander gebracht. Es empfiehlt sich daher, die Verwendung von Wrapper-Funktionen zu vermeiden.

Alvin Wong
quelle
Großartig, ich muss vorsichtig mit der Reihenfolge von isDebug = trueund sein debug.js, aber diese Antwort funktioniert!
Rufus
3
window.debug = window.consolewäre ein bisschen sauberer.
Fredrik
@fredrik dann muss ich alle Mitgliedsfunktionen "implementieren" wenn isDebug == false. : {
Alvin Wong
@AlvinWong Ich menat nur für wenn isDebug===true. Oder Ereignis dazu: jsfiddle.net/fredrik/x6Jw5
fredrik
4

Stack-Trace-Lösungen zeigen die Zeilennummer an, erlauben jedoch nicht das Klicken, um zur Quelle zu gelangen. Dies ist ein großes Problem. Die einzige Lösung, um dieses Verhalten beizubehalten , besteht darin , an die ursprüngliche Funktion zu binden .

Durch die Bindung wird verhindert, dass Zwischenlogik eingeschlossen wird, da diese Logik mit Zeilennummern in Konflikt geraten würde. Indem Sie jedoch gebundene Funktionen neu definieren und mit der Ersetzung von Konsolenzeichenfolgen spielen ist jedoch noch ein zusätzliches Verhalten möglich.

Diese Übersicht zeigt ein minimalistisches Protokollierungsframework, das Module, Protokollebenen, Formatierungen und ordnungsgemäß anklickbare Zeilennummern in 34 Zeilen bietet. Verwenden Sie es als Grundlage oder Inspiration für Ihre eigenen Bedürfnisse.

var log = Logger.get("module").level(Logger.WARN);
log.error("An error has occured", errorObject);
log("Always show this.");

EDIT: Kern unten enthalten

/*
 * Copyright 2016, Matthieu Dumas
 * This work is licensed under the Creative Commons Attribution 4.0 International License.
 * To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/
 */

/* Usage : 
 * var log = Logger.get("myModule") // .level(Logger.ALL) implicit
 * log.info("always a string as first argument", then, other, stuff)
 * log.level(Logger.WARN) // or ALL, DEBUG, INFO, WARN, ERROR, OFF
 * log.debug("does not show")
 * log("but this does because direct call on logger is not filtered by level")
 */
var Logger = (function() {
    var levels = {
        ALL:100,
        DEBUG:100,
        INFO:200,
        WARN:300,
        ERROR:400,
        OFF:500
    };
    var loggerCache = {};
    var cons = window.console;
    var noop = function() {};
    var level = function(level) {
        this.error = level<=levels.ERROR ? cons.error.bind(cons, "["+this.id+"] - ERROR - %s") : noop;
        this.warn = level<=levels.WARN ? cons.warn.bind(cons, "["+this.id+"] - WARN - %s") : noop;
        this.info = level<=levels.INFO ? cons.info.bind(cons, "["+this.id+"] - INFO - %s") : noop;
        this.debug = level<=levels.DEBUG ? cons.log.bind(cons, "["+this.id+"] - DEBUG - %s") : noop;
        this.log = cons.log.bind(cons, "["+this.id+"] %s");
        return this;
    };
    levels.get = function(id) {
        var res = loggerCache[id];
        if (!res) {
            var ctx = {id:id,level:level}; // create a context
            ctx.level(Logger.ALL); // apply level
            res = ctx.log; // extract the log function, copy context to it and returns it
            for (var prop in ctx)
                res[prop] = ctx[prop];
            loggerCache[id] = res;
        }
        return res;
    };
    return levels; // return levels augmented with "get"
})();

Solendil
quelle
Diese Antwort hat nur 3 positive Stimmen, ist aber unglaublich reichhaltiger und sauberer als jede andere auf der Seite
Tom
Es sieht jedoch so aus, als ob sich alle nützlichen Teile auf einem externen Kern befinden.
Ryan The Leach
3

Die Idee mit bind Function.prototype.bindist genial. Sie können auch den npm library lines-logger verwenden . Es zeigt Ursprungsquelldateien:

Erstellen Sie einmal in Ihrem Projekt einen Logger:

var LoggerFactory = require('lines-logger').LoggerFactory;
var loggerFactory = new LoggerFactory();
var logger = loggerFactory.getLoggerColor('global', '#753e01');

Protokolle drucken:

logger.log('Hello world!')();

Geben Sie hier die Bildbeschreibung ein

Deathangel908
quelle
2

Hier ist eine Möglichkeit, Ihre vorhandenen consoleProtokollierungsanweisungen beizubehalten, während Sie der Ausgabe einen Dateinamen und eine Zeilennummer oder andere Stapelverfolgungsinformationen hinzufügen:

(function () {
  'use strict';
  var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
  var isChrome = !!window.chrome && !!window.chrome.webstore;
  var isIE = /*@cc_on!@*/false || !!document.documentMode;
  var isEdge = !isIE && !!window.StyleMedia;
  var isPhantom = (/PhantomJS/).test(navigator.userAgent);
  Object.defineProperties(console, ['log', 'info', 'warn', 'error'].reduce(function (props, method) {
    var _consoleMethod = console[method].bind(console);
    props[method] = {
      value: function MyError () {
        var stackPos = isOpera || isChrome ? 2 : 1;
        var err = new Error();
        if (isIE || isEdge || isPhantom) { // Untested in Edge
          try { // Stack not yet defined until thrown per https://docs.microsoft.com/en-us/scripting/javascript/reference/stack-property-error-javascript
            throw err;
          } catch (e) {
            err = e;
          }
          stackPos = isPhantom ? 1 : 2;
        }

        var a = arguments;
        if (err.stack) {
          var st = err.stack.split('\n')[stackPos]; // We could utilize the whole stack after the 0th index
          var argEnd = a.length - 1;
          [].slice.call(a).reverse().some(function(arg, i) {
            var pos = argEnd - i;
            if (typeof a[pos] !== 'string') {
              return false;
            }
            if (typeof a[0] === 'string' && a[0].indexOf('%') > -1) { pos = 0 } // If formatting
            a[pos] += ' \u00a0 (' + st.slice(0, st.lastIndexOf(':')) // Strip out character count
              .slice(st.lastIndexOf('/') + 1) + ')'; // Leave only path and line (which also avoids ":" changing Safari console formatting)
            return true;
          });
        }
        return _consoleMethod.apply(null, a);
      }
    };
    return props;
  }, {}));
}());

Dann benutze es so:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <script src="console-log.js"></script>
</head>
<body>
  <script>
  function a () {
    console.log('xyz'); // xyz   (console-log.html:10)
  }
  console.info('abc'); // abc   (console-log.html:12)
  console.log('%cdef', "color:red;"); // (IN RED:) // def   (console-log.html:13)
  a();
  console.warn('uuu'); // uuu   (console-log.html:15)
  console.error('yyy'); // yyy   (console-log.html:16)
  </script>
</body>
</html>

Dies funktioniert in Firefox, Opera, Safari, Chrome und IE 10 (noch nicht auf IE11 oder Edge getestet).

Brett Zamir
quelle
Gute Arbeit, aber immer noch nicht 100% was ich brauche. Ich möchte die Dateinamen- und Zeilennummerninformationen auf der rechten Seite der Konsolenansicht haben, auf die geklickt werden kann, um die Quelle zu öffnen. Diese Lösung zeigt die Informationen als Teil der Nachricht an (wie my test log message (myscript.js:42) VM167 mypage.html:15folgt :), die nicht so gut zu lesen ist und nicht verknüpft ist. Immer noch gute Arbeit also eine Gegenstimme.
Frederic Leitenberger
Ja, obwohl das ideal wäre, gibt es keine Möglichkeit, AFAIK, den Dateinamen-Link zu fälschen, der in der Konsole angezeigt wird ...
Brett Zamir
@BrettZamir hat hier eine Frage zu diesem Code gestellt: stackoverflow.com/questions/52618368/…
Mahks
1
//isDebug controls the entire site.
var isDebug = true;

//debug.js
function debug(msg, level){
    var Global = this;
    if(!(Global.isDebug && Global.console && Global.console.log)){
        return;
    }
    level = level||'info';
    return 'console.log(\'' + level + ': '+ JSON.stringify(msg) + '\')';
}

//main.js
eval(debug('Here is a msg.'));

Das wird mir geben info: "Here is a msg." main.js(line:2).

Aber das Extra evalist nötig, schade.

Rufus
quelle
2
eval ist böse! Also jedes Übel.
Fredrik
1

Code von http://www.briangrinstead.com/blog/console-log-helper-function :

// Full version of `log` that:
//  * Prevents errors on console methods when no console present.
//  * Exposes a global 'log' function that preserves line numbering and formatting.
(function () {
  var method;
  var noop = function () { };
  var methods = [
      'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
      'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
      'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
      'timeStamp', 'trace', 'warn'
  ];
  var length = methods.length;
  var console = (window.console = window.console || {});

  while (length--) {
    method = methods[length];

    // Only stub undefined methods.
    if (!console[method]) {
        console[method] = noop;
    }
  }


  if (Function.prototype.bind) {
    window.log = Function.prototype.bind.call(console.log, console);
  }
  else {
    window.log = function() { 
      Function.prototype.apply.call(console.log, console, arguments);
    };
  }
})();

var a = {b:1};
var d = "test";
log(a, d);
Timo Kähkönen
quelle
Dies scheint nicht die ursprüngliche Zeilennummer zu zeigen, von der logaus
ragamufin
Ich bin mir fast sicher, dass es beim Testen funktioniert hat, aber ich habe den Code durch die "Vollversion" derselben Seite ersetzt. Arbeitete mindestens in Chrome 45.
Timo Kähkönen
Verstanden. Mit den Änderungen, die Sie jetzt haben, ist es im Wesentlichen dasselbe wie bei einigen anderen Antworten und funktioniert. Ich war nur neugierig auf Ihren vorherigen Code, weil Sie am Ende eine Bewerbung hatten, die mir interessante Möglichkeiten eröffnete, diese für mehr zu verwenden, aber da sie nicht die Zeilennummer zeigte, war ich wieder auf dem ersten Platz. Trotzdem danke!
Ragamufin
1

Ich habe mich in letzter Zeit selbst mit diesem Thema befasst. Benötigte etwas sehr einfaches, um die Protokollierung zu steuern, aber auch um die Zeilennummern beizubehalten. Meine Lösung sieht im Code nicht so elegant aus, bietet aber das, was für mich benötigt wird. Wenn man mit Verschlüssen und Zurückhalten vorsichtig genug ist.

Ich habe am Anfang der Anwendung einen kleinen Wrapper hinzugefügt:

window.log = {
    log_level: 5,
    d: function (level, cb) {
        if (level < this.log_level) {
            cb();
        }
    }
};

Damit ich später einfach machen kann:

log.d(3, function(){console.log("file loaded: utils.js");});

Ich habe es von Firefox und Crome getestet, und beide Browser scheinen das Konsolenprotokoll wie beabsichtigt anzuzeigen. Wenn Sie so ausfüllen, können Sie die 'd'-Methode jederzeit erweitern und andere Parameter an sie übergeben, damit eine zusätzliche Protokollierung durchgeführt werden kann.

Ich habe noch keine ernsthaften Nachteile für meinen Ansatz gefunden, außer der hässlichen Zeile im Code für die Protokollierung.

Vladimir M.
quelle
1

window.line = function () {
    var error = new Error(''),
        brower = {
            ie: !-[1,], // !!window.ActiveXObject || "ActiveXObject" in window
            opera: ~window.navigator.userAgent.indexOf("Opera"),
            firefox: ~window.navigator.userAgent.indexOf("Firefox"),
            chrome: ~window.navigator.userAgent.indexOf("Chrome"),
            safari: ~window.navigator.userAgent.indexOf("Safari"), // /^((?!chrome).)*safari/i.test(navigator.userAgent)?
        },
        todo = function () {
            // TODO: 
            console.error('a new island was found, please told the line()\'s author(roastwind)');        
        },
        line = (function(error, origin){
            // line, column, sourceURL
            if(error.stack){
                var line,
                    baseStr = '',
                    stacks = error.stack.split('\n');
                    stackLength = stacks.length,
                    isSupport = false;
                // mac版本chrome(55.0.2883.95 (64-bit))
                if(stackLength == 11 || brower.chrome){
                    line = stacks[3];
                    isSupport = true;
                // mac版本safari(10.0.1 (12602.2.14.0.7))
                }else if(brower.safari){
                    line = stacks[2];
                    isSupport = true;
                }else{
                    todo();
                }
                if(isSupport){
                    line = ~line.indexOf(origin) ? line.replace(origin, '') : line;
                    line = ~line.indexOf('/') ? line.substring(line.indexOf('/')+1, line.lastIndexOf(':')) : line;
                }
                return line;
            }else{
                todo();
            }
            return '😭';
        })(error, window.location.origin);
    return line;
}
window.log = function () {
    var _line = window.line.apply(arguments.callee.caller),
        args = Array.prototype.slice.call(arguments, 0).concat(['\t\t\t@'+_line]);
    window.console.log.apply(window.console, args);
}
log('hello');

Hier war meine Lösung zu dieser Frage. Wenn Sie die Methode: log aufrufen, wird die Zeilennummer gedruckt, in der Sie Ihr Protokoll drucken

Feng Li
quelle
1

Eine kleine Variation besteht darin, dass debug () eine Funktion zurückgibt, die dann dort ausgeführt wird, wo Sie sie benötigen - debug (message) (); Auf diese Weise wird die richtige Zeilennummer und das aufrufende Skript im Konsolenfenster korrekt angezeigt, wobei Variationen wie das Umleiten als Warnung oder das Speichern in einer Datei berücksichtigt werden.

var debugmode='console';
var debugloglevel=3;

function debug(msg, type, level) {

  if(level && level>=debugloglevel) {
    return(function() {});
  }

  switch(debugmode) {
    case 'alert':
      return(alert.bind(window, type+": "+msg));
    break;
    case 'console':
      return(console.log.bind(window.console, type+": "+msg));
    break;
    default:
      return (function() {});
  }

}

Da es eine Funktion zurückgibt, muss diese Funktion in der Debug-Zeile mit (); ausgeführt werden. Zweitens wird die Nachricht an die Debug-Funktion gesendet und nicht an die zurückgegebene Funktion, die eine Vorverarbeitung oder Überprüfung ermöglicht, die Sie möglicherweise benötigen, z. B. den Status auf Protokollebene überprüfen, die Lesbarkeit der Nachricht verbessern, verschiedene Typen überspringen oder nur Elemente melden Erfüllung der Protokollebenenkriterien;

debug(message, "serious", 1)();
debug(message, "minor", 4)();
Saul Dobney
quelle
1

Sie könnten die Logik hier vereinfachen. Dies setzt voraus, dass Ihr globales Debug-Flag NICHT dynamisch ist und beim Laden der App gesetzt oder als Konfiguration übergeben wird. Dies ist für die Umgebungskennzeichnung vorgesehen (z. B. nur Drucken im Entwicklungsmodus und nicht im Produktionsmodus).

Vanilla JS:

(function(window){ 
  var Logger = {},
      noop = function(){};

  ['log', 'debug', 'info', 'warn', 'error'].forEach(function(level){
    Logger[level] = window.isDebug ? window.console[level] : noop;
  });

  window.Logger = Logger;
})(this);

ES6:

((window) => {
  const Logger = {};
  const noop = function(){};

  ['log', 'debug', 'info', 'warn', 'error'].forEach((level) => {
    Logger[level] = window.isDebug ? window.console[level] : noop;
  });

  window.Logger = Logger;
})(this);

Modul:

const Logger = {};
const noop = function(){};

['log', 'debug', 'info', 'warn', 'error'].forEach((level) => {
  Logger[level] = window.isDebug ? window.console[level] : noop;
});

export default Logger;

Winkel 1.x:

angular
  .module('logger', [])
  .factory('Logger', ['$window',
    function Logger($window) {
      const noop = function(){};
      const logger = {};

      ['log', 'debug', 'info', 'warn', 'error'].forEach((level) => {
        logger[level] = $window.isDebug ? $window.console[level] : noop;
      });

      return logger;
    }
  ]);

Jetzt müssen Sie nur noch alle Konsolenreferenzen durch Logger ersetzen

dysfunc
quelle
1

Diese Implementierung basiert auf der ausgewählten Antwort und hilft, das Rauschen in der Fehlerkonsole zu reduzieren: https://stackoverflow.com/a/32928812/516126

var Logging = Logging || {};

const LOG_LEVEL_ERROR = 0,
    LOG_LEVEL_WARNING = 1,
    LOG_LEVEL_INFO = 2,
    LOG_LEVEL_DEBUG = 3;

Logging.setLogLevel = function (level) {
    const NOOP = function () { }
    Logging.logLevel = level;
    Logging.debug = (Logging.logLevel >= LOG_LEVEL_DEBUG) ? console.log.bind(window.console) : NOOP;
    Logging.info = (Logging.logLevel >= LOG_LEVEL_INFO) ? console.log.bind(window.console) : NOOP;
    Logging.warning = (Logging.logLevel >= LOG_LEVEL_WARNING) ? console.log.bind(window.console) : NOOP;
    Logging.error = (Logging.logLevel >= LOG_LEVEL_ERROR) ? console.log.bind(window.console) : NOOP;

}

Logging.setLogLevel(LOG_LEVEL_INFO);
Brian
quelle
0

Ich fand einige der Antworten auf dieses Problem etwas zu komplex für meine Bedürfnisse. Hier ist eine einfache Lösung, die in Coffeescript gerendert wird. Es ist eine Adaption von Brian Grinsteads Version hier

Es wird das globale Konsolenobjekt angenommen.

# exposes a global 'log' function that preserves line numbering and formatting.
(() ->
    methods = [
      'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
      'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
      'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
      'timeStamp', 'trace', 'warn']
    noop = () ->
    # stub undefined methods.
    for m in methods  when  !console[m]
        console[m] = noop

    if Function.prototype.bind?
        window.log = Function.prototype.bind.call(console.log, console);
    else
        window.log = () ->
            Function.prototype.apply.call(console.log, console, arguments)
)()
Sandover
quelle
0

Die Art und Weise, wie ich es gelöst habe, bestand darin, ein Objekt zu erstellen, dann mit Object.defineProperty () eine neue Eigenschaft für das Objekt zu erstellen und die Konsoleneigenschaft zurückzugeben, die dann als normale Funktion verwendet wurde, jetzt jedoch mit der erweiterten Fähigkeit.

var c = {};
var debugMode = true;

var createConsoleFunction = function(property) {
    Object.defineProperty(c, property, {
        get: function() {
            if(debugMode)
                return console[property];
            else
                return function() {};
        }
    });
};

Um eine Eigenschaft zu definieren, tun Sie einfach ...

createConsoleFunction("warn");
createConsoleFunction("log");
createConsoleFunction("trace");
createConsoleFunction("clear");
createConsoleFunction("error");
createConsoleFunction("info");

Und jetzt können Sie Ihre Funktion genauso nutzen

c.error("Error!");
Abel Rodríguez
quelle
0

Basierend auf anderen Antworten (hauptsächlich @arctelix one) habe ich dies für Node ES6 erstellt, aber ein schneller Test zeigte auch im Browser gute Ergebnisse. Ich übergebe nur die andere Funktion als Referenz.

let debug = () => {};
if (process.argv.includes('-v')) {
    debug = console.log;
    // debug = console; // For full object access
}
vandijkstef
quelle
0

Hier ist meine Logger-Funktion (basierend auf einigen Antworten). Hoffe, jemand kann davon Gebrauch machen:

const DEBUG = true;

let log = function ( lvl, msg, fun ) {};

if ( DEBUG === true ) {
    log = function ( lvl, msg, fun ) {
        const d = new Date();
        const timestamp = '[' + d.getHours() + ':' + d.getMinutes() + ':' +
            d.getSeconds() + '.' + d.getMilliseconds() + ']';
        let stackEntry = new Error().stack.split( '\n' )[2];
        if ( stackEntry === 'undefined' || stackEntry === null ) {
            stackEntry = new Error().stack.split( '\n' )[1];
        }
        if ( typeof fun === 'undefined' || fun === null ) {
            fun = stackEntry.substring( stackEntry.indexOf( 'at' ) + 3,
                stackEntry.lastIndexOf( ' ' ) );
            if ( fun === 'undefined' || fun === null || fun.length <= 1 ) {
                fun = 'anonymous';
            }
        }
        const idx = stackEntry.lastIndexOf( '/' );
        let file;
        if ( idx !== -1 ) {
            file = stackEntry.substring( idx + 1, stackEntry.length - 1 );
        } else {
            file = stackEntry.substring( stackEntry.lastIndexOf( '\\' ) + 1,
                stackEntry.length - 1 );
        }
        if ( file === 'undefined' || file === null ) {
            file = '<>';
        }

        const m = timestamp + ' ' + file + '::' + fun + '(): ' + msg;

        switch ( lvl ) {
        case 'log': console.log( m ); break;
        case 'debug': console.log( m ); break;
        case 'info': console.info( m ); break;
        case 'warn': console.warn( m ); break;
        case 'err': console.error( m ); break;
        default: console.log( m ); break;
        }
    };
}

Beispiele:

log( 'warn', 'log message', 'my_function' );
log( 'info', 'log message' );
beschädigen
quelle