如何在运行时获取Javascript函数调用/跟踪

时间:2012-08-07 20:04:16

标签: javascript function google-chrome runtime trace

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

例如,假设我在页面上按了一个按钮。我想让它返回它的所有功能 经历了这件事:

所以我会在控制台中看到(当我按下按钮时):

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

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

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

p.s和NO我不想浏览每个函数并添加一个"console.log("inside function X")" b / c太多的工作

p.p.s作为一个额外的奖励,我希望看到传递给函数的参数,但也许是推动它。产品:>

9 个答案:

答案 0 :(得分:33)

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

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

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

然后,对于要添加日志记录的任何namespaceObject,只需调用:

functionLogger.addLoggingToNamespace(yourNamespaceObject);

Here's a fiddle看到它的实际效果。

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

答案 1 :(得分:15)

这称为分析,Chrome和Firebug内置它。在Chrome developer Tools中,转到配置文件选项卡,然后单击记录(圆圈)按钮。执行ajax并在响应后,再次单击“录制”按钮停止。分析结果将显示在右侧窗格中。

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

答案 2 :(得分:2)

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

Adding console.log to every function automatically

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

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

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

http://pastie.org/1033665

答案 3 :(得分:2)

尝试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的控制台选项卡中查看日志

答案 4 :(得分:2)

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

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

答案 5 :(得分: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方法以获取excludedFunctions哈希:

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

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

  1. 在运行时手动修补JS函数,并将其记录到控制台
    • 是的,它可以完成工作,但是一旦您的项目增长到一定的规模,它就无用了。除非您花时间继续开发此功能,否则它不会提供足够的可控制性。
  2. Jeff建议使用探查器进行调试
    • 不是很有帮助,因为探查器视图(至少到目前为止)旨在帮助您分析性能,而不是调用图。除非您花费大量时间训练自己习惯于适得其反的用户界面,否则它将无法正常工作。

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

链接:

答案 8 :(得分:0)

您可以在 putout code transformer 的帮助下跟踪函数调用。 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;
}

这是一张完整的图片:

enter image description here

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

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

putout issue related to tracing functions