前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >T-Trace:GraalVM中类似代理的测仪

T-Trace:GraalVM中类似代理的测仪

作者头像
CNCF
发布2020-02-26 13:16:20
8720
发布2020-02-26 13:16:20
举报
文章被收录于专栏:CNCFCNCF

作者:Pavol Loffay,软件工程师 @红帽

在本文中,我们将研究类似代理的测仪(instrumentation)工具T-Trace。该工具为运行在GraalVM上的应用程序提供非侵入性的测仪功能。我们将使用T-Trace和带有Jaeger NodeJS tracer的OpenTracing API来测仪(instrument)一个简单的NodeJS应用程序。

首先,GraalVM是一个多语言的虚拟机。它可以运行任何基于JVM的语言,也可以运行其他主流语言,比如NodeJS、Python和Ruby……它还支持像C和C++这样的LLVM语言。最理想的情况是,这些语言可以组合在一个应用程序中。另一个非常有趣的特性是GraalVM可以为JVM语言生成原生可执行文件。这个特性被Quarkus和Micronaut等现代云原生Java运行时使用。

测仪的风格

在深入研究演示应用程序之前,我们先来看看不同的测仪技术。有几种测仪风格可以应用于代码片段或整个应用程序。在高层次上,我们可以讨论黑盒和白盒工具。黑盒技术不需要对应用程序进行任何修改。监视系统在这情况中仅使用应用程序生成的事件。这些事件可以是日志,也可以是应用程序生成的任何数据。

在本文中,我们将重点讨论白盒技术。这种技术假设应用程序的代码是已知的,并且可以检查。有几种方法可以将测仪点添加到应用程序中:

  • 非介入式代理(类似于代理)测仪:在这种情况下,测仪是动态地添加到应用程序中的。在某些语言(Python、Ruby、JavaScript)中,这种技术称为monkey-patching,在运行时动态地替换模块或类。在其他语言中,可以使用不太动态的技术,例如Java中的javaagent。
  • 显式测仪:该测仪直接应用于代码。但是,这并不意味着应用程序开发人员必须测仪所有代码。测仪代码可以直接嵌入(RPC)框架或外部集成。在一些语言和运行时框架中,像在其他语言中一样,初始化工具更容易。例如,在Java中,只需将一个测仪JAR放入类路径中就足够了,而在像Golang这样的静态链接语言中,初始化必须显式地完成。

应用程序

该应用程序是一个简单的NodeJS服务器,只有一个处理程序。完整的演示代码和说明可以在GitHub的pavolloffay/graalvm-t-trace中找到。下面是server.js文件:

代码语言:javascript
复制
function jaegerAvailable(jaeger) {
  console.log("Providing Jaeger object to the agent");
}
jaegerAvailable(require("jaeger-client"));

const port = 3000;

const http = require("http");
const srv = http.createServer((req, res) => {
  console.log(`server: obtained request ${res.id}`);
  setTimeout(() => {
    res.write(`OK# ${res.id}`);
    console.log(`server: replied to request ${res.id}`);
    res.end();
  }, 5);
});
srv.listen(port);

本例中唯一与跟踪相关的代码是加载npm模块jaeger-client。这是目前已知的T-Trace的局限性,因为代理脚本无法加载其他库。这个特性被添加到T-Trace中。然后应用程序在请求的开头和结尾打印请求id。代理脚本中设置了res.id。

现在让我们看一下测仪脚本jaegernode.js。有两个函数:initializeJaeger和initializeAgent。第一个函数使用HTTP发送器创建Jaeger跟踪器实例,并将其发送到收集器端口14268,最后调用第二个函数:

代码语言:javascript
复制
let initializeJaeger = function (ctx, frame) {
    agent.off('enter', initializeJaeger);

    let jaeger = frame.jaeger;

    var initTracer = jaeger.initTracer;
    console.log('agent: Jaeger tracer obtained');
    // See schema https://github.com/jaegertracing/jaeger-client-node/blob/master/src/configuration.js#L37
    var config = {
        serviceName: 't-trace-demo',
        reporter: {
            // Provide the traces endpoint; this forces the client to connect directly to the Collector and send
            // spans over HTTP
            collectorEndpoint: 'http://localhost:14268/api/traces',
            // logSpans: true
        },
        sampler: {
            type : 'const',
            param : 1
        }
    };
    var options = {
        tags: {
            't-trace-demo.version': '1.1.2',
        },
        //  metrics: metrics,
        logger: console,
        sampler: {
            type : 'const',
            param : 1
        }
    };

    var tracer = initTracer(config, options);
    initializeAgent(tracer);
};

agent.on('return', initializeJaeger, {
    roots: true,
    rootNameFilter: name => name === 'jaegerAvailable'
});

第二个函数是initializeAgent,它测仪应用程序代码:

代码语言:javascript
复制
let initializeAgent = function(tracer) {
    agent.on('enter', function(ctx, frame) {
        const args = frame.args;
        if ('request' !== frame.type || args.length !== 2 || typeof args[0] !== 'object' || typeof args[1] !== 'object') {
            return;
        }
        const req = args[0];
        const res = args[1];
        const span = tracer.startSpan(req.method);
        span.setTag("span.kind", "server");
        span.setTag("http.url", req.url);
        span.setTag("http.method", req.method);
        res.id = span.context().spanIdStr;
        res.span = span;
        console.log(`agent: handling #${res.id} request for ${req.url}`);
    }, {
        roots: true,
        rootNameFilter: name => name === 'emit',
        sourceFilter: src => src.name === 'events.js'
    });

    agent.on('return', function(ctx, frame) {
        var res = frame['this'];
        if (res.span) {
            res.span.setTag("http.status_code", res.statusCode);
            if (res.statusCode >= 400) {
                res.span.setTag("error", "true");
            }
            res.span.finish();
            console.log(`agent: finished #${res.id} request`);
        } else {
            // OK, caused for example by Tracer itself connecting to Jaeger server
        }
    }, {
        roots: true,
        rootNameFilter: name => name === 'end',
        sourceFilter: src => src.name === '_http_outgoing.js'
    });
    console.log('agent: ready');
};

测仪是通过agent.on('enter', fn)和agent.on('return', fn)完成的。当调用应用程序中的任何函数时调用第一个测仪点,当函数返回调用者执行时调用第二个测仪点。agent.on函数可以访问frame变量和方法参数。参数用于检查函数是否为HTTP处理程序。你还可以注意到,span对象被注入到响应中。

现在让我们运行应用程序和Jaeger服务器:

代码语言:javascript
复制
docker run --rm -it --net=host jaegertracing/all-in-one:1.16.0
$GRAALVM_HOME/bin/npm install jaeger-client@3.17.2
$GRAALVM_HOME/bin/node --experimental-options --js.print=true  --agentscript=jaegernode.js server.js
curl http://localhost:3000

从Jaeger的屏幕截图显示t-trace演示应用程序的跟踪。

总结

我们看到了一个带有Jaeger和OpenTracing的NodeJS hello-world的T-Trace示例。这演示了如何将类似代理的测仪应用到NodeJS应用程序中,而不需要monkey-patching。

可以对代码进行许多改进。例如,我们可以扩展它,不测仪代理脚本中的代码,而是重用NodeJS的OpenTracing测仪,并将其安装到代理脚本中。我们可以做的另一个改进是支持脚本的动态加载。在这种情况下,应用程序将使用代理脚本启动,该脚本暴露用于加载和禁用脚本的REST API。这是一个非常强大的特性,可以动态地更改测仪的粒度,而无需重新编译和重新部署应用程序。

T-Trace还能够将工具语言与主语言混合使用。例如,可以用JavaScript编写代理脚本来跟踪Ruby或C++应用程序。

参考文献

  • 演示代码: https://github.com/pavolloffay/graalvm-t-trace
  • T-Trace: https://github.com/oracle/graal/blob/master/tools/docs/T-Trace.md
  • T-Trace,通晓多种语言的跟踪: https://github.com/oracle/graal/blob/master/tools/docs/T-Trace-Manual.md#trully-polyglot---t-trace-any-language
  • T-Trace嵌入式跟踪: https://github.com/oracle/graal/blob/master/tools/docs/T-Trace-Embedding.md
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CNCF 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档