[ISSUE #756] [RocketMQ-Console] Improve message trace UI (#757)

* Trace UI improvement

1. Change Send Message Info UI
2. Handle edge case: consume trace subBefore or subAfter trace is missing (if subAfter trace is missing, the status will be unknown)
3. Fix timestamp typo
4. Time format for ms
5. Change costTime color from white to black
6. Remove graph and data format button

* improve costTime display
This commit is contained in:
StyleTang
2021-07-30 19:52:37 +08:00
committed by GitHub
parent 199cfcd348
commit d608bd69b7
6 changed files with 206 additions and 159 deletions

View File

@@ -21,6 +21,7 @@ import com.google.common.base.Charsets;
import org.apache.rocketmq.client.trace.TraceBean;
import org.apache.rocketmq.client.trace.TraceContext;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.console.model.trace.MessageTraceStatusEnum;
import org.apache.rocketmq.console.util.MsgTraceDecodeUtil;
import java.util.ArrayList;
@@ -66,9 +67,9 @@ public class MessageTraceView {
messageTraceView.setCostTime(context.getCostTime());
messageTraceView.setGroupName(context.getGroupName());
if (context.isSuccess()) {
messageTraceView.setStatus("success");
messageTraceView.setStatus(MessageTraceStatusEnum.SUCCESS.getStatus());
} else {
messageTraceView.setStatus("failed");
messageTraceView.setStatus(MessageTraceStatusEnum.FAILED.getStatus());
}
messageTraceView.setKeys(traceBean.getKeys());
messageTraceView.setMsgId(traceBean.getMsgId());

View File

@@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.rocketmq.console.model.trace;
import lombok.Getter;
@Getter
public enum MessageTraceStatusEnum {
SUCCESS("success"),
FAILED("failed"),
UNKNOWN("unknown");
private final String status;
MessageTraceStatusEnum(String status) {
this.status = status;
}
}

View File

@@ -25,8 +25,8 @@ public class TraceNode {
private String storeHost;
private String clientHost;
private int costTime;
private long beginTimeStamp;
private long endTimeStamp;
private long beginTimestamp;
private long endTimestamp;
private int retryTimes;
private String status;
private String transactionState;

View File

@@ -40,6 +40,7 @@ import org.apache.rocketmq.console.model.trace.ProducerNode;
import org.apache.rocketmq.console.model.trace.MessageTraceGraph;
import org.apache.rocketmq.console.model.trace.SubscriptionNode;
import org.apache.rocketmq.console.model.trace.TraceNode;
import org.apache.rocketmq.console.model.trace.MessageTraceStatusEnum;
import org.apache.rocketmq.console.service.MessageTraceService;
import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.slf4j.Logger;
@@ -125,13 +126,16 @@ public class MessageTraceServiceImpl implements MessageTraceService {
}
private TraceNode buildTransactionNode(MessageTraceView messageTraceView) {
return buildTraceNode(messageTraceView);
TraceNode transactionNode = buildTraceNode(messageTraceView);
transactionNode.setCostTime(-1);
return transactionNode;
}
private List<SubscriptionNode> buildSubscriptionNodeList(
Map<String, Pair<MessageTraceView, MessageTraceView>> requestIdTracePairMap) {
Map<String, List<TraceNode>> subscriptionTraceNodeMap = Maps.newHashMap();
for (Pair<MessageTraceView, MessageTraceView> traceNodePair : requestIdTracePairMap.values()) {
traceNodePair = makeBeforeOrAfterMissCompatible(traceNodePair);
MessageTraceView subBeforeTrace = traceNodePair.getObject1();
MessageTraceView subAfterTrace = traceNodePair.getObject2();
List<TraceNode> traceNodeList = subscriptionTraceNodeMap.computeIfAbsent(subBeforeTrace.getGroupName(),
@@ -141,9 +145,9 @@ public class MessageTraceServiceImpl implements MessageTraceService {
consumeNode.setStoreHost(subBeforeTrace.getStoreHost());
consumeNode.setClientHost(subBeforeTrace.getClientHost());
consumeNode.setRetryTimes(subBeforeTrace.getRetryTimes());
consumeNode.setBeginTimeStamp(subBeforeTrace.getTimeStamp());
consumeNode.setBeginTimestamp(subBeforeTrace.getTimeStamp());
consumeNode.setCostTime(subAfterTrace.getCostTime());
consumeNode.setEndTimeStamp(subBeforeTrace.getTimeStamp() + subAfterTrace.getCostTime());
consumeNode.setEndTimestamp(subBeforeTrace.getTimeStamp() + Math.max(0, subAfterTrace.getCostTime()));
consumeNode.setStatus(subAfterTrace.getStatus());
traceNodeList.add(consumeNode);
}
@@ -157,6 +161,25 @@ public class MessageTraceServiceImpl implements MessageTraceService {
}).collect(Collectors.toList());
}
private Pair<MessageTraceView, MessageTraceView> makeBeforeOrAfterMissCompatible(Pair<MessageTraceView, MessageTraceView> traceNodePair) {
if (traceNodePair.getObject1() != null && traceNodePair.getObject2() != null) {
return traceNodePair;
}
MessageTraceView subBeforeTrace = traceNodePair.getObject1();
MessageTraceView subAfterTrace = traceNodePair.getObject2();
if (subBeforeTrace == null) {
subBeforeTrace = new MessageTraceView();
BeanUtils.copyProperties(subAfterTrace, subBeforeTrace);
}
if (subAfterTrace == null) {
subAfterTrace = new MessageTraceView();
BeanUtils.copyProperties(subBeforeTrace, subAfterTrace);
subAfterTrace.setStatus(MessageTraceStatusEnum.UNKNOWN.getStatus());
subAfterTrace.setCostTime(-1);
}
return new Pair<>(subBeforeTrace, subAfterTrace);
}
private void putIntoMessageTraceViewGroupMap(MessageTraceView messageTraceView,
Map<String, Pair<MessageTraceView, MessageTraceView>> messageTraceViewGroupMap) {
Pair<MessageTraceView, MessageTraceView> messageTracePair = messageTraceViewGroupMap
@@ -183,13 +206,13 @@ public class MessageTraceServiceImpl implements MessageTraceService {
private TraceNode buildTraceNode(MessageTraceView messageTraceView) {
TraceNode traceNode = new TraceNode();
BeanUtils.copyProperties(messageTraceView, traceNode);
traceNode.setBeginTimeStamp(messageTraceView.getTimeStamp());
traceNode.setEndTimeStamp(messageTraceView.getTimeStamp() + messageTraceView.getCostTime());
traceNode.setBeginTimestamp(messageTraceView.getTimeStamp());
traceNode.setEndTimestamp(messageTraceView.getTimeStamp() + messageTraceView.getCostTime());
return traceNode;
}
private List<TraceNode> sortTraceNodeListByBeginTimestamp(List<TraceNode> traceNodeList) {
traceNodeList.sort((o1, o2) -> -Long.compare(o1.getBeginTimeStamp(), o2.getBeginTimeStamp()));
traceNodeList.sort((o1, o2) -> -Long.compare(o1.getBeginTimestamp(), o2.getBeginTimestamp()));
return traceNodeList;
}
}

View File

@@ -18,17 +18,14 @@
var module = app;
const SUCCESS_COLOR = '#75d874';
const ERROR_COLOR = 'red';
const UNKNOWN_COLOR = 'yellow';
const TRANSACTION_COMMIT_COLOR = SUCCESS_COLOR;
const TRANSACTION_ROLLBACK_COLOR = ERROR_COLOR;
const SHOW_GRAPH = 'Show Graph';
const HIDE_GRAPH = 'Hide Graph';
const SHOW_GRAPH_TRACE_DATA = 'Show Graph Trace Data';
const SHOW_ORIGINAL_TRACE_DATA = 'Show Original Trace Data';
const TRANSACTION_UNKNOWN_COLOR = 'grey'
const TIME_FORMAT_PATTERN = "YYYY-MM-DD HH:mm:ss.SSS";
const DEFAULT_DISPLAY_DURATION = 10 * 1000
// transactionTraceNode do not have costTime, assume it cost 50ms
const transactionCheckCostTime = 50;
const TRANSACTION_CHECK_COST_TIME = 50;
module.controller('messageTraceController', ['$scope', '$routeParams', 'ngDialog', '$http', 'Notification', function ($scope, $routeParams, ngDialog, $http, Notification) {
$scope.allTopicList = [];
$scope.selectedTopic = [];
@@ -120,10 +117,6 @@ module.controller('messageTraceController', ['$scope', '$routeParams', 'ngDialog
}]);
module.controller('messageTraceDetailViewDialogController', ['$scope', '$timeout', 'ngDialog', '$http', 'Notification', function ($scope, $timeout, ngDialog, $http, Notification) {
$scope.displayGraph = false;
$scope.showGraphData = true;
$scope.graphButtonName = SHOW_GRAPH;
$scope.traceDataButtonName = SHOW_ORIGINAL_TRACE_DATA;
$scope.displayMessageTraceGraph = function (messageTraceGraph) {
let dom = document.getElementById("messageTraceGraph");
$scope.messageTraceGraph = echarts.init(dom);
@@ -134,18 +127,17 @@ module.controller('messageTraceDetailViewDialogController', ['$scope', '$timeout
let endTime = 0;
let messageGroups = [];
if (messageTraceGraph.producerNode) {
startTime = +messageTraceGraph.producerNode.traceNode.beginTimeStamp;
endTime = +messageTraceGraph.producerNode.traceNode.endTimeStamp;
startTime = +messageTraceGraph.producerNode.traceNode.beginTimestamp;
endTime = +messageTraceGraph.producerNode.traceNode.endTimestamp;
} else {
messageTraceGraph.subscriptionNodeList.forEach(subscriptionNode => {
subscriptionNode.consumeNodeList.forEach(consumeNode => {
startTime = Math.min(startTime, consumeNode.beginTimeStamp);
startTime = Math.min(startTime, consumeNode.beginTimestamp);
})
})
}
function buildNodeColor(traceNode) {
let nodeColor = SUCCESS_COLOR;
if (traceNode.transactionState != null) {
switch (traceNode.transactionState) {
case 'COMMIT_MESSAGE':
@@ -158,16 +150,20 @@ module.controller('messageTraceDetailViewDialogController', ['$scope', '$timeout
return ERROR_COLOR;
}
}
if (traceNode.status !== 'success') {
nodeColor = ERROR_COLOR;
switch (traceNode.status) {
case 'failed':
return ERROR_COLOR;
case 'unknown':
return UNKNOWN_COLOR;
default:
return SUCCESS_COLOR;
}
return nodeColor;
}
function formatXAxisTime(value) {
let duration = Math.max(0, value - startTime);
if (duration < 1000)
return duration + 'ms';
return timeFormat(duration, 'ms');
duration /= 1000;
if (duration < 60)
return timeFormat(duration, 's');
@@ -190,13 +186,31 @@ module.controller('messageTraceDetailViewDialogController', ['$scope', '$timeout
return "";
}
function formatCostTimeStr(costTime) {
if (costTime < 0) {
return "";
}
let costTimeStr = costTime;
if (costTime === 0) {
costTimeStr = '<1'
}
return `${costTimeStr}ms`;
}
function buildCostTimeInfo(costTime) {
if (costTime < 0) {
return "";
}
return `costTime: ${formatCostTimeStr(costTime)}<br/>`
}
function formatNodeToolTip(params) {
let traceNode = params.data.traceData.traceNode;
return `
costTime: ${traceNode.costTime}ms<br />
${buildCostTimeInfo(traceNode.costTime)}
status: ${traceNode.status}<br />
beginTimeStamp: ${new moment(traceNode.beginTimeStamp).format(TIME_FORMAT_PATTERN)}<br />
endTimeStamp: ${new moment(traceNode.endTimeStamp).format(TIME_FORMAT_PATTERN)}<br />
beginTimestamp: ${new moment(traceNode.beginTimestamp).format(TIME_FORMAT_PATTERN)}<br />
endTimestamp: ${new moment(traceNode.endTimestamp).format(TIME_FORMAT_PATTERN)}<br />
clientHost: ${traceNode.clientHost}<br />
storeHost: ${traceNode.storeHost}<br />
retryTimes: ${traceNode.retryTimes}<br />
@@ -211,8 +225,8 @@ module.controller('messageTraceDetailViewDialogController', ['$scope', '$timeout
data.push({
value: [
index,
traceNode.beginTimeStamp,
traceNode.endTimeStamp,
traceNode.beginTimestamp,
traceNode.endTimestamp === traceNode.beginTimestamp ? traceNode.beginTimestamp + 1 : traceNode.endTimestamp,
traceNode.costTime
],
itemStyle: {
@@ -225,7 +239,7 @@ module.controller('messageTraceDetailViewDialogController', ['$scope', '$timeout
traceNode: traceNode
}
});
endTime = Math.max(traceNode.endTimeStamp, endTime);
endTime = Math.max(traceNode.endTimestamp, endTime);
}
messageTraceGraph.subscriptionNodeList.forEach(item => {
@@ -239,13 +253,12 @@ module.controller('messageTraceDetailViewDialogController', ['$scope', '$timeout
let producerNodeIndex = messageGroups.length - 1;
addTraceData(messageTraceGraph.producerNode.traceNode, producerNodeIndex);
messageTraceGraph.producerNode.transactionNodeList.forEach(transactionNode => {
transactionNode.beginTimeStamp = Math.max(messageTraceGraph.producerNode.traceNode.endTimeStamp,
transactionNode.endTimeStamp - transactionCheckCostTime);
transactionNode.beginTimestamp = Math.max(messageTraceGraph.producerNode.traceNode.endTimestamp,
transactionNode.endTimestamp - TRANSACTION_CHECK_COST_TIME);
addTraceData(transactionNode, producerNodeIndex)
endTime = Math.max(endTime, transactionNode.endTimeStamp);
endTime = Math.max(endTime, transactionNode.endTimestamp);
})
}
let totalDuration = endTime - startTime;
if (totalDuration > DEFAULT_DISPLAY_DURATION) {
dataZoomEnd = DEFAULT_DISPLAY_DURATION / totalDuration * 100
@@ -274,8 +287,8 @@ module.controller('messageTraceDetailViewDialogController', ['$scope', '$timeout
transition: ['shape'],
shape: rectShape,
style: api.style({
text: `${api.value(3)}ms`,
textFill: '#fff'
text: formatCostTimeStr(api.value(3)),
textFill: '#000'
})
};
}
@@ -334,25 +347,9 @@ module.controller('messageTraceDetailViewDialogController', ['$scope', '$timeout
$scope.messageTraceGraph.setOption(option);
}
$scope.showGraph = function () {
$scope.displayGraph = !$scope.displayGraph;
if ($scope.displayGraph) {
$scope.graphButtonName = HIDE_GRAPH;
$scope.displayMessageTraceGraph($scope.ngDialogData);
} else {
$scope.messageTraceGraph.dispose();
$scope.graphButtonName = SHOW_GRAPH;
}
$scope.displayMessageTraceGraph($scope.ngDialogData);
};
$scope.changeTraceDataFormat = function () {
$scope.showGraphData = !$scope.showGraphData;
if ($scope.showGraphData) {
$scope.traceDataButtonName = SHOW_ORIGINAL_TRACE_DATA;
} else {
$scope.traceDataButtonName = SHOW_GRAPH_TRACE_DATA;
}
}
function initGraph() {
$timeout(function () {
if (document.getElementById('messageTraceGraph') == null) {

View File

@@ -132,15 +132,16 @@
<script type="text/ng-template" id="messageTraceDetailViewDialog">
<md-content class="md-padding">
<div class="row">
<button class="ngdialog-button ngdialog-button-primary" type="button"
ng-click="changeTraceDataFormat()">{{traceDataButtonName}}
</button>
<button class="ngdialog-button ngdialog-button-primary" type="button"
ng-click="showGraph()">{{graphButtonName}}
</button>
<div id="messageTrace" class="container">
<a>
<h3 data-toggle="collapse" data-target="#messageTraceGraph">
Message Trace Graph
</h3>
</a>
<div id="messageTraceGraph" class="collapse in" style="height: 500px; width: 1024px"/>
</div>
</div>
<div class="row" ng-hide="!displayGraph" id="messageTraceGraph" style="height: 500px; width: 1024px"></div>
<div class="row" ng-hide="!showGraphData">
<div class="row">
<div id="producerSendMessage" class="container">
<a>
<h3 data-toggle="collapse" data-target="#sendMessageTrace">
@@ -152,65 +153,86 @@
No Producer Trace Data
</div>
<div id="producerTrace" ng-if="ngDialogData.producerNode != null">
<h4>Send Message Info</h4>
<table class="table table-bordered">
<tr>
<th class="text-center">topic</th>
<th class="text-center">groupName</th>
<th class="text-center">keys</th>
<th class="text-center">tags</th>
<th class="text-center">msgId</th>
</tr>
<tr>
<td class="text-center">{{ngDialogData.producerNode.topic}}</td>
<td class="text-center">{{ngDialogData.producerNode.groupName}}</td>
<td class="text-center">{{ngDialogData.producerNode.keys}}</td>
<td class="text-center">{{ngDialogData.producerNode.tags}}</td>
<td class="text-center">{{ngDialogData.producerNode.msgId}}</td>
</tr>
<tr>
<th class="text-center">beginTimeStamp</th>
<th class="text-center">endTimeStamp</th>
<th class="text-center">costTime</th>
<th class="text-center">msgType</th>
<th class="text-center">offSetMsgId</th>
</tr>
<tr>
<td class="text-center">
{{ngDialogData.producerNode.traceNode.beginTimeStamp | date:'yyyy-MM-dd HH:mm:ss.sss'}}
</td>
<td class="text-center">
{{ngDialogData.producerNode.traceNode.endTimeStamp | date:'yyyy-MM-dd HH:mm:ss.sss'}}
</td>
<td class="text-center">{{ngDialogData.producerNode.traceNode.costTime}} ms</td>
<td class="text-center">{{ngDialogData.producerNode.traceNode.msgType}}</td>
<td class="text-center">{{ngDialogData.producerNode.offSetMsgId}}</td>
</tr>
<tr>
<th class="text-center">clientHost</th>
<th class="text-center">storeHost</th>
<th class="text-center">retryTimes</th>
</tr>
<tr>
<td class="text-center">{{ngDialogData.producerNode.traceNode.clientHost}}</td>
<td class="text-center">{{ngDialogData.producerNode.traceNode.storeHost}}</td>
<td class="text-center">{{ngDialogData.producerNode.traceNode.retryTimes}}</td>
</tr>
</table>
<h4>Send Message Info : ( Message Id <b>{{ngDialogData.producerNode.msgId}}</b> )</h4>
<div class="panel panel-default">
<form>
<div class="form-row">
<div class="form-group col-md-3">
<label for="topic">Topic</label>
<input type="text" class="form-control" id="topic" value="{{ngDialogData.producerNode.topic}}" readonly>
</div>
<div class="form-group col-md-3">
<label for="producerGroup">ProducerGroup</label>
<input type="text" class="form-control" id="producerGroup" value="{{ngDialogData.producerNode.groupName}}" readonly>
</div>
<div class="form-group col-md-3">
<label for="keys">Message Key</label>
<input type="text" class="form-control" id="keys" value="{{ngDialogData.producerNode.keys}}" readonly>
</div>
<div class="form-group col-md-3">
<label for="tags">Tag</label>
<input type="text" class="form-control" id="tags" value="{{ngDialogData.producerNode.tags}}" readonly>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-3">
<label for="beginTimestamp">BeginTimestamp</label>
<input type="text" class="form-control" id="beginTimestamp"
value="{{ngDialogData.producerNode.traceNode.beginTimestamp | date:'yyyy-MM-dd HH:mm:ss.sss'}}" readonly>
</div>
<div class="form-group col-md-3">
<label for="endTimestamp">EndTimestamp</label>
<input type="text" class="form-control" id="endTimestamp"
value="{{ngDialogData.producerNode.traceNode.endTimestamp | date:'yyyy-MM-dd HH:mm:ss.sss'}}" readonly>
</div>
<div class="form-group col-md-3">
<label for="costTime">CostTime</label>
<input type="text" class="form-control" id="costTime"
value="{{ngDialogData.producerNode.traceNode.costTime === 0? '<1':ngDialogData.producerNode.traceNode.costTime}}ms"
readonly>
</div>
<div class="form-group col-md-3">
<label for="msgType">MsgType</label>
<input type="text" class="form-control" id="msgType" value="{{ngDialogData.producerNode.traceNode.msgType}}" readonly>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-3">
<label for="clientHost">ClientHost</label>
<input type="text" class="form-control" id="clientHost"
value="{{ngDialogData.producerNode.traceNode.clientHost}}" readonly>
</div>
<div class="form-group col-md-3">
<label for="storeHost">StoreHost</label>
<input type="text" class="form-control" id="storeHost"
value="{{ngDialogData.producerNode.traceNode.storeHost}}" readonly>
</div>
<div class="form-group col-md-3">
<label for="retryTimes">RetryTimes</label>
<input type="text" class="form-control" id="retryTimes" value="{{ngDialogData.producerNode.traceNode.retryTimes}}" readonly>
</div>
<div class="form-group col-md-3">
<label for="offSetMsgId">OffSetMsgId</label>
<input type="text" class="form-control" id="offSetMsgId" value="{{ngDialogData.producerNode.offSetMsgId}}" readonly>
</div>
</div>
</form>
</div>
<div id="producerTransaction"
ng-if="ngDialogData.producerNode.transactionNodeList.length > 0">
<h4>Check Transaction Info</h4>
ng-show="ngDialogData.producerNode.transactionNodeList.length > 0">
<h4>Check Transaction Info : </h4>
<table class="table table-bordered">
<tr>
<th class="text-center">timeStamp</th>
<th class="text-center">transactionState</th>
<th class="text-center">fromTransactionCheck</th>
<th class="text-center">clientHost</th>
<th class="text-center">storeHost</th>
<th class="text-center">Timestamp</th>
<th class="text-center">TransactionState</th>
<th class="text-center">FromTransactionCheck</th>
<th class="text-center">ClientHost</th>
<th class="text-center">StoreHost</th>
</tr>
<tr ng-repeat="transactionNode in ngDialogData.producerNode.transactionNodeList">
<td class="text-center">
{{transactionNode.beginTimeStamp | date:'yyyy-MM-dd HH:mm:ss.sss'}}
{{transactionNode.beginTimestamp | date:'yyyy-MM-dd HH:mm:ss.sss'}}
</td>
<td class="text-center">{{transactionNode.transactionState}}</td>
<td class="text-center">{{transactionNode.fromTransactionCheck}}</td>
@@ -229,7 +251,7 @@
</h3>
</a>
<div id="consumeMessageTrace" class="collapse in">
<div id="consumerTrace" ng-if="ngDialogData.subscriptionNodeList.length > 0">
<div id="consumerTrace" ng-show="ngDialogData.subscriptionNodeList.length > 0">
<div ng-repeat="subscriptionNode in ngDialogData.subscriptionNodeList">
<div class="container">
<a>
@@ -241,22 +263,24 @@
<div id="subscriptionNode{{subscriptionNode.subscriptionGroup}}" class="collapse in">
<table class="table table-bordered">
<tr>
<th class="text-center">beginTimeStamp</th>
<th class="text-center">endTimeStamp</th>
<th class="text-center">costTime</th>
<th class="text-center">status</th>
<th class="text-center">retryTimes</th>
<th class="text-center">clientHost</th>
<th class="text-center">storeHost</th>
<th class="text-center">BeginTimestamp</th>
<th class="text-center">EndTimestamp</th>
<th class="text-center">CostTime</th>
<th class="text-center">Status</th>
<th class="text-center">RetryTimes</th>
<th class="text-center">ClientHost</th>
<th class="text-center">StoreHost</th>
</tr>
<tr ng-repeat="consumeNode in subscriptionNode.consumeNodeList">
<td class="text-center">
{{consumeNode.beginTimeStamp | date:'yyyy-MM-dd HH:mm:ss.sss'}}
{{consumeNode.beginTimestamp | date:'yyyy-MM-dd HH:mm:ss.sss'}}
</td>
<td class="text-center">
{{consumeNode.endTimeStamp | date:'yyyy-MM-dd HH:mm:ss.sss'}}
{{consumeNode.endTimestamp | date:'yyyy-MM-dd HH:mm:ss.sss'}}
</td>
<td class="text-center">{{consumeNode.costTime < 0 ? '--' :
((consumeNode.costTime === 0 ? '<1' : consumeNode.costTime) + 'ms')}}
</td>
<td class="text-center">{{consumeNode.costTime}}</td>
<td class="text-center">{{consumeNode.status}}</td>
<td class="text-center">{{consumeNode.retryTimes}}</td>
<td class="text-center">{{consumeNode.clientHost}}</td>
@@ -267,42 +291,12 @@
</div>
</div>
</div>
<div ng-if="ngDialogData.subscriptionNodeList.length == 0">
<div ng-show="ngDialogData.subscriptionNodeList.length == 0">
No Consumer Trace Data
</div>
</div>
</div>
</div>
<div class="row" ng-hide="showGraphData">
<table class="table table-bordered">
<tr>
<th class="text-center">Message ID</th>
<th class="text-center">Tag</th>
<th class="text-center">Message Key</th>
<th class="text-center">StoreTime</th>
<th class="text-center">StoreHost</th>
<th class="text-center">ClientHost</th>
<th class="text-center">costTime</th>
<th class="text-center">status</th>
<th class="text-center">traceType</th>
<th class="text-center">msgType</th>
<th class="text-center">transactionState</th>
</tr>
<tr ng-repeat="item in ngDialogData.messageTraceViews">
<td class="text-center">{{item.msgId}}</td>
<td class="text-center">{{item.tags}}</td>
<td class="text-center">{{item.keys}}</td>
<td class="text-center">{{item.timeStamp | date:'yyyy-MM-dd HH:mm:ss'}}</td>
<td class="text-center">{{item.storeHost}}</td>
<td class="text-center">{{item.clientHost}}</td>
<td class="text-center">{{item.costTime}}ms</td>
<td class="text-center">{{item.status}}</td>
<th class="text-center">{{item.traceType}}</th>
<th class="text-center">{{item.msgType}}</th>
<th class="text-center">{{item.transactionState}}</th>
</tr>
</table>
</div>
</md-content>
<div class="ngdialog-buttons">
<button type="button" class="ngdialog-button ngdialog-button-secondary"