Подтвердить что ты не робот

Расширение console.log без влияния на строку журнала

Я хотел бы расширить функцию "console.log", чтобы добавить дополнительную информацию к ее выводу, но я не хочу влиять на информацию о имени/номере номера script, сгенерированную браузером в окне консоли. Посмотрите, как я создаю свою собственную реализацию, я получаю бесполезную информацию о трассировке, должен ли я найти эту область кода... (все они ссылаются на реализацию журнала, а не на фактический script, который вызвал сообщение журнала)

enter image description here

В принципе, мое приложение представляет собой очень подключаемую инфраструктуру, и любой выход журнала может возникать в любом количестве кадров. Поэтому я хочу, чтобы каждое сообщение журнала включало специальный уникальный идентификатор в начале сообщения журнала.

Я попытался заменить метод console.log своим, но хром жалуется на Uncaught TypeError: Illegal invocation

вот как я его переопределяю

var orig = console.log;
console.log = function( message )
{
    orig( (window == top ? '[root]' : '[' + window.name + ']') + ': ' + message );
}

Любые идеи?

[EDIT] Примечание. После исправления проблемы "незаконного вызова" кажется, что имя файла /linenumber все еще "загрязнено" переопределением...

[EDIT] Похоже, что общий ответ - НЕТ - несмотря на некоторые запутанные гонимые погони, желаемая функциональность НЕ достижима в текущих версиях браузеров.

4b9b3361

Ответ 1

Да, можно добавить информацию, не испортив номера исходной строки вызова журнала. Некоторые из других ответов здесь приблизились, но трюк заключается в том, чтобы ваш пользовательский метод ведения журнала возвращал модифицированный регистратор. Ниже приведен простой пример, который был только умеренно проверен, который использует контекстный вариант.

log = function() {
    var context = "My Descriptive Logger Prefix:";
    return Function.prototype.bind.call(console.log, console, context);
}();

Это можно использовать с:

log("A log message..."); 

Вот jsfiddle: http://jsfiddle.net/qprro98v/

Можно легко получить креатив и передать переменную контекста, а также удалить автоматически исполняемые параметры из определения функции. то есть log ( "DEBUG:" ) ( "Отладочное сообщение" ), журнал ( "INFO:" ) ( "Вот некоторая информация" ) и т.д.

Единственная действительно импортирующая часть функции (в отношении номеров строк) заключается в том, что она возвращает регистратор.

Ответ 2

Если ваш случай использования может иметь дело с несколькими ограничениями, есть способ, которым это можно заставить работать. Ограничения:

  • Дополнительный лог-контент должен быть рассчитан во время привязки; он не может быть чувствительным к времени или в зависимости от сообщения входящего журнала.

  • Дополнительный контент журнала может быть размещен только в начале сообщения журнала.

С этими ограничениями для вас может работать следующее:

var context = "ALIASED LOG:"
var logalias;

if (console.log.bind === 'undefined') { // IE < 10
    logalias = Function.prototype.bind.call(console.log, console, context);
}
else {
    logalias = console.log.bind(console, context);
}

logalias('Hello, world!');

http://jsfiddle.net/Wk2mf/

Ответ 3

На самом деле это возможно в хроме. Вот самое актуальное. Это может варьироваться в зависимости от настройки, и как я получил расщепления, нужно просто зарегистрировать весь стек и найти нужную мне информацию.

        var stack = new Error().stack;
        var file = stack.split("\n")[2].split("/")[4].split("?")[0]
        var line = stack.split("\n")[2].split(":")[5];

Вот и все, сохраняя ведение журнала собственных объектов.

var orig = console.log
console.log = function(input) {
    var isChrome = navigator.userAgent.indexOf("Chrome") !== -1;
    if(isChrome){
        var stack = new Error().stack;
        var file = stack.split("\n")[2].split("/")[4].split("?")[0]
        var line = stack.split("\n")[2].split(":")[5];
        var append = file + ":" + line;
    }
    orig.apply(console, [input, append])
}

Ответ 4

Допустимым решением может быть создание собственной функции регистрации, которая возвращает функцию console.log, связанную с аргументами журнала.

log = function() {
    // Put your extension code here
    var args = Array.prototype.slice.call(arguments);		
    args.unshift(console);
    return Function.prototype.bind.apply(console.log, args);
}

// Note the extra () to call the original console.log
log("Foo", {bar: 1})();

Ответ 5

Вам нужно вызвать console.log с правильным контекстом (console):

orig.call(console, message);

Чтобы выполнить свою функцию, разрешив несколько аргументов:

var orig = console.log;
console.log = function() {
    var msgs = [],
        prefix = (window== top ? '[root]' : '[' + window.name + ']');
    while(arguments.length) {
        msgs.push(prefix + ': ' + [].shift.call(arguments));
    }
    orig.apply(console, msgs);
};

Демо: http://jsfiddle.net/je2wR/

Помните, что вы теряете встроенный браузер объектов/массивов в консоли при объединении объектов со строками с помощью знака +.

Ответ 6

Я только что ответил на это сообщение, которое помогло мне ответить на исходный вопрос "alias":

(http://stackoverflow.com/a/12942764/401735)

my_log_alias = console.log.bind(console)

По-видимому, была создана возможность сделать это. Протестировано. Работает.

после этого my_log_alias совпадает с console.log и может быть вызван таким же образом; Вызов этого изнутри функции будет сообщать номер строки для этого вызова функции, включая строку внутри псевдонима или функции совета, где это применимо.

В частности, номер строки, которую предоставляет Chrome, укажет вам файл, в котором находится строка, поэтому то, что вы делаете, может быть ненужным; Подумайте о том, как сообщить об этом в качестве запроса об ошибке/функции в chrome, чтобы он предоставил эту информацию в console.log.

Ответ 7

Кристофер Керри обеспечил отличное решение. Я немного расширил его для своих нужд. Здесь модуль AMD:

define([], function () {

    var enableDebug = true;
    var separator = ">";    

    function bind(f, thisArg, ctx) {
        if (f.bind !== 'undefined') { // IE < 10
            return Function.prototype.bind.call(f, thisArg, ctx);
        }
        else {
            return f.bind(thisArg, ctx);
        }
    }

    function newConsole(context, parentConsole) {
        var log;
        var debug;
        var warn;
        var error;

        if (!parentConsole) {
            parentConsole = console;
        }

        context = context + separator;


        if (enableDebug) {
            debug = bind(console.log, console, context + "DEBUG" + separator);
        } else {
            debug = function () {
                // suppress all debug messages
            };
        }

        log = bind(console.log, console, context);

        warn = bind(console.warn, console, context);

        error = bind(console.error, console, context);

        return {
            debug: debug,
            info: log,
            log: log,
            warn: warn,
            error: error,
            /* access console context information */
            context: context,
            /* create a new console with nested context */
            nest: function (subContext) {
                return newConsole(context + subContext, this);
            },
            parent: parentConsole
        };
    }

    return newConsole("");
});

По умолчанию это выведет > {message}. Вы также можете добавить вложенные контексты для ведения журнала, например. console.nest("my").log("test") выводит >my> test.

Я также добавил функцию debug, которая будет отступать сообщениями с >DEBUG>

Надеюсь, что кто-нибудь найдет это полезным.

Ответ 8

Я просмотрел это несколько раз и всегда считал, что это невозможно.

Мое обходное решение, если вам интересно, - назначить консоль другой переменной, а затем обернуть все мои сообщения журнала в функцию, которая позволяет мне изменять/стиль/что угодно в сообщении.

Он отлично выглядит с CoffeeScript, не уверен, что он практичен с простым JS.

Я просто привык к префиксу всего x.

logger.debug x 'Foo'

log x 'Bar'

log x('FooBar %o'), obj

Ответ 9

В настоящее время это невозможно, в будущем мы можем сделать это с помощью объекта Proxy в ECMAScript 6.

Мой вариант использования состоял в том, чтобы автоматически префиксировать консольные сообщения с полезной информацией, такой как переданные аргументы и метод выполнения. в настоящий момент наиболее близким является использование Function.prototype.apply.

Простой подход состоит в том, чтобы просто написать ваши отладочные операторы как таковые:

console.info('=== LazyLoad.css(', arguments, '): css files are skipped, gives us a clean slate to style within theme\ CSS.');

Сложный подход заключается в использовании вспомогательной функции, как описано ниже, я лично сейчас предпочитаю простой подход.

Extending 'console.debug' function approach

/* Debug prefixing function
 * ===========================
 * 
 * A helper used to provide useful prefixing information 
 * when calling 'console.log', 'console.debug', 'console.error'.
 * But the catch is that to utilize one must leverage the 
 * '.apply' function as shown in the below examples.
 *
 * '''
 * console.debug.apply(console, _fDebugPrefix(arguments)
 *    .concat('your message'));
 *
 * // or if you need to pass non strings
 * console.debug.apply(console, _fDebugPrefix(arguments)
 *    .concat('json response was:', oJson));
 *
 *
 * // if you need to use strict mode ("use strict") one can't
 * // extract the function name but following approach works very
 * // well; updating the name is just a matter of search and replace
 * var aDebugPrefix = ['fYourFunctionName('
 *                     ,Array.prototype.slice.call(arguments, 0), 
 *                     ,')'];
 * console.debug.apply(console, 
 *                     aDebugPrefix.concat(['json response was:', oJson]));
 * '''
 */
function _fDebugPrefix(oArguments) {
    try {
        return [oArguments.callee.name + '('
                ,Array.prototype.slice.call(oArguments, 0)
                , ')'];
    }
    catch(err) { // are we in "use strict" mode ?
        return ['<callee.name unsupported in "use strict">('
                ,Array.prototype.slice.call(oArguments, 0)
                , ')'];
    }
}

Ответ 10

Я столкнулся с этой проблемой также о расширении console.log(), чтобы приложение могло расширять, управлять и делать причудливые вещи вместе с тем, чтобы записывать материал на консоль. Однако потеря информации о номере линии была равносильна отказу. После борьбы с этим вопросом я придумал длинный способ обхода, но, по крайней мере, он все еще использует "1-лайнер".

Сначала определите глобальный класс для использования или добавления некоторых методов в ваш основной существующий класс "app":

/**
 * Log message to our in-app and possibly on-screen console, return args.
 * @param {!string} aMsgLevel - one of "log", "debug", "info", "warn", or "error"
 * @param {any} aArgs - the arguments to log (not used directly, just documentation helper)
 * @returns args so it can be nested within a console.log.apply(console,app.log()) statement.
 */
MyGlobalClassWithLogMethods.prototype.debugLog = function(aMsgLevel, aArgs) {
    var s = '';
    var args = [];
    for (var i=1; i<arguments.length; i++) {
        args.push(arguments[i]);
        if (arguments[i])
            s += arguments[i].toString()+' ';
    }
    if (typeof this.mLog === 'undefined')
        this.mLog = [];
    this.mLog.push({level: aMsgLevel, msg: s});
    return args;
};

MyGlobalClassWithLogMethods.prototype.log = function() {
    var args = ['log'].concat(Array.prototype.slice.call(arguments));
    return this.debugLog.apply(this,args);
};

MyGlobalClassWithLogMethods.prototype.debug = function() {
    var args = ['debug'].concat(Array.prototype.slice.call(arguments));
    return this.debugLog.apply(this,args);
};

MyGlobalClassWithLogMethods.prototype.info = function() {
    var args = ['info'].concat(Array.prototype.slice.call(arguments));
    return this.debugLog.apply(this,args);
};

MyGlobalClassWithLogMethods.prototype.warn = function() {
    var args = ['warn'].concat(Array.prototype.slice.call(arguments));
    return this.debugLog.apply(this,args);
};

MyGlobalClassWithLogMethods.prototype.error = function() {
    var args = ['error'].concat(Array.prototype.slice.call(arguments));
    return this.debugLog.apply(this,args);
};

//not necessary, but it is used in my example code, so defining it
MyGlobalClassWithLogMethods.prototype.toString = function() {
    return "app: " + JSON.stringify(this);
};

Затем мы применяем эти методы так:

//JS line done as early as possible so rest of app can use logging mechanism
window.app = new MyGlobalClassWithLogMethods();

//only way to get "line info" reliably as well as log the msg for actual page display;
//  ugly, but works. Any number of params accepted, and any kind of var will get
//  converted to str using .toString() method.
console.log.apply(console,app.log('the log msg'));
console.debug.apply(console,app.debug('the log msg','(debug)', app));
console.info.apply(console,app.info('the log msg','(info)'));
console.warn.apply(console,app.warn('the log msg','(warn)'));
console.error.apply(console,app.error('the log msg','(error)'));

Теперь консоль получает сообщения журнала с соответствующей информацией о строке, а также наше приложение содержит массив сообщений журнала, которые можно использовать. Например, чтобы отобразить ваш журнал в приложении с помощью HTML, JQuery и некоторых CSS, можно использовать следующий упрощенный пример.

Во-первых, HTML:

<div id="debug_area">
    <h4 class="text-center">Debug Log</h4>
    <ul id="log_list">
        <!-- console log/debug/info/warn/error ('msg') lines will go here -->
    </ul>
</div>

некоторый CSS:

.log_level_log {
    color: black;
    background-color: white;
    font-size: x-small;
}
.log_level_debug {
    color: #060;
    background-color: #80FF80;
    font-size: x-small;
}
.log_level_info {
    color: #00F;
    background-color: #BEF;
    font-size: x-small;
}
.log_level_warn {
    color: #E65C00;
    background-color: #FB8;
    font-size: x-small;
}
.log_level_error {
    color: #F00;
    background-color: #FBB;
    font-size: x-small;
}

и некоторые JQuery:

var theLog = app.mLog || [];
if (theLog.length>0) {
    var theLogList = $('#log_list');
    theLogList.empty();
    for (var i=0; i<theLog.length; i++) {
        theLogList.prepend($('<li class="log_level_'+theLog[i].level+'"></li>').text(theLog[i].msg));
    }
}

Это упрощенное использование, но как только у вас есть механизм на месте, вы можете делать все, что может придумать ваше воображение, в том числе оставить строки журнала в коде, но установить порог, чтобы пройти только предупреждения и ошибки, Надеюсь, это поможет другим своим проектам.

Ответ 11

Попробуйте setTimeout(console.log.bind(console,'foo'));

Ответ 12

Недавно Chrome представила функцию, которая может решить вашу проблему без взлома кода. Он называется "черный ящик", который в основном позволяет отмечать файлы, которые следует игнорировать с помощью своих инструментов.

https://gist.github.com/paulirish/c307a5a585ddbcc17242

Да, это решение специфично для браузера, но если вы используете Chrome, вам нужно это решение.

Решения с огромным взломом при опрокидывании ошибки для каждого журнала могут отображать правильную строку, но это не будет кликабельная ссылка в вашей консоли.

Решения, основанные на привязке/сглаживании, позволяют изменять печатный текст. Вы не сможете переслать аргументы третьей функции для дальнейшей обработки.