Właściwe opakowanie dla console.log z poprawnym numerem linii?

133

Obecnie tworzę aplikację i umieszczam isDebugprzełącznik globalny . Chciałbym owinąć się console.logdla wygodniejszego użytkowania.

//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.');

Następnie otrzymuję ten wynik w konsoli Firefox.

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

Co jeśli chcę zalogować się z numerem linii debug(), na przykład info: Here is a msg. main.js (line 2)?

Rufus
źródło
Możesz użyć console.logdo informacji, console.warnostrzeżenia i console.errorbłędu, zamiast dodawać coś console.logza pomocą funkcji opakowującej.
Alvin Wong,
2
@AlvinWong Tak, wiem o tym, ale problem polega na tym, że potrzebuję globalnego przełącznika debugowania, który kontroluje, czy consolenależy go użyć. Aby osiągnąć taki cel, jedyną drogą wydaje się opakowanie?
Rufus,
W przypadku przeglądarki Google Chrome patrz stackoverflow.com/a/25729203/1429301 W twoim przypadku wzorcem byłby debug.js
Alexander

Odpowiedzi:

117

To jest stare pytanie i wszystkie udzielone odpowiedzi są zbyt hakerskie, mają DUŻE problemy z przeglądarkami i nie zapewniają niczego super użytecznego. To rozwiązanie działa w każdej przeglądarce i raportuje wszystkie dane konsoli dokładnie tak, jak powinno. Nie są wymagane żadne hacki i jedna linia kodu Sprawdź kod .

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

Utwórz przełącznik w ten sposób:

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

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

Następnie po prostu zadzwoń w następujący sposób:

debug('This is happening.')

Możesz nawet przejąć console.log za pomocą takiego przełącznika:

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

Jeśli chcesz zrobić z tym coś pożytecznego ... Możesz dodać wszystkie metody konsoli i opakować je w funkcję wielokrotnego użytku, która daje nie tylko kontrolę globalną, ale także poziom klasy:

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

Teraz możesz dodać go do swoich zajęć:

var MyClass = function() {
  this.isDebug = true //local state
  this.debug = Debugger(isDebug, this)
  this.debug.warn('It works in classses')
}
arctelix
źródło
18
Popraw mnie, jeśli się mylę, ale to nie pozwala jednak na dodanie żadnych dodatkowych funkcji, prawda? W zasadzie aliasujesz tylko obiekt konsoli? Prymitywny przykład - nie ma możliwości, aby console.log () zdarzenie dwukrotnie dla każdego debug.log ()?
AB Carroll
3
@ABCarroll Możesz console.logdwukrotnie, wiążąc niestandardową log()funkcję zawierającą dwa wywołania console.log, jednak numery linii odzwierciedlałyby linię, która console.logfaktycznie się znajduje, a nie miejsce, w którym debug.logjest wywoływana. Możesz jednak zrobić takie rzeczy, jak dodawanie dynamicznych prefiksów / sufiksów itp. Istnieją również sposoby na zrekompensowanie problemu z numerem linii, ale myślę, że to kolejne pytanie. Sprawdź ten projekt, aby zobaczyć przykład: github.com/arctelix/iDebugConsole/blob/master/README.md
arctelix
2
Ta metoda nie działa w Firefoksie od wersji 47 do 49 włącznie. I został naprawiony tylko w wersji 50.0a2. Cóż, FF50 zostanie wydany za 2 tygodnie, ale spędzam kilka godzin, aby zrozumieć, dlaczego nie działa. Więc myślę, że ta informacja może być pomocna dla kogoś. link
Vladimir Liubimov
Uważam, że @ABCarroll miał na myśli to, że wszystko wewnątrz instancji nie może być używane w czasie wykonywania. na przykład stan globalny można zdefiniować tylko w instancji, więc jeśli później zmienisz this.isDebugna false, nie będzie to miało znaczenia. Po prostu nie wiem, czy da się to obejść, może jest to zamierzone. W tym sensie isDebugjest to dość mylące vari powinno być constzamiast tego.
cregox
4
To nie odpowiada na pytanie "A co, jeśli chcę logować się z numerem linii, gdzie wywoływana jest funkcja debug ()?"
technomage
24

Podobała mi się odpowiedź @ fredrik , więc zwinąłem ją z inną odpowiedzią, która dzieli ślad stosu Webkit i połączyłem ją z bezpiecznym opakowaniem console.log @ PaulIrish . „Standaryzuje” na filename:line„obiekt specjalny”, dzięki czemu wyróżnia się i wygląda prawie tak samo w FF i Chrome.

Testowanie na skrzypcach: 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

Działa to również w węźle i możesz to przetestować za pomocą:

// 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
źródło
nieco bardziej zaawansowaną odpowiedź na uwagę dodatkowych consolemetod, takich jak warn, erroritp - stackoverflow.com/a/14842659/1037948
drzaus
1
var line = stack.split('\n')[2];-'undefined' is not an object
sygnatura 27.07.15
@sigod - prawdopodobnie albo zależy od przeglądarki, albo że napisałem to 2 lata temu i przeglądarki się zmieniły. jaki jest twój scenariusz
drzaus
1
jeden z moich kolegów skopiował Twój kod i wkleił go do naszego projektu. Zepsuło witrynę w IE11 i Safari 5. Nie jestem pewien co do innych wersji tej przeglądarki. Może dodasz czek dla przyszłych kopistów?
sygnatura 27.07.15
1
@sigod, a co teraz? dodane if(!stack) return '?'do metody, która zawodzi, a nie gdzie jest wywoływana (więc jeśli ktoś używa samej metody, jest również „chroniony”)
drzaus
20

Możesz zachować numery wierszy i wyprowadzić poziom dziennika, używając sprytnie 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)

Idąc o krok dalej, możesz skorzystać z consolerozróżnień błąd / ostrzeżenie / informacje i nadal mieć niestandardowe poziomy. Spróbuj!

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
źródło
1
Od jakiegoś czasu próbuję automatycznie poprzedzić wyjście console.debug(...)z function namei arguments- czy są jakieś przemyślenia, jak to zrobić?
Daniel Sokolowski
3
Patrzyłem na mnóstwo opakowań konsoli / podkładek / itp. i to jest pierwszy, z jakim się spotkałem, łączący w sobie zachowanie numerów linii z dostosowywaniem wyjścia. To sprytne wykorzystanie faktu, że .bind również robi za ciebie curry, możesz powiązać jeden lub więcej argumentów oprócz kontekstu . Możesz pójść o krok dalej i przekazać mu funkcję noop z metodą .toString, która może uruchamiać kod, gdy wywoływana jest metoda dziennika! Zobacz to jsfiddle
Sam Hasler
2
Może nie we wszystkich przeglądarkach (nie spojrzał na niego), ale zastępując %sze %ow Chrome będzie drukować parametry w sposób, że można się spodziewać (obiekty są rozszerzalne, numery i łańcuchy są kolorowe, etc).
anson
uwielbiam to rozwiązanie. Wprowadziłem kilka zmian, które działają lepiej w mojej aplikacji, ale większość z nich jest nadal nienaruszona i działa pięknie. Dzięki
Ward
9

Od: Jak uzyskać numer linii funkcji wywołującej JavaScript? Jak uzyskać adres URL źródła wywołującego JavaScript? Errorobiekt ma właściwość numer wiersza (w FF). Więc coś takiego powinno działać:

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

W przeglądarce Webkit masz err.stackciąg znaków reprezentujący aktualny stos wywołań. Wyświetli aktualny numer linii i więcej informacji.

AKTUALIZACJA

Aby uzyskać poprawny numer linii, musisz wywołać błąd w tej linii. Coś jak:

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
źródło
1
new Error();daje mi kontekst, w którym jest wykonywany, jeśli go wstawię, debug.jsotrzymam info: Here is a msg. file: http://localhost/js/debug.js line:7.
Rufus,
1
Po co Log = Error? Nadal modyfikujesz klasę Error, prawda?
drzaus
Połącz
8

Sposób na zachowanie numeru linii jest tutaj: https://gist.github.com/bgrins/5108712 . Mniej więcej sprowadza się to do tego:

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);
    };
}

Możesz to opakować isDebugi ustawić window.logna, function() { }jeśli nie debugujesz.

Brian Grinstead
źródło
7

Możesz przekazać numer linii do metody debugowania, na przykład:

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

Tutaj (new Error).lineNumberdaje ci bieżący numer linii w twoim javascriptkodzie.

Subodh
źródło
2
Trochę rozwlekłe, prawda?
Rufus,
2
Myślę, że wystarczy odpowiedzieć na Twoje pytanie. :)
Subodh,
1
właściwość lineNumber jest niestandardowa i obecnie działa tylko w przeglądarce Firefox, zobacz tutaj
Matthias
7

Chrome Devtools pozwala to osiągnąć dzięki Blackboxing . Możesz utworzyć opakowanie console.log, które może mieć efekty uboczne, wywoływać inne funkcje itp. I nadal zachować numer wiersza, który wywołał funkcję opakowania.

Wystarczy umieścić małe opakowanie console.log w osobnym pliku, np

(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)
    }
})()

Nazwij go na przykład log-blackbox.js

Następnie przejdź do ustawień Chrome Devtools i znajdź sekcję „Blackboxing”, dodaj wzorzec nazwy pliku, który chcesz blackbox, w tym przypadku log-blackbox.js

kzahel
źródło
Uwaga: upewnij się, że nie masz żadnego kodu, który chciałbyś pokazać w śladzie stosu w tym samym pliku, ponieważ zostanie on również usunięty ze śladu.
jamesthollowell
6

Znalazłem proste rozwiązanie, aby połączyć zaakceptowaną odpowiedź (powiązanie z console.log / błąd / itp.) Z jakąś zewnętrzną logiką do filtrowania tego, co jest faktycznie rejestrowane.

// 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;

Stosowanie:

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
  • Zauważ, że console.assertużywa rejestrowania warunkowego.
  • Upewnij się, że narzędzia programistyczne przeglądarki wyświetlają wszystkie poziomy wiadomości!
Jacob Phillips
źródło
Ponieważ nie podaje numeru linii ani przykładów roboczych pokazujących poziom dziennika.
not2qubit
Numer linii będzie taki sam, jak przy bezpośrednim korzystaniu z konsoli. Zaktualizowałem odpowiedź o przykłady użycia. Nie ma wielu głosów, ponieważ odpowiedziałem dwa lata później :)
Jacob Phillips
4

Jeśli chcesz po prostu kontrolować, czy używane jest debugowanie i mieć poprawny numer linii, możesz to zrobić:

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(){}
    };
}

Jeśli potrzebujesz dostępu do debugowania, możesz to zrobić:

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

Jeśli isDebug == true, numery wierszy i nazwy plików wyświetlane w konsoli będą poprawne, ponieważ debug.logetc jest w rzeczywistości aliasem console.logitp.

Jeśli isDebug == falsenie są wyświetlane żadne komunikaty debugowania, ponieważ debug.logetc po prostu nic nie robi (pusta funkcja).

Jak już wiesz, funkcja opakowująca zepsuje numery wierszy i nazwy plików, więc dobrym pomysłem jest zapobieganie używaniu funkcji opakowujących.

Alvin Wong
źródło
Świetnie, muszę uważać na kolejność isDebug = truei debug.js, ale ta odpowiedź działa!
Rufus,
3
window.debug = window.consolebyłoby trochę czystsze.
fredrik,
@fredrik, wtedy będę musiał "zaimplementować" wszystkie funkcje składowe, jeśli isDebug == false. : {
Alvin Wong,
@AlvinWong Po prostu menat for if isDebug===true. Lub wydarzenie do tego: jsfiddle.net/fredrik/x6Jw5
Fredrik
4

Rozwiązania śledzenia stosu wyświetlają numer linii, ale nie pozwalają na kliknięcie, aby przejść do źródła, co jest głównym problemem. Jedynym rozwiązaniem pozwalającym zachować to zachowanie jest powiązanie z oryginalną funkcją.

Wiązanie uniemożliwia włączenie logiki pośredniej, ponieważ ta logika mogłaby zepsuć numery linii. Jednak dzięki przedefiniowaniu funkcji związanych i graniu z podstawianiem ciągów konsoli , nadal możliwe jest pewne dodatkowe zachowanie.

To streszczenie przedstawia minimalistyczną strukturę rejestrowania, która oferuje moduły, poziomy dziennika, formatowanie i odpowiednie klikalne numery linii w 34 liniach. Wykorzystaj go jako podstawę lub inspirację do własnych potrzeb.

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

EDYCJA: treść zawarta poniżej

/*
 * 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
źródło
Ta odpowiedź ma tylko 3 głosy za, ale jest niewiarygodnie bogatsza i bardziej przejrzysta niż jakakolwiek inna na stronie
Tom,
jednak wygląda na to, że wszystkie użyteczne części mają charakter zewnętrzny.
Ryan The Leach
3

Pomysł z bindem Function.prototype.bindjest genialny. Możesz również użyć rejestratora wierszy biblioteki npm . Pokazuje pliki źródłowe pochodzenia:

Utwórz rejestrator każdego raz w swoim projekcie:

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

Drukuj dzienniki:

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

wprowadź opis obrazu tutaj

śmierć anioła908
źródło
2

Oto sposób na zachowanie istniejących consoleinstrukcji rejestrowania podczas dodawania nazwy pliku i numeru wiersza lub innych informacji o śledzeniu stosu do danych wyjściowych:

(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;
  }, {}));
}());

Następnie użyj tego w ten sposób:

<!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>

Działa to w przeglądarkach Firefox, Opera, Safari, Chrome i IE 10 (jeszcze nie testowane w IE11 ani Edge).

Brett Zamir
źródło
Dobra robota, ale wciąż nie w 100% tego, czego potrzebuję. Chciałbym mieć informacje o nazwie pliku i numerze wiersza po prawej stronie widoku konsoli, gdzie można je kliknąć, aby otworzyć źródło. To rozwiązanie pokazuje informacje jako część wiadomości (jak ta my test log message (myscript.js:42) VM167 mypage.html:15:), która nie jest tak dobra do czytania, a także nie jest połączona. Wciąż dobra robota, a więc upvote.
Frederic Leitenberger,
Tak, podczas gdy byłby idealny, nie ma sposobu, AFAIK, aby sfałszować link nazwę, która pojawia się w konsoli ...
Brett Zamir
@BrettZamir opublikował pytanie dotyczące tego kodu tutaj: 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.'));

To mi da info: "Here is a msg." main.js(line:2).

Ale evaltrzeba, szkoda.

Rufus
źródło
2
eval jest zło! Więc każde zło.
fredrik,
1

Kod z 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
źródło
Nie wydaje się, aby zobaczyć oryginalny numer linii gdzie logjest wywoływana z
ragamufin
Jestem prawie pewien, że zadziałało, gdy testowałem, ale zamieniłem kod na „pełną” wersję z tej samej strony. Pracował co najmniej w Chrome 45.
Timo Kähkönen
Zrozumiany. Dzięki zmianom, które masz teraz, zasadniczo jest to to samo, co kilka innych odpowiedzi i działa. Byłem po prostu ciekawy twojego poprzedniego kodu, ponieważ na końcu miałeś aplikację, która dała mi interesujące możliwości wykorzystania go na więcej, ale ponieważ nie pokazywał numeru linii, wróciłem do punktu wyjścia. W każdym razie dzięki!
ragamufin
1

Sam ostatnio przyglądałem się temu problemowi. Potrzebowałem czegoś bardzo prostego do sterowania rejestrowaniem, ale także do zachowania numerów linii. Moje rozwiązanie nie wygląda tak elegancko w kodzie, ale zapewnia to, co jest mi potrzebne. Jeśli ktoś jest wystarczająco ostrożny przy zamykaniu i utrzymywaniu.

Dodałem małą otulinę na początek aplikacji:

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

Więc później mogę po prostu zrobić:

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

Przetestowałem to z Firefox i Crome, i obie przeglądarki wydają się wyświetlać dziennik konsoli zgodnie z przeznaczeniem. Jeśli wypełnisz w ten sposób, zawsze możesz rozszerzyć metodę 'd' i przekazać do niej inne parametry, aby mógł wykonać dodatkowe logowanie.

Nie znalazłem jeszcze żadnych poważnych wad mojego podejścia, z wyjątkiem brzydkiej linii w kodzie do logowania.

Vladimir M.
źródło
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');

oto moje rozwiązanie na to pytanie. kiedy wywołasz metodę: log, wydrukuje ona numer linii, w której drukujesz dziennik

Feng Li
źródło
1

Mała zmiana polega na tym, że funkcja debug () zwraca funkcję, która jest następnie wykonywana tam, gdzie jej potrzebujesz - debug (message) (); i tak prawidłowo pokazuje poprawny numer linii i skrypt wywołujący w oknie konsoli, jednocześnie zezwalając na różne warianty, takie jak przekierowanie jako alert lub zapisywanie do pliku.

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() {});
  }

}

Ponieważ zwraca funkcję, ta funkcja musi zostać wykonana w linii debugowania za pomocą () ;. Po drugie, wiadomość jest wysyłana do funkcji debugowania, a nie do funkcji zwracanej, która umożliwia wstępne przetwarzanie lub sprawdzenie, czego możesz potrzebować, na przykład sprawdzenie stanu na poziomie dziennika, zwiększenie czytelności wiadomości, pomijanie różnych typów lub tylko raportowanie elementów spełniające kryteria poziomu dziennika;

debug(message, "serious", 1)();
debug(message, "minor", 4)();
Saul Dobney
źródło
1

Tutaj możesz uprościć logikę. Zakłada się, że Twoja globalna flaga debugowania NIE jest dynamiczna i ustawiona podczas ładowania aplikacji lub przekazywana jako konfiguracja. Ma to służyć do oznaczania środowiska (np. Drukowanie tylko w trybie deweloperskim, a nie produkcyjnym)

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);

Moduł:

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

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

export default Logger;

Kątowy 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;
    }
  ]);

Wszystko, co musisz teraz zrobić, to zamienić wszystkie referencje konsoli na Logger

dysfunc
źródło
1

Ta implementacja jest oparta na wybranej odpowiedzi i pomaga zmniejszyć ilość szumów w konsoli błędów: 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
źródło
0

Niektóre odpowiedzi na ten problem okazały się dla mnie zbyt skomplikowane. Oto proste rozwiązanie, wyrenderowane w Coffeescript. Dostosowano go z wersji Briana Grinsteada tutaj

Zakłada globalny obiekt konsoli.

# 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
źródło
0

Sposób, w jaki to rozwiązałem, polegał na utworzeniu obiektu, a następnie utworzeniu nowej właściwości obiektu za pomocą Object.defineProperty () i zwróceniu właściwości konsoli, która była wtedy używana jako normalna funkcja, ale teraz z rozszerzonymi możliwościami.

var c = {};
var debugMode = true;

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

Następnie, aby zdefiniować właściwość, po prostu wykonaj ...

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

Teraz możesz używać swojej funkcji tak samo, jak

c.error("Error!");
Abel Rodríguez
źródło
0

Na podstawie innych odpowiedzi (głównie @arctelix) stworzyłem to dla Node ES6, ale szybki test pokazał również dobre wyniki w przeglądarce. Po prostu przekazuję drugą funkcję jako odniesienie.

let debug = () => {};
if (process.argv.includes('-v')) {
    debug = console.log;
    // debug = console; // For full object access
}
vandijkstef
źródło
0

Oto moja funkcja rejestratora (oparta na niektórych odpowiedziach). Mam nadzieję, że ktoś może to wykorzystać:

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;
        }
    };
}

Przykłady:

log( 'warn', 'log message', 'my_function' );
log( 'info', 'log message' );
zniszczyć
źródło
0

Wiem, że to stare pytanie, ale już od jakiegoś czasu pracuję nad własnym rozwiązaniem. Wracam, żeby zobaczyć, czy są tam nowe informacje. Ponieważ nie wygląda na to, żeby ktoś się zbliżył, pomyślałem, że wrzucę moje rozwiązanie do wyżymaczki (ponieważ wydaje się, że nadal jest to najbardziej aktywny wątek w tej sprawie):

https://github.com/fatlard1993/log

Początkowo zastosowałem podobne podejście do niektórych innych odpowiedzi, dopóki nie dowiedziałem się o proxy. To dało mi pomysł i motywację, aby zrobić to, czym jest teraz.

justFatLard
źródło