import debug from 'debug';
import React from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Map as makeMap } from 'immutable';
import { noop } from 'lodash';
import { clickCloseDetails, clickShowTopologyForNode } from '../actions/request-actions';
import { brightenColor, getNeutralColor, getNodeColorDark,getStatusColor,setNodeColor } from '../utils/color-utils';
import { isGenericTable, isPropertyList } from '../utils/node-details-utils';
import { resetDocumentTitle, setDocumentTitle } from '../utils/title-utils';
import Overlay from './overlay';
import MatchedText from './matched-text';
import NodeDetailsControls from './node-details/node-details-controls';
import NodeDetailsGenericTable from './node-details/node-details-generic-table';
import NodeDetailsPropertyList from './node-details/node-details-property-list';
import NodeDetailsHealth from './node-details/node-details-health';
import f from './node-details/node-details-info';
import NodeDetailsRelatives from './node-details/node-details-relatives';
import NodeDetailsTable from './node-details/node-details-table';
import Warning from './warning';
import * as echarts from 'echarts'
import axios from 'axios'
import moment from 'moment'
import { Table,Radio,Checkbox,Button, Drawer, Descriptions } from "antd";
import "antd/dist/antd.css";
import '../../styles/nodeDetail.less'
import getToken from '../utils/get-token'
const log = debug('scope:node-details');
function getTruncationText(count) {
return 'This section was too long to be handled efficiently and has been truncated'
+ ` (${count} extra entries not included). We are working to remove this limitation.`;
}
class NodeDetails extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
// baseUrl:'http://observe-server.cestong.com.cn', //本地调试使用
// traceUrl:'http://observe-front.cestong.com.cn', //本地调试使用
// coreBaseUrl: 'http://observe-front.cestong.com.cn/core', //本地调试使用
coreBaseUrl:'',//上线时打开
baseUrl:'/re', //上线时打开
traceUrl:'',//上线时打开
nodeData:{}, //节点基础信息
AnalystData:{},//散点图
queryParams:{
start_time:Math.round((new Date().getTime())/1000 - (5*60)),
end_time:Math.round(new Date().getTime()/1000),
app_alias:'opentelemetry-demo',
service_name:'frontend',
percentile:0.5,
sort_field:'Timestamp',
sort_type: 'DESC'
},
traceData:[],
livenessData:[],
barData:[],
tableData: [],
bgColor:setNodeColor('R'),
pagination: {
pageIndex:1,
pageSize:10,
total:0,
current:1,
},
pagination2: {
page_num:1,
page_size:10,
total:0,
current:1,
},
traceQuery:{
only_exception:0,
only_database:0,
},
timeoutId: null,
messaging_stats: {
topic_stats:[]
},
cloudTable: [],
cloudTable2: [],
cloudPagination: {
page_num:1,
page_size:10,
total:0,
current:1,
}
}
}
handleClickClose = (ev) => {
ev.preventDefault();
this.props.clickCloseDetails(this.props.nodeId);
}
handleShowTopologyForNode = (ev) => {
ev.preventDefault();
this.props.clickShowTopologyForNode(this.props.topologyId, this.props.nodeId);
}
componentDidMount() {
let _this = this
//上线时打开开始
this.setQueryParams();//做为iframe嵌套时打开,上线时打开
const traceURL = `http://${parent.location.hostname}`
const coreBaseURL = `http://${parent.location.hostname}/core`
this.setState({
traceUrl:traceURL,
coreBaseUrl:coreBaseURL
},()=>{
})
//上线时打开结束
// 接受父组件参数
// window.top == window true 自己本身没有被嵌套
if(window.top !== window){// false 被嵌套
const data = JSON.parse(parent.localStorage.global_times)
const queryParams = _this.state.queryParams
queryParams.start_time = data.startTime
queryParams.end_time = data.endTime
_this.setState({
queryParams: queryParams
},()=>{
});
}
window.addEventListener('message', function(event) {
// 处理接收到的消息
const data = event.data
const queryParams = _this.state.queryParams
if (data.eventType == 'globalTimesChange') {
queryParams.start_time = data.data.startTime
queryParams.end_time = data.data.endTime
_this.setState({
queryParams: queryParams
},()=>{
if (_this.props.shape == 'cylinder'){
_this.getTableData(); //table
_this.getBarData(); // 柱形图
}
if (_this.props.shape == 'dottedcylinder'){
_this.getDottBasicsData() // dottedcylinder 基础信息
}
if (_this.props.shape == 'circle'){
_this.getNodeBasic();
_this.getNodeAnalyst();//获取散点图
_this.getNodeLiveness(); // 折线图
_this.getServiceSpans();
}
if (_this.props.shape == 'cloud'){
_this.getCloudTableData()
}
});
} else if (data.eventType == 'getLoginAuth') {
_this.setState({
getToken: data.data
}, ()=>{})
}
}, false);
}
componentWillUnmount() {
clearTimeout(this.timeoutId);
}
setQueryParams(){
var strr = parent.location.href; //上线做为iframe嵌套时使用
let param = this.parseQueryString(strr); //全链路需要的参数多,因此解析成对象形式
const queryParams = this.state.queryParams;
if(parseInt(param.start_time)!=0 && parseInt(param.end_time) !=0){ //上线时打开
let newStartTime = parseInt(param.start_time); // 设置新的属性值
let newEndTime = parseInt(param.end_time); // 设置新的属性值
queryParams.start_time = newStartTime
queryParams.end_time = newEndTime
}
const newAppAlias = param.app_alias
queryParams.app_alias = newAppAlias
this.setState({
queryParams: queryParams
},()=>{
if (this.props.shape == 'cylinder'){
this.getTableData(); //table
this.getBarData(); // 柱形图
}
if (this.props.shape == 'dottedcylinder'){
this.getDottBasicsData() // dottedcylinder 基础信息
}
if (this.props.shape == 'circle'){
this.getNodeBasic();
this.getNodeAnalyst();//获取散点图
this.getNodeLiveness(); // 折线图
this.getServiceSpans();
}
if (this.props.shape == 'cloud') {
this.getCloudTableData()
}
});
}
//解析URL
parseQueryString(url){
var json = {};
var arr = url.substr(url.indexOf('?') + 1).split('&');
arr.forEach(item=>{
var tmp = item.split('=');
json[tmp[0]] = tmp[1];
});
return json;
}
componentWillUnmount() {
resetDocumentTitle();
}
renderTools() {
const showSwitchTopology = this.props.nodeId !== this.props.selectedNodeId;
const topologyTitle = `View ${this.props.label} in ${this.props.topologyId}`;
return (
{showSwitchTopology
&& (
Show in
{/* {this.props.topologyId.replace(/-/g, ' ')} */}
)
}
);
}
renderLoading() {
const node = this.props.nodes.get(this.props.nodeId);
const label = node ? node.get('label') : this.props.label;
// NOTE: If we start the fa-spin animation before the node details panel has been
// mounted, the spinner is displayed blurred the whole time in Chrome (possibly
// caused by a bug having to do with animating the details panel).
const spinnerClassName = classNames('fa fa-circle-notch', { 'fa-spin': this.props.mounted });
const nodeColor = (node
? getNodeColorDark(node.get('rank'), label, node.get('pseudo'))
: getNeutralColor());
const tools = this.renderTools();
const styles = {
header: {
backgroundColor: nodeColor
}
};
return (
);
}
renderNotAvailable() {
const tools = this.renderTools();
return (
{tools}
{this.props.label}
{' '}
not found!
);
}
render() {
// if (this.props.notFound) {
// return this.renderNotAvailable();
// }
// if (this.props.details) {
// return this.renderDetails();
// }
// return this.renderLoading();
return this.renderNodeDetails();
}
renderDetails() {
const {
details, nodeControlStatus, nodeMatches = makeMap(), topologyId
} = this.props;
const showControls = details.controls && details.controls.length > 0;
const nodeColor = getNodeColorDark(details.rank, details.label, details.pseudo);
// const nodeColor= setNodeColor(details.color)
const {error, pending} = nodeControlStatus ? nodeControlStatus.toJS() : {};
const tools = this.renderTools();
const styles = {
controls: {
backgroundColor: brightenColor(nodeColor)
},
header: {
backgroundColor: nodeColor
}
};
return (
{tools}
{showControls
&& (
)
}
{details.metrics
&& (
)
}
{details.metadata
&& (
)
}
{details.connections && details.connections.filter(cs => cs.connections.length > 0)
.map(connections => (
))}
{details.children && details.children.map(children => (
))}
{details.tables && details.tables.length > 0 && details.tables.map((table) => {
if (table.rows.length > 0) {
return (
{table.label && table.label.length > 0 && table.label}
{table.truncationCount > 0
&& (
)
}
{this.renderTable(table)}
);
}
return null;
})}
{this.props.renderNodeDetailsExtras({ details, topologyId })}
);
}
renderNodeDetails(){
const {
details, nodeControlStatus, nodeMatches = makeMap(), topologyId
} = this.props;
const node = this.props.nodes.get(this.props.nodeId);
const label = node ? node.get('label') : this.props.label;
// const nodeColor = getNodeColorDark(details.rank, details.label, details.pseudo);
// const nodeColor= setNodeColor(details.color)
const {error, pending} = nodeControlStatus ? nodeControlStatus.toJS() : {};
const tools = this.renderTools();
const styles = {
controls: {
// backgroundColor: brightenColor(nodeColor)
},
header: {
// backgroundColor: '#5BB2FA'
backgroundColor:this.state.bgColor
}
};
const columns = [
{
title: 'TraceID',
dataIndex: 'trace_id',
key: 'trace_id',
width:'20%',
ellipsis:true,
align:'center',
// scopedSlots:{customRender:'trace_id'},
render: (text,record) => {text}
},
{
title: '方法',
dataIndex: 'method',
key: 'method',
width:'15%',
ellipsis:true,
align:'center'
},
{
title: '状态码',
dataIndex: 'code',
key: 'code',
width:'15%',
ellipsis:true,
align:'center'
},
{
title: '请求时长(ms)',
dataIndex: 'duration',
key: 'duration',
width:'30%',
ellipsis:true,
align:'center',
render:(text) => {text.toFixed(2)}
},
{
title: '日期',
dataIndex: 'datetime',
key: 'datetime',
width:'30%',
ellipsis:true,
align:'center',
defaultSortOrder: 'descend',
sorter:true
},
]
const colums2 = [
{
title: '服务名',
dataIndex: 'service_name',
key: 'service_name',
ellipsis:true,
align:'center',
render: (text, record) => (
{record.service_name_cn? record.service_name_cn: record.service_name}
)
},
{
title: '执行语句',
dataIndex: 'query',
key: 'query',
width:'40%',
ellipsis:true,
align:'center',
render: (text, record) => (
)
},
{
title: '慢查询次数',
dataIndex: 'slow_num',
key: 'slow_num',
ellipsis:true,
align:'center'
},
{
title: '错误数量',
dataIndex: 'error_num',
key: 'error_num',
ellipsis:true,
align:'center'
}
]
const cloudColums = [
{
title: '应用名称',
dataIndex: 'app_name',
align:'center',
width: 200,
key: 'app_name'
},
{
title: '请求总数',
width: 100,
dataIndex: 'request_total',
align:'center',
key: 'request_total',
},
{
title: '错误数',
dataIndex: 'error_num',
align:'center',
width: 130,
key: 'error_num',
},
{
title: '平均延迟',
dataIndex: 'duration_average',
align:'center',
key: 'duration_average',
width: 130,
render: (text, record) => (
{record.duration_average.toFixed(2)}
)
},
{
title: '中位延迟',
dataIndex: 'duration_median',
align:'center',
width: 140,
key: 'duration_median',
render: (text, record) => (
{record.duration_median.toFixed(2)}
)
}
];
const cloudColums2 = [
{
title: '应用名称',
align:'center',
dataIndex: 'name',
width: 210,
key: 'name'
},
{
title: '请求总数',
align:'center',
width: 100,
dataIndex: 'request_total',
key: 'request_total',
},
{
title: '错误数',
align:'center',
width: 140,
dataIndex: 'error_num',
key: 'error_num',
},
{
title: '平均延迟',
dataIndex: 'duration_average',
align:'center',
width: 130,
key: 'duration_average',
render: (text, record) => (
{record.duration_average.toFixed(2)}
)
},
{
title: '中位延迟',
dataIndex: 'duration_median',
align:'center',
width: 140,
key: 'duration_median',
render: (text, record) => (
{record.duration_median.toFixed(2)}
)
}
];
const expandedRowRender = (record) => {
const columns = [
{
title: '服务名',
dataIndex: 'service_name_cn',
align:'center',
width: 180,
key: 'service_name_cn',
},
{
title: '请求总数',
dataIndex: 'request_total',
align:'center',
width: 114,
key: 'request_total',
},
{
title: '错误数',
dataIndex: 'error_num',
align:'center',
width: 120,
key: 'error_num',
},
{
title: '平均延迟',
dataIndex: 'duration_average',
key: 'duration_average',
align:'center',
width: 130,
render: (text, record) => (
{record.duration_average.toFixed(2)}
)
},
{
title: '中位延迟',
dataIndex: 'duration_median',
key: 'duration_median',
align:'center',
width: 130,
render: (text, record) => (
{record.duration_median.toFixed(2)}
)
}
];
return ;
};
return (
{tools}
{label}
{this.state.nodeData.subtitle}
{/* {showControls
&& (
)
} */}
{
this.props.shape == 'cylinder'&&
}
{ this.props.shape == 'circle'&&
(
{/* https://zhongdian.feishu.cn/docx/HkXSdrGa2ou84zxPvvycGX5Fn3b 中让去掉的 */}
{/*
状态
可用性
{this.state.nodeData.apdex?Math.floor(this.state.nodeData.apdex*100):0}
成功率
{this.state.nodeData.arc__success?(this.state.nodeData.arc__success*100).toFixed(2):0}%
失败率
{this.state.nodeData.arc__faild?(this.state.nodeData.arc__faild*100).toFixed(2):0}%
接收数量
{this.state.nodeData.receive?this.state.nodeData.receive:0}
发送数量
{this.state.nodeData.send?this.state.nodeData.send:0}
*/}
调用次数
{
this.state.livenessData.length>0?
(
)
:(
暂无数据
)
}
延迟比例
{
(JSON.stringify(this.state.AnalystData)!="{}" && this.state.AnalystData.failed || this.state.AnalystData.success )?
(
)
:(
暂无数据
)
}
50分位
{/* p.95 */}
99分位
)
}
{
this.props.shape == 'dottedcylinder' && (
{/* {this.state.messaging_stats.name} */}
{this.state.messaging_stats.produce_num || 0}
{this.state.messaging_stats.consume_num || 0}
{this.state.messaging_stats.produce_error_rate?(this.state.messaging_stats.produce_error_rate*100).toFixed(2):0}
{this.state.messaging_stats.consume_error_rate?(this.state.messaging_stats.consume_error_rate*100).toFixed(2):0}
{this.state.messaging_stats.produce_duration_average?this.state.messaging_stats.produce_duration_average.toFixed(2):0}
{this.state.messaging_stats.consume_duration_average?this.state.messaging_stats.consume_duration_average.toFixed(2):0}
{this.state.messaging_stats.message_size_average?this.state.messaging_stats.message_size_average.toFixed(2):0}
{this.state.messaging_stats.topic_num || 0}
主题统计信息
{this.state.messaging_stats && this.state.messaging_stats.topic_stats && this.state.messaging_stats.topic_stats.length > 0 && this.state.messaging_stats.topic_stats.map((item, index)=>{
return
{item.name || '--'}
{item.produce_num || 0}
{item.consume_num || 0}
{item.produce_error_rate ? (item.produce_error_rate*100).toFixed(2)+'%' :0}
{item.consume_error_rate?(item.consume_error_rate*!100).toFixed(2)+ '%' :0}
{item.produce_duration_average?item.produce_duration_average.toFixed(2):0}
{item.consume_duration_average?item.consume_duration_average.toFixed(2):0}
{item.message_size_average?item.message_size_average.toFixed(2):0}
})}
)
}
{
this.props.shape == 'cloud' && (
已知应用
expandedRowRender(record)}
size='small' bordered pagination={false}>
)
}
);
}
//获取基础信息
getNodeBasic(){
axios({
url: `${this.state.baseUrl}/api/v1/apps_score/${this.state.queryParams.app_alias}/svr`,
method: "get",
headers: { 'Authorization': getToken },
params: {
source_service:this.props.id,
start_time:this.state.queryParams.start_time,
end_time:this.state.queryParams.end_time
}
}).then(res => {
if(res && res.data.code == 200){
const newObj= ((res ||{}).data || {}).data || {}
this.setState({
nodeData:{...newObj}
},()=>{
if(JSON.stringify(this.state.nodeData)!="{}"){
this.state.nodeData.apdex>=0.94?this.setState({bgColor:setNodeColor('G')})
:(this.state.nodeData.apdex>=0.85&&this.state.nodeData.apdex<0.94)?this.setState({bgColor:setNodeColor('B')})
:(this.state.nodeData.apdex>=0.7&&this.state.nodeData.apdex<0.85)?this.setState({bgColor:setNodeColor('DI')})
:(this.state.nodeData.apdex>=0.5&&this.state.nodeData.apdex<0.7)?this.setState({bgColor:setNodeColor('Y')})
:this.setState({bgColor:setNodeColor('R')})
}else{
this.setState({bgColor:setNodeColor('R')})
}
})
}
});
}
//获取散点图--延迟比例
getNodeAnalyst(){
axios({
url: `${this.state.baseUrl}/api/v1/app/analyst/${this.state.queryParams.app_alias}/svr`,
method: "get",
headers: { 'Authorization': getToken },
params: {
source_service:this.props.id,
start_time:this.state.queryParams.start_time,
end_time:this.state.queryParams.end_time,
percentile:this.state.queryParams.percentile
}
}).then(res => {
if(res && res.data.code == 200){
const obj = ((res || {}).data ||{}).data || {}
this.setState({
AnalystData:{...obj}
},()=>{
if(JSON.stringify(this.state.AnalystData)!="{}"){
if (this.state.AnalystData?.failed || this.state.AnalystData?.success) {
this.initChart(this.state.AnalystData)
}
}
})
}
});
}
// 散点图渲染
initChart(tmpData) {
let chart = echarts.getInstanceByDom(document.getElementById("main"));
if (chart == null) {
chart = echarts.init(document.getElementById("main"));
}else {
chart.dispose();
chart = echarts.init(document.getElementById("main"));
}
let successData = tmpData.success && tmpData.success.map(item => new Date(item[0]).toLocaleString()) || []
let failedData = tmpData.failed && tmpData.failed.map(item => new Date(item[0]).toLocaleString()) || []
let option = {
title: {
text: '',
subtext: '',
textStyle:{
fontSize:14
},
},
grid: {
top:'8%',
left: '3%',
right: '7%',
bottom: '14%',
containLabel: true
},
tooltip: {
showDelay: 0,
formatter: function (params) {
let newParams = moment(params.data[0]).format('YYYY-MM-DD HH:mm:ss');
let time = newParams+"
"+ params.data[1]+"ms"
return time;
},
axisPointer: {
show: true,
type: 'cross',
lineStyle: {
type: 'dashed',
width: 1
}
}
},
toolbox: {
show:true,
showTitle: true,
feature: {
rect: {
show: true,
title: 'Trace选择'
},
brush: {
type: ["rect"], // 开启矩形选择
show: true,//是否显示 这里我们直接true
iconStyle: {
opacity: 0,//通过opacity设置为0隐藏图标
},
}
},
left:"40%", //组件离容器左侧的距离,'left', 'center', 'right','20%'
top:"-3%", //组件离容器上侧的距离,'top', 'middle', 'bottom','20%'
right:"auto", //组件离容器右侧的距离,'20%'
bottom:"auto",
},
brush: {
toolbox: ['rect'],
xAxisIndex: 0,
throttleType:'debounce',
throttleDelay:600
},
legend: {
data: ['成功', '失败'],
left: 'center',
bottom: 0,
itemGap: 100,
textStyle: {//文字颜色
fontSize: 12,
padding:[0,3],//文字与图形之间的左右间距
rich:{
labelName:{
fontSize:14,
color:'#333',
fontWeight:500
}
}
},
formatter: function (params) {
// 获取legend显示内容
let data = tmpData;
let sl,fl;
if(data.success!=null){
sl = tmpData.success.length;
}else{
sl = 0
}
if( data.failed!=null){
fl = tmpData.failed.length;
}else{
fl = 0
}
var target;
if(params == '成功'){
target = sl;
}else if(params == '失败'){
target = fl;
}
return target != undefined?params +' '+`{labelName|${target}}`:params
},
},
xAxis: [
{
type: 'time',
data: [
...successData,
...failedData
],
gridIndex:0,
axisLabel: {
show:true,
rotate: 45, // 旋转标签,适用于标签较长的情况
interval: 'auto',
margin: 10, // 增加刻度标签间隔
textStyle: {
fontSize: 10,
textAlign:'center'
},
formatter: function(params) {
let newParams = moment(params).format('HH:mm:ss');
let time = newParams
return time;
}
},
splitLine: {
show: true
},
}
],
yAxis: [
{
type: 'value',
// scale: true,
gridIndex:0,
axisLabel: {
formatter: '{value}'
},
axisLine:{
show:true
},
axisTick:{
show:true
},
splitLine: {
show: true
},
data:[0,2500,5000,7500,10000],
}
],
series: [
{
name: '成功',
type: 'scatter',
emphasis: {
focus: 'series'
},
//设置散点图样式
itemStyle:{
color:'#13ce66'
},
symbolSize:10,//设置散点的大小
data:tmpData.success,
markArea: {
silent: true,
itemStyle: {
color: 'transparent',
borderWidth: 0,
borderType: 'dashed'
},
},
},
{
name: '失败',
type: 'scatter',
emphasis: {
focus: 'series'
},
itemStyle:{
color:'#ff4949'
},
// prettier-ignore
data:tmpData.failed,
// data:[],
markArea: {
silent: true,
itemStyle: {
color: 'transparent',
borderWidth: 0,
borderType: 'dashed'
},
},
}
]
}
chart.setOption(option,true)
// 默认开启框选
chart.dispatchAction({
type: 'takeGlobalCursor',
key: 'brush',
brushOption: {
brushType: 'rect' // 指定选框类型
}
})
chart.off("brushSelected");
//框选选择数据
chart.on('brushSelected', (params) => {
var brushComponent = params.batch[0];
let successIndexList=[];
let failIndexList =[];
let successList=[];
let failList=[];
if(brushComponent.selected.length>1){
successIndexList = brushComponent.selected[0].dataIndex
failIndexList = brushComponent.selected[1].dataIndex
}else{
if(brushComponent.selected[0].seriesName !=undefined){
if(brushComponent.selected[0].seriesName =="失败"){
failIndexList = brushComponent.selected[0].dataIndex
}else{
successIndexList = brushComponent.selected[0].dataIndex
}
}
}
if(successIndexList.length>0){
for(let i = 0;i0){
for(let k=0;k0){
let timeArr =[];
let valueArr=[];
for(let m=0;m0){
dataRange = {
start_time:minTime,
end_time:maxTime,
min_duration:minValue,
max_duration:maxValue,
failed:false,
app_alias:this.state.queryParams.app_alias,
service_name:this.props.id,
}
}else{
dataRange = {
start_time:minTime,
end_time:maxTime,
min_duration:minValue,
max_duration:maxValue,
failed:true,
app_alias:this.state.queryParams.app_alias,
service_name:this.props.id,
}
}
let timeAndDuration = JSON.stringify(dataRange);
// let href = this.$router.resolve({
// path:'/latency/index',
// query:{
// data:timeAndDuration
// }
// })
// window.open(window.location.origin+"/"+href.href,"_blank")
let href = `${this.state.traceUrl}/#/latency/index?start_time=${dataRange.start_time}&end_time=${dataRange.end_time}&min_duration=${dataRange.min_duration}&max_duration=${dataRange.max_duration}&failed=${dataRange.failed}&app_alias=${this.state.queryParams.app_alias}&service_name=${this.props.id}`
window.open(href,"_blank")
this.timeoutId = setTimeout(()=>{
chart.dispatchAction({
type: 'brush',//选择action行为
areas:[]//areas表示选框的集合,此时为空即可。
});
},500)
}
});
window.addEventListener("resize",function (){
chart.resize();
});
}
//获取折线图
// http://127.0.0.1:8000/api/v1/service/liveness?service_name={service_name}
getNodeLiveness(){
axios({
url: `${this.state.baseUrl}/api/v1/service/liveness`,
method: "get",
headers: { 'Authorization': getToken },
params: {
service_name:this.props.id,
start_time:this.state.queryParams.start_time,
end_time:this.state.queryParams.end_time,
}
}).then(res => {
if(res && res.data.code == 200){
const list = ((res || {}).data || {}).data || []
this.setState({
livenessData:[...list]
},()=>{
if(this.state.livenessData.length>0){
this.initLiveness(this.state.livenessData);
}
})
}
});
}
//渲染折线图
initLiveness(data){
let liveChart = echarts.getInstanceByDom(document.getElementById("box"));
if (liveChart == null) {
liveChart = echarts.init(document.getElementById("box"));
}else {
liveChart.dispose();
liveChart = echarts.init(document.getElementById("box"));
}
let option = {
grid: {
top:'5%',
left: '1%',
right: '1%',
bottom: '5%',
containLabel: true
},
tooltip: {
showDelay: 0,
formatter: function (params) {
let newParams = moment(params.data[0]).format('YYYY-MM-DD HH:mm:ss');
let time = newParams+"
"+ params.data[1]+"ms"
return time;
},
axisPointer: {
show: true,
type: 'cross',
lineStyle: {
type: 'dashed',
width: 1
}
}
},
xAxis: {
type: 'time',
boundaryGap: false,
// splitNumber: 3,
axisLabel: {
show:true,
rotate: 45, // 旋转标签,适用于标签较长的情况
interval: 'auto',
margin: 10, // 增加刻度标签间隔
textStyle: {
fontSize: 10,
textAlign:'center'
},
formatter: function(params) {
let newParams = moment(params).format('HH:mm:ss');
let time = newParams
return time;
}
},
splitLine: {
show: true
},
},
yAxis: {
type: 'value',
boundaryGap: [0, '30%']
},
visualMap: {
type: 'piecewise',
show: false,
dimension: 0,
seriesIndex: 0,
pieces: [
{
gt: 1,
lt: 3,
color: 'rgba(0, 0, 180, 0.4)'
},
{
gt: 5,
lt: 7,
color: 'rgba(0, 0, 180, 0.4)'
}
]
},
series: [
{
type: 'line',
smooth: 0.6,
symbol: 'none',
lineStyle: {
color: '#5470C6',
width: 1
},
markLine: {
symbol: ['none', 'none'],
label: { show: false },
data: [{ xAxis: 1 }, { xAxis: 3 }, { xAxis: 5 }, { xAxis: 7 }]
},
areaStyle: {},
data: data
}
]
};
liveChart.setOption(option,true)
}
//获取异常trace列表 /api/v1/service/spans
getServiceSpans(){
this.setState({ loading: true });
this.setState({
traceData:[],
pagination:{total:0}
},()=>{
})
axios({
url: `${this.state.baseUrl}/api/v1/service/spans`,
method: "get",
headers: { 'Authorization': getToken },
params: {
service_name:this.props.id,
only_exception:this.state.traceQuery.only_exception, // 仅显示异常trace相关
only_database:this.state.traceQuery.only_database,
pageIndex:this.state.pagination.pageIndex,
pageSize:this.state.pagination.pageSize,
start_time:this.state.queryParams.start_time,
end_time:this.state.queryParams.end_time,
app_alias:this.state.queryParams.app_alias,
sort_field:'Timestamp',
sort_type: this.state.queryParams.sort_type
}
}).then(res => {
if(res && res.data.code == 200){
const list = (((res || {}).data || {}).data || {}).list || []
const total = (((res || {}).data || {}).data || {}).count || 0
this.setState({
traceData:[...list],
pagination:{...this.state.pagination,total:total}
},()=>{
})
}
});
}
//获取table 数据
getTableData(){
this.setState({ loading: true });
this.setState({
tableData:[],
pagination2:{total:0}
},()=>{
})
axios({
url: `${this.state.coreBaseUrl}/v1/system-component/reqlist`,
method: "get",
headers: { 'Authorization': getToken },
params: {
page_num:this.state.pagination2.page_num,
page_size:this.state.pagination2.page_size,
start_time:this.state.queryParams.start_time,
end_time:this.state.queryParams.end_time,
app_alias:this.state.queryParams.app_alias,
component: this.props.id//
}
}).then(res => {
if(res && res.data.code == 200){
const list = (((res || {}).data || {}).data || {}).list || []
const total = (((res || {}).data || {}).data || {}).total || 0
const page_num = (((res || {}).data || {}).data || {}).page_num || 1
const page_size = (((res || {}).data || {}).data || {}).page_size || 10
this.setState({
tableData:[...list],
pagination2:{page_num:page_num,page_size:page_size,total:total}
},()=>{
})
}
});
}
getBarData(){// 获取柱状图数据
this.setState({ loading: true });
this.setState({
tableData:[],
pagination2:{total:0}
},()=>{
})
axios({
url: `${this.state.coreBaseUrl}/v1/system-component/stats`,
method: "get",
headers: { 'Authorization': getToken },
params: {
start_time:this.state.queryParams.start_time,
end_time:this.state.queryParams.end_time,
app_alias:this.state.queryParams.app_alias,
component: this.props.id //
}
}).then(res => {
if(res && res.data.code == 200){
const list = res?.data?.data?.database_stats?.request_bar || []
this.setState({
barData:list
},()=>{
if(this.state.barData.length>0){
setTimeout(()=>{
this.initLineBar(this.state.barData);
},0)
}
})
}
});
}
// 渲染调用统计 折线/柱形图
initLineBar(data){
let liveChart = echarts.getInstanceByDom(document.getElementById("chartContent"));
let xAxisArr = data.map(item => item.start_time) || []
let dataArr = data.map(item => item.total) || []
if (liveChart == null) {
liveChart = echarts.init(document.getElementById("chartContent"));
} else {
liveChart.dispose();
liveChart = echarts.init(document.getElementById("chartContent"));
}
let option = {
xAxis: {
type: 'category',
data: xAxisArr,
axisLabel: {
show:true,
rotate: 45, // 旋转标签,适用于标签较长的情况
interval: 'auto',
margin: 10, // 增加刻度标签间隔
textStyle: {
fontSize: 10,
textAlign:'center'
},
formatter: function(params) {
let newParams = moment(params).format('HH:mm:ss');
let time = newParams
return time;
}
},
},
tooltip: {
showDelay: 0,
axisPointer: {
show: true,
type: 'cross',
lineStyle: {
type: 'dashed',
width: 1
}
}
},
yAxis: {
type: 'value'
},
series: [
{
data: dataArr,
type: 'bar'
}
]
};
liveChart.setOption(option,true)
}
handleTableChange=(pagination,filters, sorter, extra)=>{
const {current} = pagination
let sort = ''
if(sorter && sorter.order == 'ascend'){
sort = 'ASC'
} else if(sorter && sorter.order == 'descend'){
sort = 'DESC'
}
this.setState({
pagination: {...this.state.pagination,pageIndex:current,current:current},
queryParams:{...this.state.queryParams,sort_type:sort}
},()=>{
this.getServiceSpans();
});
}
handleTableChange2=(pagination,filters, sorter, extra)=>{
const {current} = pagination
this.setState({
pagination2: {...this.state.pagination2,page_num:current}
},()=>{
this.getTableData();
});
}
//仅异常
onChangeError = e =>{
const only_exception = e.target.checked ? 1:0;
const pageIndex = 1
const current = 1
this.setState({
traceQuery:{...this.state.traceQuery,only_exception:only_exception},
pagination: {...this.state.pagination,pageIndex:pageIndex,current:current},
},()=>{
this.getServiceSpans();
});
}
//仅sql
onChangeSql = e =>{
const only_database = e.target.checked ? 1:0;
const pageIndex = 1
const current = 1
this.setState({
traceQuery:{...this.state.traceQuery,only_database:only_database},
pagination: {...this.state.pagination,pageIndex:pageIndex,current:current},
},()=>{
this.getServiceSpans(this.props.source,this.props.target);
});
}
//单选按钮
onChange = e => {
const percentile = e.target.value
this.setState({
queryParams:{...this.state.queryParams,percentile:percentile},
AnalystData:[]
},()=>{
this.getNodeAnalyst();
});
};
renderTable(table) {
const { nodeMatches = makeMap() } = this.props;
if (isGenericTable(table)) {
return (
);
} if (isPropertyList(table)) {
return (
);
}
log(`Undefined type '${table.type}' for table ${table.id}`);
return null;
}
componentDidUpdate() {
this.updateTitle();
}
updateTitle() {
setDocumentTitle(this.props.details && this.props.details.label);
}
//dottedcylinder 类型 -获取基础信息
getDottBasicsData(){
this.setState({ loading: true });
this.setState({
messaging_stats:{
topic_stats:[]
}
},()=>{
})
axios({
url: `${this.state.coreBaseUrl}/v1/system-component/stats`,
method: "get",
headers: { 'Authorization': getToken },
params: {
start_time:this.state.queryParams.start_time,
end_time:this.state.queryParams.end_time,
app_alias:this.state.queryParams.app_alias,
component: this.props.id//
}
}).then(res => {
if(res && res.data.code == 200){
const messaging_stats = (((res || {}).data || {}).data || {}).messaging_stats || {}
this.setState({
messaging_stats:messaging_stats
},()=>{
})
}
});
}
// cloud 类型-获取嵌套table
getCloudTableData(){// 获取柱状图数据
this.setState({ loading: true });
this.setState({
cloudTable:[],
cloudTable2:[],
},()=>{
})
axios({
url: `${this.state.coreBaseUrl}/v1/service/related-apps`,
method: "get",
headers: { 'Authorization': getToken },
params: {
start_time: this.state.queryParams.start_time, //1727366400
end_time:this.state.queryParams.end_time, // 1727415703
app_alias:this.state.queryParams.app_alias,
type: this.props.id //
}
}).then(res => {
if(res && res.data.code == 200){
const list = (((res || {}).data ||{}).data || {}).app_list || []
const list2 = (((res ||{}).data ||{}).data ||{}).client_list || []
this.setState({
cloudTable:[...list],
cloudTable2: [...list2]
},()=>{
})
}
});
}
}
NodeDetails.propTypes = {
renderNodeDetailsExtras: PropTypes.func,
};
NodeDetails.defaultProps = {
renderNodeDetailsExtras: noop,
};
function mapStateToProps(state, ownProps) {
const currentTopologyId = state.get('currentTopologyId');
return {
nodeMatches: state.getIn(['searchNodeMatches', currentTopologyId, ownProps.id]),
nodes: state.get('nodes'),
selectedNodeId: state.get('selectedNodeId'),
transitioning: state.get('pausedAt') !== ownProps.timestamp,
};
}
export default connect(
mapStateToProps,
{ clickCloseDetails, clickShowTopologyForNode }
)(NodeDetails);