Skip to content

Commit

Permalink
[front]Added support for displaying trace-profiling data by querying …
Browse files Browse the repository at this point in the history
…from Elasticsearch (#528)

Signed-off-by: yiqianxu <[email protected]>
  • Loading branch information
thousandxu authored Jun 9, 2023
1 parent 38e908b commit f681111
Show file tree
Hide file tree
Showing 8 changed files with 312 additions and 28 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

## Unreleased
### New features
- Added support for displaying trace-profiling data by querying from Elasticsearch. ([#528](https://github.com/KindlingProject/kindling/pull/528))
- Display scheduler run queue latency on Trace-Profiling chart. To learn more about the concept of 'Run Queue Latency', refer to [this blog post](https://www.brendangregg.com/blog/2016-10-08/linux-bcc-runqlat.html). You can also find a use case for this feature in [this blog post](http://kindling.harmonycloud.cn/blogs/use-cases/optimize-cpu/). ([#494](https://github.com/KindlingProject/kindling/pull/494))
### Enhancements
- MySQL CommandLine Case: Ignore quit command and get sql with CLIENT_QUERY_ATTRIBUTES([#523](https://github.com/KindlingProject/kindling/pull/523))
Expand Down
22 changes: 1 addition & 21 deletions camera-front/node/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,6 @@ const { createProxyMiddleware } = require('http-proxy-middleware');
const settings = require('./settings');
const port = settings.port;

const restream = function (proxyReq, req) {
let masterIp = req.headers.authorization;
proxyReq.setHeader('Authorization', masterIp || '');
};
const esserverAdress = 'http://' + settings.apmServerConfig.host + ':' + settings.apmServerConfig.port;
const esserverProxy = createProxyMiddleware('/camera', {
target: esserverAdress,
secure: false,
changeOrigin: true,
// onProxyReq: restream,
// onProxyRes: async function (proxyRes, req, res) {
// // console.log(proxyRes, 'proxyRes');
// if (typeof req.session.username === 'undefined') {
// req.session.destroy();
// res.status(401).json({
// msg: noAuthorizeMsg,
// });
// }
// },
});
app.use(esserverProxy);
const profileAddress = 'http://' + settings.profileConfig.host + ':' + settings.profileConfig.port;
const profileProxy = createProxyMiddleware('/profile', {
target: profileAddress,
Expand Down Expand Up @@ -73,6 +52,7 @@ app.get("/test", function(req, res, next) {
});

app.use('/file', require('./routers/file.js'));
app.use('/esserver', require('./routers/esServer.js'));

var httpServer = http.createServer(app);
// var httpsServer = https.createServer(credentials, app);
Expand Down
1 change: 1 addition & 0 deletions camera-front/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"author": "thousand",
"license": "MIT",
"dependencies": {
"@elastic/elasticsearch": "^6.8.3",
"async": "^3.2.4",
"compression": "^1.7.4",
"connect-history-api-fallback": "^2.0.0",
Expand Down
235 changes: 235 additions & 0 deletions camera-front/node/routers/esServer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@

'use strict';
const router = require('express').Router();
const _ = require('lodash');
const fs = require('fs');
const esClientService = require('./esquery');
const esService = new esClientService();
const { Client } = require('@elastic/elasticsearch');

const esServerConfig = require('../settings').esServerConfig;
const traceIndex = esServerConfig.trace_index;
const onoffCpuIndex = esServerConfig.onoffcpu_index;
const esClient = new Client({ node: `http://${esServerConfig.host}:${esServerConfig.port}` });

router.get('/getTraceList', (req, res, next) => {
let { pid, startTimestamp, endTimestamp, protocol, isServer } = req.query;
console.log('/esserver/getTraceList:', pid, startTimestamp, endTimestamp, protocol, isServer);
let query = [
{
term: {'labels.is_server': 'true'}
}
];
if (protocol && protocol !== '') {
query.push({
match: {'labels.protocol': protocol}
});
}
if (pid && pid !== 0) {
query.push({
match: {'labels.pid': pid}
});
}
startTimestamp = startTimestamp * 1000000;
endTimestamp = endTimestamp * 1000000;
query.push({
range: {'timestamp': {
'gte': startTimestamp,
'lte': endTimestamp
}}
});
esClient.search({
index: traceIndex,
body: {
query: {
bool: {
must: query
}
}
},
}, {
headers: {
'content-type': 'application/json'
}
}, (err, result) => {
if (err) {
console.log(err);
res.status(504).json({
success: false,
data: err
});
} else {
let hits = result.body.hits.hits;
let fresult = _.map(hits, '_source');

res.status(200).json(fresult);
}
});
});

router.get('/onoffcpu', (req, res) => {
const { pid, startTimestamp, endTimestamp } = req.query;
esClient.search({
index: onoffCpuIndex,
body: {
query: {
bool: {
must: [
{
match: {'labels.pid': pid}
}, {
range: {
'labels.startTime': {
'lte': endTimestamp * 1000000
}
}
}, {
range: {
'labels.endTime': {
'gte': startTimestamp * 1000000
}
}
}

]
}
},
size: 10000,
sort: [
{
'labels.tid': {"order" : "desc"}
}, {
'labels.startTime': {"order" : "asc"}
}
]
}
}, {
headers: {
'content-type': 'application/json'
}
}, (err, result) => {
if (err) {
console.log(err);
res.status(504).json({
success: false,
data: err
});
} else {
let hits = result.body.hits.hits;
let fresult = _.map(hits, '_source');

res.status(200).json(fresult);
}
});
})


function handleData(data) {
let nodes = [], edges = [];
_.forEach(data, item => {
let metric = _.find(item.metrics, { Name: 'request_total_time' });
let timestamp = parseInt(item.timestamp / 1000000);
if (_.findIndex(nodes, {apm_span_ids: item.labels.apm_span_ids}) === -1) {
let node = {
content_key: item.labels.content_key,
id: item.labels.apm_span_ids,
apm_span_ids: item.labels.apm_span_ids,
dst_container: item.labels.dst_container,
dst_pod: item.labels.dst_pod,
dst_workload_name: item.labels.dst_workload_name,
pid: item.labels.pid,
protocol: item.labels.protocol,
list: [
{
endTime: timestamp,
totalTime: metric ? metric.Data.Value : 0,
p90: item.labels.p90,
is_profiled: item.labels.is_profiled,
}
]
}
nodes.push(node);
} else {
let node = _.find(nodes, {apm_span_ids: item.labels.apm_span_ids});
if (_.findIndex(node.list, {endTime: timestamp}) === -1) {
node.list.push({
endTime: timestamp,
totalTime: metric ? metric.Data.Value : 0,
p90: item.labels.p90,
is_profiled: item.labels.is_profiled,
});
}
}
});
_.forEach(data, item => {
if (item.labels.apm_parent_id !== '0') {
let sourceNode = _.find(nodes, node => node.apm_span_ids.split(',').indexOf(item.labels.apm_parent_id) > -1);
if (sourceNode) {
let sourceId = sourceNode.apm_span_ids;
if (_.findIndex(edges, {source: sourceId, target: item.labels.apm_span_ids}) === -1) {
let edge = {
source: sourceId,
target: item.labels.apm_span_ids
}
edges.push(edge);
}
}
}
});
return { nodes, edges };
}

router.get('/getTraceData', async(req, res, next) => {
const { traceId } = req.query;
console.log(traceId);
// try {
// let result = await esService.getEsData('span_trace_group_dev', 'labels.trace_id', traceId, 1, 100);
// console.log('result', result);
// let hits = result.body.hits.hits;
// console.log(JSON.stringify(result.body.hits));
// let data = _.map(hits, '_source');
// let finalResult = handleData(data);

// res.status(200).json({
// success: true,
// data: finalResult
// });
// } catch (error) {
// console.log('我报错了 哈哈哈哈哈哈');
// console.log('error', error)
// }

esClient.search({
index: traceIndex,
// 如果您使用的是Elasticsearch≤6,取消对这一行的注释
// type: '_doc',
body: {
query: {
match: { 'labels.trace_id.keyword': traceId }
}
},
}, {
headers: {
'content-type': 'application/json'
}
}, (err, result) => {
if (err) {
console.log(err);
res.status(504).json({
success: false,
data: err
});
} else {
let hits = result.body.hits.hits;
let data = _.map(hits, '_source');
let finalResult = handleData(data);

res.status(200).json({
success: true,
data: finalResult
});
}
});
});

module.exports = router;
57 changes: 57 additions & 0 deletions camera-front/node/routers/esquery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@

'use strict';
const { Client } = require('@elastic/elasticsearch')


const esServerConfig = require('../settings').esServerConfig;
const esClient = new Client({ node: `http://${esServerConfig.host}:${esServerConfig.port}` });


function esClientService() {
/**
* 获取ES查询出来的数据,这里使用的是对name进行类似于sql中的like "%aaa%" 的查询方式,其他的查询方式,后面再补上
* @param index 索引
* @param keyword 关键词
* @param value
* @param pagenum 页码
* @param size 单页大小
*/
this.getEsData = async (index, keyword, value, pagenum, size) => {
// 获取数据
const result = await esClient.search({
index,
body: {
track_total_hits: true,
query: {
bool: {
must: [{
"query_string": {
"default_field": keyword,
"query": value
}
}],
must_not: [],
should: []
}
},
search_after: [(pagenum - 1) * size],
size: size,
sort: [
{"_id": "asc"}
]
}
}, {
headers: {
'content-type': 'application/json'
}
});
result.size = size;
result.pages = Math.ceil(result.body.hits.total.value / size);
return Promise.resolve(result);
// return result;
}


}

module.exports = esClientService;
6 changes: 4 additions & 2 deletions camera-front/node/settings.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const setting = {
apmServerConfig: {
esServerConfig: {
host: 'localhost',
port: '2234',
port: '9200',
trace_index: 'single_net_request_metric_group_dev',
onoffcpu_index: 'camera_event_group_dev'
},
profileConfig: {
host: 'localhost',
Expand Down
12 changes: 9 additions & 3 deletions camera-front/src/request/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,23 @@ interface IParams1 {
isServer: boolean;
podName?: string;
}
export const getTraceList = (params: IParams1) => {
return axios.get(basicUrl + '/camera/trace', {params});
// export const getTraceList = (params: IParams1) => {
// return axios.get(basicUrl + '/camera/trace', {params});
// }
export const getTraceList = (params) => {
return axios.get(basicUrl + '/esserver/getTraceList', {params});
}

interface IParams2 {
pid: number;
startTimestamp: number;
endTimestamp: number;
}
// export const getTraceData = (params: IParams2) => {
// return axios.get(basicUrl + '/camera/onoffcpu', {params});
// }
export const getTraceData = (params: IParams2) => {
return axios.get(basicUrl + '/camera/onoffcpu', {params});
return axios.get(basicUrl + '/esserver/onoffcpu', {params});
}

interface IParams3 {
Expand Down
Loading

0 comments on commit f681111

Please sign in to comment.