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

yzxexxkh  于 2023-01-19  发布在  Java
关注(0)|答案(9)|浏览(178)

当我在 RUNTIME 与基于 AJAX 的应用程序交互时,我希望控制台能显示出它调用的所有函数。(所以没有堆栈跟踪、断点、分析或任何东西)
举个例子,假设我按下了页面上的一个按钮,我希望它返回所有的函数:
所以我会在控制台中看到类似这样的内容(当我按下按钮时):

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

这基本上意味着button 1Clicked()调用了calculating(),而calculating()调用了printingResults()
有没有实用程序,插件,浏览器,或者语言中的某种方式可以做到这一点?顺便说一句,我用的是谷歌浏览器。
p.s和否我不想逐一查看每个函数并添加一个"console.log("inside function X")" B/c,这太麻烦了
p. p. s作为一个额外的好处,我希望看到参数也传递到函数中,但也许这是推动它。:〉

5jdjgkvh

5jdjgkvh1#

我想不出一个很好的方法来全局拦截所有函数调用以插入日志记录(尽管在下面的更新部分有一个不错的解决方案)。
相反,只给你关心的特定名称空间中的函数添加日志记录,你可以用下面的设置代码做到这一点:

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);,在调用时为所有全局函数添加日志记录。此外,如果您确实需要,您可以遍历树以找到任何函数并相应地更新它们。此方法的一个缺点是它只适用于当时存在的函数。因此,它仍然不是最好的解决方案。但这比手工添加日志记录语句要少得多:)

qyzbxkaa

qyzbxkaa2#

这就是所谓的profiling,Chrome和Firebug都内置了这个功能。在Chrome developer Tools中,进入profiles选项卡,点击record(圈)按钮。执行 AJAX ,在响应后,再次点击record按钮停止。profiling的结果将出现在右侧窗格中。
注意,这会给予你所有的东西,所以如果你使用像jQuery这样的库,绝大多数的函数调用对你来说都是垃圾,我已经试过几次了,我发现做console.log('inside <method>')的事情要有帮助得多。

dgtucam1

dgtucam13#

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

tjjdgumg

tjjdgumg4#

也许您可以让JavaScript为您完成一些添加console.log的工作:
Adding console.log to every function automatically
此外,这个博客由保罗爱尔兰可能会有所帮助:
http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
它包含一些专门针对日志参数的JavaScript的链接:
http://pastie.org/1033665

d7v8vwbk

d7v8vwbk5#

给予一下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的控制台选项卡中查看日志

kdfy810k

kdfy810k6#

作为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);
5n0oy7gb

5n0oy7gb7#

让我向你抛出第三个解决方案:* 无所不知的调试器 *。
请注意,所有其他答案都提供了两种解决方案:

  • 在运行时手动修补JS函数,并将其记录到控制台
  • 是的,它可以完成工作,但是一旦你的项目增长到一定的规模就没用了。它不能给你足够的可控制性,除非你继续花时间不断地开发这个特性。
  • Jeff建议使用分析器进行调试
  • 不是很有帮助,因为评测器视图(至少目前是这样)旨在帮助您分析性能,而不是调用图;除非你花很多时间训练自己习惯于这种适得其反的用户界面,否则效果并不好。

这就是为什么我编写了Dbux-一个VSCode扩展,它提供了一个全知的调试器,包括动态执行分析工具、代码注解和一个完整的动态调用图可视化工具,旨在帮助开发人员理解和调试程序。
一些例子-

    • fibonacci(6)的调用图:**

    • 在一个屏幕中显示代码和其它Dbux工具的调用图:**

4c8rllxm

4c8rllxm8#

您可以在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;
}

以下是一个完整的画面:

如果将已处理的源代码另存为trace.js并使用node运行它,则将具有:

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

putout issue related to tracing functions

wwtsj6pe

wwtsj6pe9#

我使用了@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);
        }
    }
};

相关问题