39

当我在RUNTIME与基于 AJAX 的应用程序交互时,我希望控制台能够输出它调用的所有函数。(所以没有堆栈跟踪、断点、分析或任何东西)

例如,假设我按下了页面上的一个按钮。我希望它返回它在发生这种情况时经历的所有功能:

所以我会在控制台中看到类似的东西(当我按下按钮时):

1. button1Clicked();
2.     calculating();
3.          printingResults();

这基本上意味着 button1Clicked() 调用了 computed() 调用了 printingResults()

是否有实用程序、插件、浏览器或语言中的某种方式来执行此操作?我正在使用谷歌浏览器,顺便说一句。

ps 和 NO 我不想遍历每个函数并添加一个"console.log("inside function X")"工作量太大的 b/c

pps 作为额外的奖励,我也希望看到参数也传递给函数,但也许这是在推动它。:>

4

9 回答 9

39

我想不出一种很好的方法来全局拦截所有函数调用以插入日志记录(尽管下面的更新部分有一个不错的解决方法)。

相反,仅将日志记录添加到您关心的某个命名空间中的函数怎么样?您可以使用以下设置代码执行此操作:

var functionLogger = {};

functionLogger.log = true;//Set this to false to disable logging 

/**
 * Gets a function that when called will log information about itself if logging is turned on.
 *
 * @param func The function to add logging to.
 * @param name The name of the function.
 *
 * @return A function that will perform logging and then call the function. 
 */
functionLogger.getLoggableFunction = function(func, name) {
    return function() {
        if (functionLogger.log) {
            var logText = name + '(';

            for (var i = 0; i < arguments.length; i++) {
                if (i > 0) {
                    logText += ', ';
                }
                logText += arguments[i];
            }
            logText += ');';

            console.log(logText);
        }

        return func.apply(this, arguments);
    }
};

/**
 * After this is called, all direct children of the provided namespace object that are 
 * functions will log their name as well as the values of the parameters passed in.
 *
 * @param namespaceObject The object whose child functions you'd like to add logging to.
 */
functionLogger.addLoggingToNamespace = function(namespaceObject){
    for(var name in namespaceObject){
        var potentialFunction = namespaceObject[name];

        if(Object.prototype.toString.call(potentialFunction) === '[object Function]'){
            namespaceObject[name] = functionLogger.getLoggableFunction(potentialFunction, name);
        }
    }
};

然后,对于要添加日志记录的任何命名空间对象,您只需调用:

functionLogger.addLoggingToNamespace(yourNamespaceObject);

这是一个在行动中看到它的小提琴。

更新
请注意,您可以调用functionLogger.addLoggingToNamespace(window);以在调用时将日志记录添加到所有全局函数。此外,如果您真的需要,您可以遍历树以查找任何函数并相应地更新它们。这种方法的一个缺点是它只适用于当时存在的函数。因此,它仍然不是最好的解决方案,但它比手动添加日志语句要少很多工作:)

于 2012-08-07T21:05:23.593 回答
17

这称为配置文件,Chrome 和 Firebug 内置了它。在Chrome 开发人员工具中,转到配置文件选项卡并单击记录(圆圈)按钮。执行您的 ajax 并在您响应后,再次单击记录按钮以停止。分析的结果将显示在右窗格中。

请注意,这将为您提供一切,因此如果您使用像 jQuery 这样的库,那么绝大多数函数调用对您来说都是垃圾。我已经尝试了几次,我发现这样做更有帮助console.log('inside <method>')

于 2012-08-07T20:21:58.020 回答
4

我刚刚发现你可以用console.trace()

于 2019-11-27T07:09:45.283 回答
3

也许您可以让 JavaScript 为您添加一些添加 console.log 的工作:

自动将 console.log 添加到每个函数

Paul Irish 的这个博客也可能会有所帮助:

http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/

它包括一个指向一些专门针对日志记录参数的 JavaScript 的链接:

http://pastie.org/1033665

于 2012-08-07T21:20:47.790 回答
3

试试diyism_trace_for_javascript.htm:

https://code.google.com/p/diyism-trace/downloads/list

eval('window.c=function(){3+5;}');
declare_ticks_for(window);

function a(k, c) {
  return k + 2;
}

function b() {
  4 + 3;
  a(3, {'a':'c','b':'d'});
  c();
  return 5 + 4;
}

b();

在 chrome 或 firefox 的控制台选项卡中查看日志

于 2013-01-17T10:18:49.310 回答
3

Briguy37 解决方案的一种变体,我编写了一个接受在每个方法之前调用的函数的解决方案。它也适用于 ECMAScript 6 类,其中方法不是由 for...in 枚举的。我正在使用它来修改对象原型,将日志记录添加到我的对象的所有新实例中。

function inject(obj, beforeFn) {
    for (let propName of Object.getOwnPropertyNames(obj)) {
        let prop = obj[propName];
        if (Object.prototype.toString.call(prop) === '[object Function]') {
            obj[propName] = (function(fnName) {
                return function() {
                    beforeFn.call(this, fnName, arguments);
                    return prop.apply(this, arguments);
                }
            })(propName);
        }
    }
}

function logFnCall(name, args) {
    let s = name + '(';
    for (let i = 0; i < args.length; i++) {
        if (i > 0)
            s += ', ';
        s += String(args[i]);
    }
    s += ')';
    console.log(s);
}

inject(Foo.prototype, logFnCall);
于 2016-07-26T05:01:25.353 回答
1

让我把第三种(当然也有些不完美)的解决方案扔进戒指。

请注意,所有其他答案都提供两种解决方案:

  1. 在运行时手动修补您的 JS 函数,并将它们记录到控制台
    • 是的,它可以完成工作,但是一旦您的项目发展到一定规模,它将毫无用处。它不会给你足够的可控性,除非你一直花时间去开发这个功能。
  2. Jeff 建议使用分析器进行调试
    • 不是很有帮助,因为分析器视图(至少现在)旨在帮助您分析性能,而不是调用图;效果不好,除非您花大量时间训练自己习惯适得其反的用户界面。

这就是我编写 Dbux 的原因,这是一项正在进行中的工作,目前仅作为 VSCode 插件工作,但有一些限制。然而,它是一个无所不知的调试器,具有动态执行分析工具、代码注释和一个成熟的动态调用图可视化工具,旨在帮助开发人员进行程序理解和调试:

链接:

于 2020-09-28T20:14:06.807 回答
0

我使用了@ Briguy37 的解决方案并进行了改进。就我而言,我不想跟踪某些库中的函数,所以我添加了一些代码来排除它们。以下是它的使用方法:

  • 首先,包括你不想跟踪的函数的定义;
  • excludeLoggingToNamespace 列出到目前为止定义的函数并排除它们;
  • 包括要跟踪的函数的定义;
  • 调用 addLoggingToNamespace 为上述步骤中定义的函数添加日志记录功能。

例子:

<script src="js/someLibrary.js"></script>
<script>
    functionLogger.excludeLoggingToNamespace(window);
</script>
<script src="js/codeIWantToTraceHere.js"></script>
<script>
    functionLogger.addLoggingToNamespace(window);
</script>

这是我添加到@Briguy37 解决方案中的代码:

var excludedFunctions = {};

        functionLogger.excludeLoggingToNamespace = function(namespaceObject){
            for(var name in namespaceObject){
                var potentialFunction = namespaceObject[name];

                if(Object.prototype.toString.call(potentialFunction) === '[object Function]') {
                    excludedFunctions[name] = name;
                }
            }
        }; 

而且我不得不修改@Briguy37 的 addLoggingToNamespace 方法来考虑 excludeFunctions 哈希:

functionLogger.addLoggingToNamespace = function(namespaceObject){
    for(var name in namespaceObject){
        var potentialFunction = namespaceObject[name];

        if(Object.prototype.toString.call(potentialFunction) === '[object Function]' && 
           !excludedFunctions[name]) {
            namespaceObject[name] = functionLogger.getLoggableFunction(potentialFunction, name);
        }
    }
};    
于 2017-02-01T16:05:14.983 回答
0

您可以在putout 代码转换器的帮助下跟踪函数调用。Plugin看起来是这样的:

const {template, types, operator} = require('putout');
const {replaceWith} = operator;
const {BlockStatement} = types;

// create nodes
const buildLog = template(`console.log('TYPE' + ' ' + 'NAME')`);
const buildLogEnter = template(`console.log('enter' + ' ' + 'NAME' + '(' + JSON.stringify(Array.from(arguments)) + ')')`);
const buildLogException = template(`console.log('TYPE' + ' ' + 'NAME' + ': ' + trace$error.message); throw trace$error`);
const buildTryCatch = template(`try {
        BLOCK;
    } catch(trace$error) {
        CATCH;
    } finally {
        FINALLY;
    }
`);

const JSON = 'JSON';

// nodes we are searching for
module.exports.include = () => [
    'Function',
];

module.exports.fix = (path) => {
    const name = getName(path);
    
    // create 3 types of events
    const enterLog = buildLogEnter({
        NAME: name,
        JSON,
    });
    const exitLog = buildLogEvent(name, 'exit');
    const errorLog = buildLogExceptionEvent(name);
    
    // move function body into try-catch
    const bodyPath = path.get('body');
    replaceWith(bodyPath, BlockStatement([buildTryCatch({
        BLOCK: path.node.body.body,
        CATCH: errorLog,
        FINALLY: exitLog,
    })]));
    
    // add into the beginning of function "console.log" with "enter" event
    bodyPath.node.body.unshift(enterLog);
};


// get name of a function
function getName(path) {
    if (path.isClassMethod())
        return path.node.key.name;
    
    if (path.isFunctionDeclaration())
        return path.node.id.name;
    
    const {line} = path.node.loc.start;
    return `<anonymous:${line}>`;
}

// build logger
function buildLogEvent(name, type) {    
    return buildLog({
        NAME: name,
        TYPE: type,
    });
}

// build logger that throws
function buildLogExceptionEvent(name) {    
    return buildLogException({
        NAME: name,
        TYPE: 'error',
    });
}

假设这是您要跟踪的代码:

const processFile = (a) => a;
process([]);

function process(runners) {
    const files = getFiles(runners);
    const linted = lintFiles(files);
    
    return linted;
}

function getFiles(runners) {
    const files = [];
    
    for (const run of runners) {
        files.push(...run());
    }
    
    return files;
}

function lintFiles(files) {
    const linted = [];
    
    for (const file of files) {
        linted.push(processFile(file));
    }
   
    return linted;
}

这是一张完整的图片:

在此处输入图像描述

如果您将处理后的源代码另存为trace.js并使用节点运行它,您将拥有:

> node trace.js
enter process([[]])
enter getFiles([[]])
exit getFiles
enter lintFiles([[]])
exit lintFiles
exit process

存在与跟踪功能相关的问题

于 2021-06-14T19:26:52.523 回答