wrapper ที่เหมาะสมสำหรับ console.log ด้วยหมายเลขบรรทัดที่ถูกต้อง?


132

ตอนนี้ฉันกำลังพัฒนาแอปพลิเคชันและวางisDebugสวิตช์ส่วนกลาง ฉันต้องการที่จะห่อconsole.logเพื่อการใช้งานที่สะดวกมากขึ้น

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

จากนั้นฉันจะได้ผลลัพธ์นี้ในคอนโซล Firefox

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

ถ้าฉันต้องการเข้าสู่ระบบที่มีจำนวนสายที่debug()ได้รับการเรียกเช่นinfo: Here is a msg. main.js (line 2)?


คุณสามารถใช้console.logสำหรับข้อมูลconsole.warnคำเตือนและconsole.errorข้อผิดพลาดแทนที่จะเพิ่มบางอย่างconsole.logผ่านฟังก์ชั่น wrapper
อัลวินหว่อง

2
@AlvinWong ใช่ฉันรู้ว่า แต่ปัญหาคือฉันต้องการสวิตช์ดีบักระดับโลกซึ่งควบคุมว่าconsoleจะต้องใช้หรือไม่ เพื่อให้บรรลุเป้าหมายดังกล่าวเสื้อคลุมดูเหมือนจะเป็นวิธีเดียว?
รูฟัส

สำหรับ Google Chrome ดูstackoverflow.com/a/25729203/1429301 ในกรณีของคุณรูปแบบจะเป็น debug.js
Frison Alexander

คำตอบ:


117

นี่เป็นคำถามเก่าและคำตอบทั้งหมดที่ให้ไว้คือแฮ็คกี้มากเกินไปมีปัญหาข้ามเบราว์เซอร์ MAJOR และไม่มีประโยชน์ใด ๆ โซลูชันนี้ทำงานได้ในทุกเบราว์เซอร์และรายงานข้อมูลคอนโซลทั้งหมดอย่างที่ควรจะเป็น ไม่มี hacks จำเป็นและหนึ่งบรรทัดของรหัสตรวจสอบ codepen

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

สร้างสวิตช์แบบนี้:

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

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

จากนั้นเพียงโทรไปที่:

debug('This is happening.')

คุณสามารถเข้าควบคุม console.log ด้วยสวิตช์ดังนี้:

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

หากคุณต้องการทำสิ่งที่มีประโยชน์กับสิ่งนั้น .. คุณสามารถเพิ่มวิธีการคอนโซลทั้งหมดและห่อมันในฟังก์ชั่นที่นำกลับมาใช้ใหม่ซึ่งไม่เพียง แต่ให้การควบคุมระดับโลก แต่ยังมีระดับชั้นเรียนด้วย:

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

ตอนนี้คุณสามารถเพิ่มลงในชั้นเรียนของคุณ:

var MyClass = function() {
  this.isDebug = true //local state
  this.debug = Debugger(isDebug, this)
  this.debug.warn('It works in classses')
}

16
ถูกต้องฉันถ้าฉันผิด แต่สิ่งนี้ไม่อนุญาตให้คุณเพิ่มฟังก์ชันการทำงานเพิ่มเติมใด ๆ คุณเป็นเพียงแค่นามแฝงวัตถุคอนโซล? ตัวอย่างที่หยาบ - ไม่มีทางที่จะ console.log () เหตุการณ์สองครั้งสำหรับ debug.log () ทุกครั้งหรือไม่
AB Carroll

3
@ABCarroll คุณสามารถทำได้console.logสองครั้งด้วยการผูกlog()ฟังก์ชั่นที่กำหนดเองที่มีการโทรสองconsole.logครั้งอย่างไรก็ตามหมายเลขบรรทัดจะแสดงถึงสายที่มีconsole.logอยู่จริงไม่ใช่ตำแหน่งที่debug.logถูกเรียก อย่างไรก็ตามคุณสามารถทำสิ่งต่าง ๆ เช่นเพิ่มคำนำหน้าแบบไดนามิก / คำต่อท้าย ฯลฯ นอกจากนี้ยังมีวิธีการชดเชยปัญหาหมายเลขบรรทัด แต่นั่นคือคำถามอื่นที่ฉันคิด ลองดูตัวอย่างจากโครงการนี้: github.com/arctelix/iDebugConsole/blob/master/README.md
arctelix

2
วิธีนี้ใช้ไม่ได้กับ Firefox ตั้งแต่เวอร์ชัน 47 ถึง 49 และได้รับการแก้ไขในรุ่น 50.0a2 เท่านั้น FF50 จะออกวางจำหน่ายใน 2 สัปดาห์ แต่ฉันใช้เวลาหลายชั่วโมงกว่าจะรู้ว่าทำไมมันไม่ทำงาน ดังนั้นฉันคิดว่าข้อมูลนี้อาจเป็นประโยชน์กับใครบางคน ลิงค์
Vladimir Liubimov

ฉันเชื่อว่า @ABCarroll หมายถึงทุกอย่างที่อยู่ภายในอินสแตนซ์ไม่สามารถใช้งานได้ในขณะทำงาน สำหรับกรณีอื่นของรัฐทั่วโลกสามารถกำหนดเฉพาะใน instantiation ดังนั้นหากคุณในภายหลังเปลี่ยนthis.isDebugไปfalseก็จะไม่เป็นเรื่อง ฉันไม่ทราบว่ามีวิธีแก้ไขปัญหานี้หรือไม่โดยการออกแบบ ในแง่ที่ว่าisDebugเป็นความเข้าใจผิดมากvarและควรจะเป็นconstแทน
cregox

2
สิ่งนี้ไม่ตอบคำถาม "ถ้าฉันต้องการเข้าสู่ระบบด้วยหมายเลขบรรทัดที่ debug () ถูกเรียก?"
technomage

24

ฉันชอบ@ คำตอบของ Fredrikดังนั้นฉันรีดขึ้นกับคำตอบที่แยก stacktrace Webkit อื่นและรวมกับ@ PaulIrish ของเสื้อคลุม "ทำให้มาตรฐาน" filename:lineเป็น "วัตถุพิเศษ" เพื่อให้โดดเด่นและดูเป็นส่วนใหญ่ใน FF และ Chrome

การทดสอบในซอ: 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

สิ่งนี้ยังทำงานในโหนดและคุณสามารถทดสอบได้ด้วย:

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

คำตอบที่เล็กน้อยที่สูงขึ้นไปยังบัญชีสำหรับเสริมconsoleวิธีการเช่นwarn, errorฯลฯ - stackoverflow.com/a/14842659/1037948
drzaus

1
var line = stack.split('\n')[2];-'undefined' is not an object
sigod

@sigod - อาจขึ้นอยู่กับเบราว์เซอร์หรือว่าฉันเขียนเมื่อ 2 ปีก่อนและเบราว์เซอร์มีการเปลี่ยนแปลง สถานการณ์ของคุณคืออะไร
drzaus

1
เพื่อนร่วมงานคนหนึ่งของฉันคัดลอกโค้ดของคุณลงในโครงการของเรา มันทำให้ไซต์แตกใน IE11 และ Safari 5 ไม่แน่ใจเกี่ยวกับเบราว์เซอร์รุ่นอื่น บางทีคุณอาจเพิ่มการตรวจสอบเพื่อคัดลอกภัยพิบัติในอนาคต?
sigod

1
@ sigod แล้วตอนนี้ล่ะ? เพิ่มif(!stack) return '?'ไปยังวิธีการที่ล้มเหลวมากกว่าที่จะถูกเรียก (ดังนั้นถ้าใครใช้วิธีการที่ตัวเองพวกเขากำลัง "ป้องกัน" ด้วย)
drzaus

18

คุณสามารถรักษาหมายเลขบรรทัดและส่งออกระดับบันทึกด้วยการใช้ฉลาด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)

หากใช้ขั้นตอนต่อไปคุณสามารถใช้ประโยชน์จากconsoleความแตกต่างของข้อผิดพลาด / คำเตือน / ข้อมูลและยังคงมีระดับที่กำหนดเอง ลองมัน!

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)

1
ฉันพยายามมาพักหนึ่งแล้วเพื่อนำหน้าผลลัพธ์โดยอัตโนมัติconsole.debug(...)ด้วย- function nameและargumentsมีความคิดเกี่ยวกับวิธีการทำเช่นนั้นหรือไม่?
Daniel Sokolowski

3
ฉันได้ดูความหลากหลายของคอนโซล wrappers / shims / etc และนี่เป็นครั้งแรกที่ฉันได้เจอที่รวมการรักษาหมายเลขบรรทัดกับการปรับแต่งผลลัพธ์ มันเป็นความฉลาดใช้ความจริงที่ว่า.bind ยังไม่ currying บางอย่างสำหรับคุณคุณสามารถผูกหนึ่งหรือมากกว่าหนึ่งข้อโต้แย้งนอกเหนือไปจากบริบท คุณสามารถทำขั้นตอนต่อไปและส่งผ่านฟังก์ชั่น noop ด้วย. toString วิธีการที่สามารถเรียกใช้รหัสเมื่อเรียกวิธีการบันทึก! ดูjsfiddle นี้
Sam Hasler

2
อาจไม่ได้อยู่ในเบราว์เซอร์ทั้งหมด (ยังไม่ได้ตรวจสอบ) แต่การแทนที่%sด้วย%oใน Chrome จะพิมพ์พารามิเตอร์ในแบบที่คุณคาดหวัง (วัตถุสามารถขยายได้ตัวเลขและสตริงเป็นสี ฯลฯ )
anson

รักทางออกนี้ ฉันทำการเปลี่ยนแปลงเล็กน้อยซึ่งทำงานได้ดีขึ้นสำหรับแอปพลิเคชันของฉัน แต่ส่วนใหญ่ของมันยังคงอยู่และทำงานได้อย่างสวยงาม ขอบคุณ
วอร์ด

9

จาก: วิธีรับหมายเลขบรรทัดฟังก์ชัน JavaScript caller วิธีรับ URL แหล่งที่มาของตัวเรียก JavaScript Errorวัตถุมีคุณสมบัติหมายเลขบรรทัด (ใน FF) ดังนั้นสิ่งนี้ควรทำงาน:

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

ในเบราว์เซอร์ Webkit คุณมี err.stackสตริงที่แสดงถึงสแต็คการโทรปัจจุบัน มันจะแสดงหมายเลขบรรทัดปัจจุบันและข้อมูลเพิ่มเติม

UPDATE

ในการรับชุดผ้าปูที่นอนที่ถูกต้องคุณต้องเรียกใช้ข้อผิดพลาดในบรรทัดนั้น สิ่งที่ต้องการ:

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

1
new Error();ทำให้ผมมีบริบทที่จะรันถ้าผมใส่ไว้ในแล้วฉันจะได้รับdebug.js info: Here is a msg. file: http://localhost/js/debug.js line:7
รูฟัส

1
จุดประสงค์Log = Errorคืออะไร คุณยังแก้ไขคลาส Error อยู่ใช่ไหม
drzaus

รวมคำตอบของคุณกับคนอื่น ๆ - ดูด้านล่างstackoverflow.com/a/14841411/1037948
drzaus

8

วิธีการเก็บหมายเลขบรรทัดอยู่ที่นี่: https://gist.github.com/bgrins/5108712 มันมากหรือน้อยลงไปจนถึงสิ่งนี้:

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

คุณสามารถปิดสิ่งนี้ด้วยisDebugและตั้งค่าwindow.logเป็นfunction() { }หากคุณไม่ได้ดีบั๊ก


7

คุณสามารถส่งหมายเลขบรรทัดไปยังวิธีการแก้ปัญหาของคุณเช่นนี้:

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

ที่นี่(new Error).lineNumberจะให้หมายเลขบรรทัดปัจจุบันในjavascriptรหัสของคุณ


2
verbose เล็กน้อยใช่ไหม
รูฟัส

2
ฉันคิดว่ามันเพียงพอที่จะตอบคำถามของคุณ :)
Subodh

1
คุณสมบัติ lineNumber ไม่ใช่แบบมาตรฐานและใช้ได้เฉพาะกับ Firefox ตอนนี้ดูที่นี่
Matthias

6

Chrome Devtools ช่วยให้คุณทำสิ่งนี้ได้ด้วยBlackboxing Blackboxingคุณสามารถสร้าง wrapper console.log ที่สามารถมีผลข้างเคียงเรียกใช้ฟังก์ชันอื่น ๆ และยังคงหมายเลขบรรทัดที่เรียกว่าฟังก์ชัน wrapper

เพียงใส่เสื้อคลุม console.log ขนาดเล็กลงในไฟล์แยกต่างหากเช่น

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

ตั้งชื่อบางอย่างเช่น log-blackbox.js

จากนั้นไปที่การตั้งค่า Chrome Devtools และค้นหาส่วน "Blackboxing" เพิ่มรูปแบบชื่อไฟล์ที่คุณต้องการ Blackbox ในกรณีนี้ log-blackbox.js


หมายเหตุ: ให้แน่ใจว่าคุณไม่ได้มีรหัสใด ๆ ที่คุณจะต้องการที่จะแสดงขึ้นมาในกองติดตามในไฟล์เดียวกันขณะที่มันยังจะถูกลบออกจากการติดตาม
jamesthollowell

6

ฉันพบวิธีง่ายๆในการรวมคำตอบที่ยอมรับได้ (การผูกกับ console.log / ข้อผิดพลาด / ฯลฯ ) กับตรรกะภายนอกบางอย่างเพื่อกรองสิ่งที่บันทึกไว้จริง

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

การใช้งาน:

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
  • โปรดทราบว่าconsole.assertใช้การบันทึกตามเงื่อนไข
  • ตรวจสอบให้แน่ใจว่าเครื่องมือ dev ของเบราว์เซอร์แสดงระดับข้อความทั้งหมด!

เพราะมันไม่ได้ให้หมายเลขบรรทัดใด ๆ หรือตัวอย่างการทำงานแสดงระดับการบันทึก
not2qubit

หมายเลขบรรทัดจะเหมือนกับถ้าใช้คอนโซลโดยตรง ฉันอัพเดตคำตอบด้วยตัวอย่างการใช้งาน มันไม่ได้มีคะแนนเสียงจำนวนมากเพราะผมตอบมันสองปีต่อมา :)
จาค็อบฟิลลิป

4

หากคุณต้องการควบคุมว่าจะใช้การดีบักหรือไม่และมีหมายเลขบรรทัดที่ถูกต้องคุณสามารถทำได้ดังนี้:

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

เมื่อคุณต้องการเข้าถึงการดีบักคุณสามารถทำได้:

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

หากisDebug == trueหมายเลขบรรทัดและชื่อไฟล์ที่แสดงในคอนโซลจะถูกต้องเพราะdebug.logจริง ๆ แล้วเป็นนามแฝงconsole.logเป็นต้น

ถ้าisDebug == falseไม่มีข้อความดีบั๊กที่แสดงเพราะdebug.logอื่น ๆ ก็ไม่ทำอะไรเลย (ฟังก์ชั่นที่ว่างเปล่า)

อย่างที่คุณทราบแล้วฟังก์ชั่น wrapper จะทำให้หมายเลขบรรทัดและชื่อไฟล์สับสนดังนั้นจึงเป็นความคิดที่ดีที่จะป้องกันไม่ให้ใช้ฟังก์ชั่น wrapper


เยี่ยมมากฉันต้องระวังเกี่ยวกับลำดับisDebug = trueและdebug.jsแต่คำตอบนี้ใช้ได้ผล!
รูฟัส

3
window.debug = window.consoleจะสะอาดกว่านี้นิดหน่อย
fredrik

@fredrik แล้วฉันจะต้อง "ใช้" isDebug == falseทุกฟังก์ชั่สมาชิกถ้า : {
Alvin Wong

@AlvinWong ฉันเพียงแค่ Menat isDebug===trueถ้า หรือเหตุการณ์นี้: jsfiddle.net/fredrik/x6Jw5
fredrik

4

โซลูชันการติดตามสแต็กแสดงหมายเลขบรรทัด แต่ไม่อนุญาตให้คลิกเพื่อไปยังแหล่งซึ่งเป็นปัญหาที่สำคัญ ทางออกเดียวที่จะทำให้พฤติกรรมนี้คือการผูกกับฟังก์ชั่นเดิม

การเชื่อมโยงป้องกันไม่ให้รวมตรรกะระดับกลางเนื่องจากตรรกะนี้จะยุ่งกับหมายเลขบรรทัด อย่างไรก็ตามโดยการกำหนดฟังก์ชั่นที่ถูกผูกไว้ใหม่และเล่นกับการทดแทนสตริงคอนโซลพฤติกรรมบางอย่างเพิ่มเติมยังคงเป็นไปได้

สรุปสาระสำคัญนี้แสดงกรอบการบันทึกที่เรียบง่ายที่นำเสนอโมดูลระดับการบันทึกการจัดรูปแบบและหมายเลขบรรทัดที่คลิกได้ที่เหมาะสมใน 34 บรรทัด ใช้เป็นพื้นฐานหรือแรงบันดาลใจสำหรับความต้องการของคุณเอง

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

แก้ไข: สรุปสาระสำคัญด้านล่าง

/*
 * 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"
})();


คำตอบนี้มีเพียง 3 upvotes แต่ยิ่งรวยและสะอาดกว่าคนอื่น ๆ ในหน้าอย่างไม่น่าเชื่อ
Tom

อย่างไรก็ตามดูเหมือนว่าทุกส่วนที่มีประโยชน์อยู่ในส่วนสำคัญภายนอก
Ryan The Leach

3

ความคิดที่มีการผูกFunction.prototype.bindเป็นเลิศ นอกจากนี้คุณยังสามารถใช้ NPM ห้องสมุดเส้นตัดไม้ มันแสดงให้เห็นว่าไฟล์ต้นฉบับกำเนิด:

สร้างคนตัดไม้ทุกคนในโครงการของคุณ:

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

พิมพ์บันทึก:

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

ป้อนคำอธิบายรูปภาพที่นี่


2

นี่คือวิธีเก็บรักษาconsoleคำสั่งการบันทึกที่มีอยู่ของคุณในขณะที่เพิ่มชื่อไฟล์และหมายเลขบรรทัดหรือข้อมูลการติดตามสแต็กอื่น ๆ ลงในเอาต์พุต:

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

จากนั้นใช้แบบนี้:

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

สามารถใช้งานได้ใน Firefox, Opera, Safari, Chrome และ IE 10 (ยังไม่ผ่านการทดสอบบน IE11 หรือ Edge)


เยี่ยมมาก แต่ก็ยังไม่ถึง 100% เท่าที่ฉันต้องการ ฉันต้องการมีข้อมูลชื่อไฟล์และหมายเลขบรรทัดทางด้านขวาของมุมมองคอนโซลซึ่งสามารถคลิกเพื่อเปิดแหล่งที่มา วิธีการแก้ปัญหานี้แสดงข้อมูลเป็นส่วนหนึ่งของข้อความ (เช่นนี้my test log message (myscript.js:42) VM167 mypage.html:15:) ซึ่งไม่ดีเท่าที่อ่านและไม่ได้เชื่อมโยง ยังคงทำงานได้ดีจึง upvote
Frederic Leitenberger

ใช่ในขณะที่จะเหมาะไม่มีทาง AFAIK เพื่อสวมรอยลิงค์ชื่อไฟล์ที่ปรากฏในคอนโซล ...
Brett Zamir

@BrettZamir โพสต์คำถามเกี่ยวกับรหัสนี้ที่นี่: 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.'));

นี้จะให้ฉัน info: "Here is a msg." main.js(line:2)นี้จะให้ฉัน

แต่ความevalต้องการพิเศษก็น่าเสียดาย


2
eval is Evil! ดังนั้นทุกความชั่วร้าย
fredrik

1

รหัสจาก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);

สิ่งนี้ดูเหมือนจะไม่แสดงหมายเลขโทรศัพท์ดั้งเดิมที่logถูกเรียกจาก
ragamufin

ฉันเกือบแน่ใจว่ามันทำงานเมื่อฉันทดสอบ แต่ฉันแทนที่รหัสด้วยรุ่น "เต็ม" จากหน้าเดียวกัน ทำงานอย่างน้อยใน Chrome 45
Timo Kähkönen

เข้าใจ ด้วยการเปลี่ยนแปลงที่คุณมีอยู่ตอนนี้มันก็เหมือนกับคำตอบและผลงานอื่น ๆ ฉันแค่อยากรู้เกี่ยวกับรหัสก่อนหน้าของคุณเพราะคุณมีการสมัครในตอนท้ายที่เพิ่มความเป็นไปได้ที่น่าสนใจสำหรับฉันที่จะใช้สิ่งนี้มากขึ้น แต่เนื่องจากมันไม่แสดงหมายเลขบรรทัดฉันจึงกลับไปที่ตารางหนึ่ง ขอบคุณนะ!
ragamufin

1

ฉันได้ดูปัญหานี้ด้วยตัวเองเมื่อเร็ว ๆ นี้ ต้องการสิ่งที่ตรงไปตรงมาเพื่อควบคุมการบันทึก แต่ยังเพื่อรักษาหมายเลขบรรทัด โซลูชันของฉันไม่ได้ดูสวยงามในโค้ด แต่ให้สิ่งที่จำเป็นสำหรับฉัน หากมีความระมัดระวังเพียงพอกับการปิดและการรักษา

ฉันได้เพิ่ม wrapper เล็ก ๆ ไว้ที่จุดเริ่มต้นของแอปพลิเคชัน:

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

ดังนั้นหลังจากนั้นฉันก็ทำได้:

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

ฉันทดสอบ Firefox และ crome แล้วและเบราว์เซอร์ทั้งคู่ดูเหมือนจะแสดงบันทึกคอนโซลตามที่ตั้งใจไว้ หากคุณกรอกเช่นนั้นคุณสามารถขยายวิธี 'd' และส่งพารามิเตอร์อื่น ๆ ไปยังมันได้ตลอดเวลาเพื่อให้สามารถทำการบันทึกเพิ่มเติมได้

ยังไม่พบข้อเสียร้ายแรงสำหรับวิธีการของฉันเลยยกเว้นบรรทัดที่น่าเกลียดในรหัสสำหรับการบันทึก


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

นี่คือทางออกของฉันเกี่ยวกับคำถามนี้ เมื่อคุณเรียกใช้เมธอด: บันทึกมันจะพิมพ์หมายเลขบรรทัดที่คุณพิมพ์บันทึกของคุณ


1

รูปแบบเล็กน้อยคือให้ debug () คืนค่าฟังก์ชันซึ่งจะถูกดำเนินการตามที่คุณต้องการ - debug (message) (); และแสดงหมายเลขบรรทัดที่ถูกต้องและสคริปต์โทรในหน้าต่างคอนโซลอย่างถูกต้องในขณะที่อนุญาตให้มีรูปแบบต่าง ๆ เช่นการเปลี่ยนเส้นทางเป็นการแจ้งเตือนหรือบันทึกเป็นไฟล์

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

}

เนื่องจากส่งคืนฟังก์ชันฟังก์ชันนั้นจะต้องถูกเรียกใช้งานที่บรรทัด debug ด้วย (); ประการที่สองข้อความจะถูกส่งไปยังฟังก์ชั่นการดีบักแทนที่จะเข้าสู่ฟังก์ชั่นที่ส่งคืนซึ่งช่วยให้การประมวลผลหรือการตรวจสอบล่วงหน้าที่คุณอาจต้องการเช่นการตรวจสอบสถานะระดับบันทึกทำให้ข้อความอ่านง่ายขึ้นข้ามประเภทที่แตกต่างกัน ประชุมตามเกณฑ์ระดับบันทึก

debug(message, "serious", 1)();
debug(message, "minor", 4)();

1

คุณสามารถลดความซับซ้อนของตรรกะได้ที่นี่ สิ่งนี้ถือว่าธงการดีบักทั่วโลกของคุณไม่ได้เป็นแบบไดนามิกและตั้งค่าเมื่อโหลดแอปหรือส่งผ่านเป็นค่ากำหนดบางอย่าง สิ่งนี้มีไว้เพื่อใช้สำหรับการตั้งค่าสภาพแวดล้อม (เช่นพิมพ์เฉพาะเมื่ออยู่ในโหมด dev และไม่ได้ใช้งาน)

วานิลลา 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);

โมดูล:

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

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

export default Logger;

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

สิ่งที่คุณต้องทำตอนนี้คือแทนที่การอ้างอิงคอนโซลทั้งหมดด้วย Logger


1

การใช้งานนี้ขึ้นอยู่กับคำตอบที่เลือกและช่วยลดปริมาณเสียงรบกวนในคอนโซลข้อผิดพลาด: 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);

0

ฉันพบคำตอบของปัญหานี้เล็กน้อยที่ซับซ้อนเกินไปสำหรับความต้องการของฉัน นี่เป็นวิธีแก้ปัญหาง่ายๆที่แสดงใน Coffeescript มันดัดแปลงมาจากเวอร์ชั่นของ Brian Grinstead ที่นี่

จะถือว่าวัตถุคอนโซลทั่วโลก

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

0

วิธีที่ฉันแก้ไขมันคือการสร้างวัตถุจากนั้นสร้างคุณสมบัติใหม่บนวัตถุโดยใช้Object.defineProperty ()และส่งคืนคุณสมบัติคอนโซลซึ่งใช้เป็นฟังก์ชันปกติ แต่ตอนนี้มีความสามารถเพิ่มเติม

var c = {};
var debugMode = true;

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

จากนั้นเพื่อกำหนดคุณสมบัติที่คุณเพิ่งทำ ...

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

และตอนนี้คุณสามารถใช้ฟังก์ชั่นของคุณได้เช่นกัน

c.error("Error!");

0

จากคำตอบอื่น ๆ (ส่วนใหญ่ @arctelix one) ฉันสร้างสิ่งนี้สำหรับ Node ES6 แต่การทดสอบอย่างรวดเร็วแสดงผลลัพธ์ที่ดีในเบราว์เซอร์เช่นกัน ฉันแค่ผ่านฟังก์ชั่นอื่น ๆ เป็นข้อมูลอ้างอิง

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

0

นี่คือฟังก์ชันตัวบันทึกของฉัน (ขึ้นอยู่กับคำตอบบางส่วน) หวังว่าใครบางคนสามารถใช้มันได้:

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

ตัวอย่าง:

log( 'warn', 'log message', 'my_function' );
log( 'info', 'log message' );
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.