Http 慢日志只能展示出根據開發者設置的 apdex 值計算得出的不能容忍的請求的一些基本信息,包括 host、url、status code、耗時以及發生的時間等信息。
但是絕大部分時候,這些信息不能幫助開發者定位到實際的請求耗時操作,所以提供了這樣的一個慢鏈路追蹤模塊,旨在幫助開發者能以更清晰的方式解決慢 HTTP 請求問題。
此功能已經正式上線,歡迎大家使用并提出反饋意見。
用法
I. 升級 @alicloud/agenthub
首先執行如下命令重新安裝 @alicloud/agenthub 模塊來升級最新的 agentx 依賴:
npm install @alicloud/agenthub
需要確認下上一步全局安裝的 agenthub 依賴的 agentx 版本 >=1.10.1,才能正常使用 tracing 功能。
安裝完成后,按照原來的方式重啟下 agenthub 即可生效。
II. 使用 @alicloud/opentracing 埋點
目前需要開發者使用 @alicloud/opentracing 這個模塊在整個 HTTP 請求的耗時操作(一般是異步調用)前后進行手動埋點來串聯起整個調用鏈路,下面是此模塊的用法。
1. 安裝
npm install @alicloud/opentracing
2. 使用
公共類:Tracer(name[, option][, reporter]):
a. 參數:
name String - tracer 的名稱
option Object - 可選
limit Number - 每分鐘限制記錄落盤的數據條數限制,防止大量異常的情況下大量日志寫入文件造成磁盤溢出
logger Object - 日志句柄,最小需要實現 info、log、warn 和 error 方法,默認采用 console
reporter Object - 自定義發送方法,需要實現 report 方法,入參為 span
b. 成員方法:startSpan(spanName[, option])
spanName String - span 的名稱,用來標記此 span 下的異步調用
option Object - 可選
childOf Object - 傳入當前 span 的父級 span 實例
返回值 Object - 返回內置的類 Span 的實例
內置類:Span
a. 成員方法:setTag(tag, value)
tag String - Tag 名稱,可以自定義,一般從 opentracing.Tags 中獲取(里面定義了常見的 host、url、statusCode 等鏈路信息 Key)
value String - Tag 名稱對應的值
b. 成員方法:log(key, value)
key String - 自定義的日志鍵
value String - 自定義的日志值
c. 成員方法:finish(req)
req Object - http 請求的 request 對象
完整例子
下面是一個給 express 應用中間件埋點的使用完整樣例,模擬了并發的異步耗時調用,和順序的異步耗時調用:
注意: 有效的 root span(即能在控制臺正常看到的慢鏈路)中的 tags 信息必須包含 HTTP_METHOD、HTTP_URL 和 HTTP_STATUS_CODE,否則會被服務端過濾掉!
'use strict';
const express = require('express');
const app = express();
const opentracing = require('@alicloud/opentracing');
const tracer = new opentracing.Tracer('測試鏈路');
// 模擬耗時的異步操作
function delay(time, req) {
let child = tracer.startSpan('子模塊 1: 隨機并發延遲', { childOf: req.parentSpan });
child.setTag('timeout', time);
child.log({ state: 'timer1' });
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
child.finish(req);
}, time);
});
}
// 在所有中間件之前,開啟一個根 span,記錄下 hostname、method、url,以及收到 end 事件后的
app.use(function (req, res, next) {
req.parentSpan = tracer.startSpan('根模塊');
req.parentSpan.setTag(opentracing.Tags.PEER_HOSTNAME, req.hostname);
req.parentSpan.setTag(opentracing.Tags.HTTP_METHOD, req.method.toUpperCase());
req.parentSpan.setTag(opentracing.Tags.HTTP_URL, req.url);
next();
res.once('finish', () => {
req.parentSpan.setTag(opentracing.Tags.HTTP_STATUS_CODE, res.statusCode);
req.parentSpan.finish(req);
});
});
// 模擬并發的耗時異步操作
app.use(function (req, res, next) {
Promise.all([
delay(Math.random() * 10 * 1000, req),
delay(Math.random() * 10 * 1000, req)
]).then(() => next());
});
// 繼續模擬一個順序的 3s 耗時異步操作
app.use(function (req, res, next) {
let child = tracer.startSpan('子模塊 2: 延遲 3s', { childOf: req.parentSpan });
child.setTag('timeout', '3s');
child.log({ state: 'timer2' });
// 3s call
setTimeout(() => {
child.finish(req);
next()
}, 3000);
});
// 響應頁面
app.get('*', function (req, res) {
res.send('Hello Node.js Performance Platform!');
});
app.listen(3000);
在瀏覽器請求 http://localhost:3000/delay
, 等待約 1min 后,可以在控制臺的相應 Tab 頁看到:
點擊 請求信息 欄下面對應的字符串或者長條可以看到開發者自行記錄的當前請求詳情,比如例子中在隨機延遲中分別用 tag 和 log 記錄了當前延遲 ms 數和定時器名稱,那么點開 子模塊1 對應的請求信息欄中的長條后可以看到如下內容: