我正在尝试使用 opentelemetry 在 NodeJs (v18.16.1) 中执行手动检测。该场景非常简单,我正在对虚拟 JSON API 进行 axios HTTP 调用,并且我想收集生成的跟踪。
依赖关系:
"@opentelemetry/api": "^1.3.0",
"@opentelemetry/auto-instrumentations-node": "^0.38.0",
"@opentelemetry/context-async-hooks": "^1.15.0",
"@opentelemetry/instrumentation": "^0.41.0",
"@opentelemetry/instrumentation-http": "^0.35.1",
"@opentelemetry/resources": "^1.9.1",
"@opentelemetry/sdk-metrics": "^1.9.1",
"@opentelemetry/sdk-node": "^0.35.1",
"@opentelemetry/sdk-trace-base": "^1.9.1",
"@opentelemetry/sdk-trace-node": "^1.9.1",
"@opentelemetry/semantic-conventions": "^1.9.1",
"axios": "^1.4.0"
设置如下:
otel/tracing.js
文件:
const opentelemetry = require('@opentelemetry/api')
const { Resource } = require("@opentelemetry/resources");
const { SemanticResourceAttributes } = require("@opentelemetry/semantic-conventions");
const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
const { registerInstrumentations } = require("@opentelemetry/instrumentation");
const { ConsoleSpanExporter, SimpleSpanProcessor} = require("@opentelemetry/sdk-trace-base");
const { AsyncHooksContextManager } = require('@opentelemetry/context-async-hooks');
const {
HttpInstrumentation,
} = require("@opentelemetry/instrumentation-http");
const contextManager = new AsyncHooksContextManager();
contextManager.enable()
opentelemetry.context.setGlobalContextManager(contextManager)
registerInstrumentations({
instrumentations: [new HttpInstrumentation()],
});
const resource =
Resource.default().merge(
new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'poc-otel-tracing',
[SemanticResourceAttributes.SERVICE_VERSION]: "0.1.0",
})
);
const provider = new NodeTracerProvider({
resource: resource
});
const exporter = new ConsoleSpanExporter();
const processor = new SimpleSpanProcessor(exporter);
provider.addSpanProcessor(processor);
provider.register();
// process.on('exit', function() {
// void exporter.shutdown()
// });
src/index.ts
文件
import axios from "axios";
import opentelemetry, {Span} from "@opentelemetry/api";
const tracer = opentelemetry.trace.getTracer('poc-otel-tracing');
const API_BASE_URL = 'https://jsonplaceholder.typicode.com/posts';
const func = async() => {
const rootSpan = tracer.startSpan('root-span')
await apiCall(rootSpan)
rootSpan.end()
}
const apiCall = async (parentSpan: Span) => {
const ctx = opentelemetry.trace.setSpan(opentelemetry.context.active(), parentSpan)
const childSpan = tracer.startSpan('api-call', undefined, ctx)
try {
await axios.get(API_BASE_URL);
} catch (e) {
childSpan.recordException(e);
}
childSpan.end()
}
void func()
请注意,我使用的是 typescript,并且我使用
npm run build && node --require "./otel/tracing.js" dist/index.js
来运行已编译的 JS 并需要 otel/tracing.js 脚本。
控制台输出如下:
{
traceId: '15b261f7332feb1fdc79cff5a280529b',
parentId: undefined,
traceState: undefined,
name: 'HTTPS GET',
id: '477643c3a9536749',
kind: 2,
timestamp: 1690206903281000,
duration: 128485,
attributes: {
'http.url': 'https://jsonplaceholder.typicode.com/posts',
'http.method': 'GET',
'http.target': '/posts',
'net.peer.name': 'jsonplaceholder.typicode.com',
'http.host': 'jsonplaceholder.typicode.com:443',
'net.peer.ip': '172.64.162.6',
'net.peer.port': 443,
'http.status_code': 200,
'http.status_text': 'OK',
'http.flavor': '1.1',
'net.transport': 'ip_tcp'
},
status: { code: 0 },
events: [],
links: []
}
{
traceId: 'ba35dbc454728c2bbc5b30a435dec3bb',
parentId: 'a3897743fb82276c',
traceState: undefined,
name: 'api-call',
id: 'ec3d00cb4ad95d26',
kind: 0,
timestamp: 1690206903278000,
duration: 136158,
attributes: {},
status: { code: 0 },
events: [],
links: []
}
{
traceId: 'ba35dbc454728c2bbc5b30a435dec3bb',
parentId: undefined,
traceState: undefined,
name: 'root-span',
id: 'a3897743fb82276c',
kind: 0,
timestamp: 1690206903278000,
duration: 136633,
attributes: {},
status: { code: 0 },
events: [],
links: []
}
我不明白为什么为 axios http 调用创建的跨度与“api-call”跨度不相关(即 axios 生成的跨度应该以“api-call”作为父跨度)并且应该是相同的痕迹。
这是我期望的输出:
{
traceId: 'ba35dbc454728c2bbc5b30a435dec3bb',
parentId: 'a3897743fb82276c',
traceState: undefined,
name: 'HTTPS GET',
id: '477643c3a9536749',
kind: 2,
timestamp: 1690206903281000,
duration: 128485,
attributes: {
'http.url': 'https://jsonplaceholder.typicode.com/posts',
'http.method': 'GET',
'http.target': '/posts',
'net.peer.name': 'jsonplaceholder.typicode.com',
'http.host': 'jsonplaceholder.typicode.com:443',
'net.peer.ip': '172.64.162.6',
'net.peer.port': 443,
'http.status_code': 200,
'http.status_text': 'OK',
'http.flavor': '1.1',
'net.transport': 'ip_tcp'
},
status: { code: 0 },
events: [],
links: []
}
{
traceId: 'ba35dbc454728c2bbc5b30a435dec3bb',
parentId: 'a3897743fb82276c',
traceState: undefined,
name: 'api-call',
id: 'ec3d00cb4ad95d26',
kind: 0,
timestamp: 1690206903278000,
duration: 136158,
attributes: {},
status: { code: 0 },
events: [],
links: []
}
{
traceId: 'ba35dbc454728c2bbc5b30a435dec3bb',
parentId: undefined,
traceState: undefined,
name: 'root-span',
id: 'a3897743fb82276c',
kind: 0,
timestamp: 1690206903278000,
duration: 136633,
attributes: {},
status: { code: 0 },
events: [],
links: []
}
有人可以帮助我理解我做错了什么吗? 预先感谢!
原因:跟踪信息未传播到请求(在您的情况下通过 axios)
修复: 从上下文中提取跟踪信息,并将其注入请求标头
const traceHeaders = {};
// inject context to trace headers for propagtion to the next service
propagation.inject(context.active(), traceHeaders);
// pass the trace headers to your request
const config = {
headers: traceHeaders
};
await axios.get(API_BASE_URL, config);
希望它对你有用。 如果您需要,我在here记录了详细的设置,并提供了完整源代码的链接