[ISSUE #525]Support the message track detail showing function in the rocketmq console (#154)

* I have finished developing the new feature for the message track which includes the part of rocketmq-console#525 initially.Please review it.

* [ISSUE #525] Support the message track,add the function which supports trace topic name value configurable by users.

* [ISSUE#525]optimize codes for message track

* [ISSUE#525]remove the unnecessary codes for msg trace feature.

* [ISSUE#525]implement the HttpBasicAuthorizedFilter to add Basic realm="rocketmq" in the http response header
This commit is contained in:
Hu Zongtang
2019-01-04 15:24:56 +08:00
committed by Zhendong Liu
parent fa12fa32ae
commit efb5e9a1f2
19 changed files with 805 additions and 3 deletions

View File

@@ -21,7 +21,7 @@
<commons-lang.version>2.6</commons-lang.version>
<commons-io.version>2.4</commons-io.version>
<commons-cli.version>1.2</commons-cli.version>
<rocketmq.version>4.0.0-incubating</rocketmq.version>
<rocketmq.version>4.4.0-SNAPSHOT</rocketmq.version>
<surefire.version>2.19.1</surefire.version>
<aspectj.version>1.6.11</aspectj.version>
<main.basedir>${basedir}/../..</main.basedir>

View File

@@ -19,9 +19,11 @@ package org.apache.rocketmq.console;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@EnableAutoConfiguration
@SpringBootApplication
@ServletComponentScan
public class App {
public static void main(String[] args) {

View File

@@ -41,6 +41,8 @@ public class RMQConfigure {
private boolean enableDashBoardCollect;
private String msgTrackTopicName;
public String getNamesrvAddr() {
return namesrvAddr;
}
@@ -84,4 +86,12 @@ public class RMQConfigure {
public void setEnableDashBoardCollect(String enableDashBoardCollect) {
this.enableDashBoardCollect = Boolean.valueOf(enableDashBoardCollect);
}
public String getMsgTrackTopicName() {
return msgTrackTopicName;
}
public void setMsgTrackTopicName(String msgTrackTopicName) {
this.msgTrackTopicName = msgTrackTopicName;
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.controller;
import com.google.common.collect.Maps;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.common.MixAll;
import org.apache.rocketmq.common.Pair;
import org.apache.rocketmq.console.config.RMQConfigure;
import org.apache.rocketmq.console.model.MessageView;
import org.apache.rocketmq.console.service.MessageService;
import org.apache.rocketmq.console.service.MessageTraceService;
import org.apache.rocketmq.tools.admin.api.MessageTrack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/messageTrace")
public class MessageTraceController {
private Logger logger = LoggerFactory.getLogger(MessageController.class);
@Resource
private MessageService messageService;
@Resource
private MessageTraceService messageTraceService;
@Resource
private RMQConfigure rmqConfigure;
@RequestMapping(value = "/viewMessage.query", method = RequestMethod.GET)
@ResponseBody
public Object viewMessage(@RequestParam(required = false) String topic, @RequestParam String msgId) {
Map<String, Object> messageViewMap = Maps.newHashMap();
Pair<MessageView, List<MessageTrack>> messageViewListPair = messageService.viewMessage(topic, msgId);
messageViewMap.put("messageView", messageViewListPair.getObject1());
return messageViewMap;
}
@RequestMapping(value = "/viewMessageTraceDetail.query", method = RequestMethod.GET)
@ResponseBody
public Object viewTraceMessages(@RequestParam(required = false) String topic, @RequestParam String msgId) {
String queryTopic = rmqConfigure.getMsgTrackTopicName();
if (StringUtils.isEmpty(queryTopic)) {
queryTopic = MixAll.RMQ_SYS_TRACE_TOPIC;
}
logger.info("query data topic name is:{}",queryTopic);
return messageTraceService.queryMessageTraceByTopicAndKey(queryTopic, msgId);
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
@Component
@WebFilter(urlPatterns = "/*", filterName = "httpBasicAuthorizedFilter")
public class HttpBasicAuthorizedFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.setContentType("application/json; charset=utf-8");
httpResponse.setHeader("WWW-Authenticate", "Basic realm=\"rocketmq\"");
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}

View File

@@ -0,0 +1,162 @@
/*
* 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;
import java.util.ArrayList;
import java.util.List;
import org.apache.rocketmq.client.trace.TraceBean;
import org.apache.rocketmq.client.trace.TraceContext;
import org.apache.rocketmq.console.util.MsgTraceDecodeUtil;
public class MessageTraceView {
private String msgId;
private String tags;
private String keys;
private String storeHost;
private int costTime;
private String msgType;
private String offSetMsgId;
private long timeStamp;
private String topic;
private String groupName;
private String status;
public static List<MessageTraceView> decodeFromTraceTransData(String key,String messageBody) {
List<MessageTraceView> messageTraceViewList = new ArrayList<MessageTraceView>();
if (messageBody == null || messageBody.length() <= 0) {
return messageTraceViewList;
}
List<TraceContext> traceContextList = MsgTraceDecodeUtil.decoderFromTraceDataString(messageBody);
for (TraceContext context : traceContextList) {
MessageTraceView messageTraceView = new MessageTraceView();
TraceBean traceBean = context.getTraceBeans().get(0);
if (!traceBean.getMsgId().equals(key)) {
continue;
}
messageTraceView.setCostTime(context.getCostTime());
messageTraceView.setGroupName(context.getGroupName());
if (context.isSuccess()) {
messageTraceView.setStatus("Sucess");
}
else {
messageTraceView.setStatus("failed");
}
messageTraceView.setKeys(traceBean.getKeys());
messageTraceView.setMsgId(traceBean.getMsgId());
messageTraceView.setTags(traceBean.getTags());
messageTraceView.setTopic(traceBean.getTopic());
messageTraceView.setMsgType(context.getTraceType().name());
messageTraceView.setOffSetMsgId(traceBean.getOffsetMsgId());
messageTraceView.setTimeStamp(context.getTimeStamp());
messageTraceView.setStoreHost(traceBean.getStoreHost());
messageTraceViewList.add(messageTraceView);
}
return messageTraceViewList;
}
public String getMsgId() {
return msgId;
}
public void setMsgId(String msgId) {
this.msgId = msgId;
}
public String getTags() {
return tags;
}
public void setTags(String tags) {
this.tags = tags;
}
public String getKeys() {
return keys;
}
public void setKeys(String keys) {
this.keys = keys;
}
public String getStoreHost() {
return storeHost;
}
public void setStoreHost(String storeHost) {
this.storeHost = storeHost;
}
public int getCostTime() {
return costTime;
}
public void setCostTime(int costTime) {
this.costTime = costTime;
}
public String getMsgType() {
return msgType;
}
public void setMsgType(String msgType) {
this.msgType = msgType;
}
public String getOffSetMsgId() {
return offSetMsgId;
}
public void setOffSetMsgId(String offSetMsgId) {
this.offSetMsgId = offSetMsgId;
}
public long getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(long timeStamp) {
this.timeStamp = timeStamp;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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.service;
import java.util.List;
import org.apache.rocketmq.console.model.MessageTraceView;
public interface MessageTraceService {
List<MessageTraceView> queryMessageTraceByTopicAndKey(final String topic, final String key);
}

View File

@@ -44,6 +44,7 @@ import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
import org.apache.rocketmq.common.protocol.body.GroupList;
import org.apache.rocketmq.common.protocol.body.KVTable;
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
import org.apache.rocketmq.common.protocol.body.QueryConsumeQueueResponseBody;
import org.apache.rocketmq.common.protocol.body.QueueTimeSpan;
import org.apache.rocketmq.common.protocol.body.SubscriptionGroupWrapper;
import org.apache.rocketmq.common.protocol.body.TopicConfigSerializeWrapper;
@@ -498,4 +499,10 @@ public class MQAdminExtImpl implements MQAdminExt {
List<String> list) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException, UnsupportedEncodingException {
return null;
}
@Override public QueryConsumeQueueResponseBody queryConsumeQueue(String brokerAddr, String topic,
int queueId, long index, int count,
String consumerGroup) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException {
return null;
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.service.impl;
import com.google.common.base.Charsets;
import com.google.common.base.Throwables;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.console.model.MessageTraceView;
import org.apache.rocketmq.console.service.MessageTraceService;
import org.apache.rocketmq.tools.admin.MQAdminExt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class MessageTraceServiceImpl implements MessageTraceService {
private Logger logger = LoggerFactory.getLogger(MessageTraceServiceImpl.class);
private final static int QUERY_MESSAGE_MAX_NUM = 64;
@Resource
private MQAdminExt mqAdminExt;
@Override public List<MessageTraceView> queryMessageTraceByTopicAndKey(String topic, String key) {
try {
List<MessageTraceView> messageTraceViews = new ArrayList<MessageTraceView>();
List<MessageExt> messageTraceList = mqAdminExt.queryMessage(topic, key, QUERY_MESSAGE_MAX_NUM, 0, System.currentTimeMillis()).getMessageList();
for (MessageExt messageExt : messageTraceList) {
List<MessageTraceView> messageTraceView = MessageTraceView.decodeFromTraceTransData(key, new String(messageExt.getBody(), Charsets.UTF_8));
messageTraceViews.addAll(messageTraceView);
}
return messageTraceViews;
}
catch (Exception err) {
throw Throwables.propagate(err);
}
}
}

View File

@@ -19,6 +19,7 @@ package org.apache.rocketmq.console.service.impl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.base.Throwables;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PostConstruct;
@@ -86,7 +87,7 @@ public class MonitorServiceImpl implements MonitorService {
}
@PostConstruct
private void loadData() {
private void loadData() throws IOException {
String content = MixAll.file2String(getConsumerMonitorConfigDataPath());
if (content == null) {
content = MixAll.file2String(getConsumerMonitorConfigDataPathBackUp());

View File

@@ -0,0 +1,100 @@
/*
* 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.util;
import java.util.ArrayList;
import java.util.List;
import org.apache.rocketmq.client.trace.TraceBean;
import org.apache.rocketmq.client.trace.TraceConstants;
import org.apache.rocketmq.client.trace.TraceContext;
import org.apache.rocketmq.client.trace.TraceType;
import org.apache.rocketmq.common.message.MessageType;
import static org.apache.rocketmq.client.trace.TraceType.Pub;
public class MsgTraceDecodeUtil {
public static List<TraceContext> decoderFromTraceDataString(String traceData) {
List<TraceContext> resList = new ArrayList<TraceContext>();
if (traceData == null || traceData.length() <= 0) {
return resList;
}
String[] contextList = traceData.split(String.valueOf(TraceConstants.FIELD_SPLITOR));
for (String context : contextList) {
String[] line = context.split(String.valueOf(TraceConstants.CONTENT_SPLITOR));
if (line[0].equals(Pub.name())) {
TraceContext pubContext = new TraceContext();
pubContext.setTraceType(Pub);
pubContext.setTimeStamp(Long.parseLong(line[1]));
pubContext.setRegionId(line[2]);
pubContext.setGroupName(line[3]);
TraceBean bean = new TraceBean();
bean.setTopic(line[4]);
bean.setMsgId(line[5]);
bean.setTags(line[6]);
bean.setKeys(line[7]);
bean.setStoreHost(line[8]);
bean.setBodyLength(Integer.parseInt(line[9]));
pubContext.setCostTime(Integer.parseInt(line[10]));
bean.setMsgType(MessageType.values()[Integer.parseInt(line[11])]);
if (line.length == 13) {
pubContext.setSuccess(Boolean.parseBoolean(line[12]));
} else if (line.length == 14) {
bean.setOffsetMsgId(line[12]);
pubContext.setSuccess(Boolean.parseBoolean(line[13]));
}
pubContext.setTraceBeans(new ArrayList<TraceBean>(1));
pubContext.getTraceBeans().add(bean);
resList.add(pubContext);
} else if (line[0].equals(TraceType.SubBefore.name())) {
TraceContext subBeforeContext = new TraceContext();
subBeforeContext.setTraceType(TraceType.SubBefore);
subBeforeContext.setTimeStamp(Long.parseLong(line[1]));
subBeforeContext.setRegionId(line[2]);
subBeforeContext.setGroupName(line[3]);
subBeforeContext.setRequestId(line[4]);
TraceBean bean = new TraceBean();
bean.setMsgId(line[5]);
bean.setRetryTimes(Integer.parseInt(line[6]));
bean.setKeys(line[7]);
subBeforeContext.setTraceBeans(new ArrayList<TraceBean>(1));
subBeforeContext.getTraceBeans().add(bean);
resList.add(subBeforeContext);
} else if (line[0].equals(TraceType.SubAfter.name())) {
TraceContext subAfterContext = new TraceContext();
subAfterContext.setTraceType(TraceType.SubAfter);
subAfterContext.setRequestId(line[1]);
TraceBean bean = new TraceBean();
bean.setMsgId(line[2]);
bean.setKeys(line[5]);
subAfterContext.setTraceBeans(new ArrayList<TraceBean>(1));
subAfterContext.getTraceBeans().add(bean);
subAfterContext.setCostTime(Integer.parseInt(line[3]));
subAfterContext.setSuccess(Boolean.parseBoolean(line[4]));
if (line.length >= 7) {
// add the context type
subAfterContext.setContextCode(Integer.parseInt(line[6]));
}
resList.add(subAfterContext);
}
}
return resList;
}
}

View File

@@ -13,4 +13,6 @@ rocketmq.config.isVIPChannel=
#rocketmq-console's data path:dashboard/monitor
rocketmq.config.dataPath=/tmp/rocketmq-console/data
#set it false if you don't want use dashboard.default true
rocketmq.config.enableDashBoardCollect=true
rocketmq.config.enableDashBoardCollect=true
#set the message track trace topic if you don't want use the default one
rocketmq.config.msgTrackTopicName=

View File

@@ -109,6 +109,7 @@
<script type="text/javascript" src="src/consumer.js?timestamp=6"></script>
<script type="text/javascript" src="src/producer.js"></script>
<script type="text/javascript" src="src/message.js"></script>
<script type="text/javascript" src="src/messageTrace.js"></script>
<script type="text/javascript" src="src/ops.js?timestamp=7"></script>
<script type="text/javascript" src="src/remoteApi/remoteApi.js"></script>
<script type="text/javascript" src="vendor/preLoading/main.js"></script>

View File

@@ -145,6 +145,9 @@ app.config(['$routeProvider', '$httpProvider','$cookiesProvider','getDictNamePro
}).when('/message', {
templateUrl: 'view/pages/message.html',
controller:'messageController'
}).when('/messageTrace', {
templateUrl: 'view/pages/messageTrace.html',
controller:'messageTraceController'
}).when('/ops', {
templateUrl: 'view/pages/ops.html',
controller:'opsController'

View File

@@ -20,6 +20,7 @@ var en = {
"CONSUMER":"Consumer",
"PRODUCER":"Producer",
"MESSAGE":"Message",
"MESSAGETRACE":"MessageTrace",
"COMMIT": "Commit",
"OPERATION": "Operation",
"ADD": "Add",

View File

@@ -21,6 +21,7 @@ var zh = {
"CONSUMER":"消费者",
"PRODUCER":"生产者",
"MESSAGE":"消息",
"MESSAGETRACE":"消息轨迹",
"OPERATION": "操作",
"ADD": "新增",
"UPDATE": "更新",

View File

@@ -0,0 +1,121 @@
/*
* 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.
*/
var module = app;
module.controller('messageTraceController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
$scope.allTopicList = [];
$scope.selectedTopic =[];
$scope.key ="";
$scope.messageId ="";
$scope.queryMessageByTopicAndKeyResult=[];
$scope.queryMessageByMessageIdResult={};
$scope.queryMessageTraceListsByTopicAndKeyResult=[];
$http({
method: "GET",
url: "topic/list.query"
}).success(function (resp) {
if(resp.status ==0){
$scope.allTopicList = resp.data.topicList.sort();
console.log($scope.allTopicList);
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
$scope.timepickerBegin = moment().subtract(1, 'hour').format('YYYY-MM-DD HH:mm');
$scope.timepickerEnd = moment().add(1,'hour').format('YYYY-MM-DD HH:mm');
$scope.timepickerOptions ={format: 'YYYY-MM-DD HH:mm', showClear: true};
$scope.queryMessageByTopicAndKey = function () {
console.log($scope.selectedTopic);
console.log($scope.key);
$http({
method: "GET",
url: "message/queryMessageByTopicAndKey.query",
params: {
topic: $scope.selectedTopic,
key:$scope.key
}
}).success(function (resp) {
if (resp.status == 0) {
console.log(resp);
$scope.queryMessageByTopicAndKeyResult = resp.data;
console.log($scope.queryMessageByTopicAndKeyResult);
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
$scope.queryMessageByMessageId = function (messageId,topic) {
$http({
method: "GET",
url: "messageTrace/viewMessage.query",
params: {
msgId: messageId,
topic:topic
}
}).success(function (resp) {
if (resp.status == 0) {
console.log(resp);
$scope.queryMessageByMessageIdResult = resp.data;
console.log($scope.queryMessageByTopicAndKeyResult);
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
$scope.queryMessageTraceByMessageId = function (messageId,topic) {
$http({
method: "GET",
url: "messageTrace/viewMessageTraceDetail.query",
params: {
msgId: messageId,
topic:topic
}
}).success(function (resp) {
if (resp.status == 0) {
console.log(resp);
ngDialog.open({
template: 'messageTraceDetailViewDialog',
controller: 'messageTraceDetailViewDialogController',
data:resp.data
});
}else {
Notification.error({message: resp.errMsg, delay: 2000});
}
});
};
}]);
module.controller('messageTraceDetailViewDialogController',['$scope', 'ngDialog', '$http','Notification', function ($scope, ngDialog, $http,Notification) {
$scope.showExceptionDesc = function (errmsg) {
if(errmsg == null){
errmsg = "Don't have Exception"
}
ngDialog.open({
template: 'operationResultDialog',
data:{
result:errmsg
}
});
};
}]
);

View File

@@ -17,6 +17,7 @@
<li ng-class="path =='consumer' ? 'active':''"><a ng-href="#/consumer">{{'CONSUMER' | translate}}</a></li>
<li ng-class="path =='producer' ? 'active':''"><a ng-href="#/producer">{{'PRODUCER' | translate}}</a></li>
<li ng-class="path =='message' ? 'active':''"><a ng-href="#/message">{{'MESSAGE' | translate}}</a></li>
<li ng-class="path =='messageTrace' ? 'active':''"><a ng-href="#/messageTrace">{{'MESSAGETRACE' | translate}}</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">

View File

@@ -0,0 +1,182 @@
<!--
~ 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.
-->
<div class="container-fluid" id="deployHistoryList">
<div class="modal-body">
<div ng-cloak="" class="tabsdemoDynamicHeight">
<md-content>
<md-tabs md-dynamic-height="" md-border-bottom="">
<md-tab label="Message Key">
<md-content class="md-padding" style="min-height:600px">
<h5 class="md-display-5">Only Return 64 Messages</h5>
<form class="form-inline pull-left col-sm-12">
<div class="form-group">
<label>Topic:</label>
</div>
<div class="form-group">
<div style="width: 300px">
<select name="mySelectTopic" chosen
ng-model="selectedTopic"
ng-options="item for item in allTopicList"
required>
<option value=""></option>
</select>
</div>
</div>
<div class="form-group">
<label>Key:</label>
<input class="form-control" style="width: 450px" type="text" ng-model="key"
required/>
</div>
<button type="button" class="btn btn-raised btn-sm btn-primary" data-toggle="modal"
ng-click="queryMessageByTopicAndKey()">
<span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
</button>
</form>
<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">Operation</th>
</tr>
<tr ng-repeat="item in queryMessageByTopicAndKeyResult">
<td class="text-center">{{item.msgId}}</td>
<td class="text-center">{{item.properties.TAGS}}</td>
<td class="text-center">{{item.properties.KEYS}}</td>
<td class="text-center">{{item.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}
</td>
<td class="text-center">
<button class="btn btn-raised btn-sm btn-primary" type="button"
ng-click="queryMessageTraceByMessageId(item.msgId,item.topic)">Message Trace Detail
</button>
</td>
</tr>
</table>
</md-content>
</md-tab>
<md-tab label="Message ID">
<h5 class="md-display-5">topic can't be empty if you producer client version>=v3.5.8</h5>
<md-content class="md-padding" style="min-height:600px">
<form class="form-inline pull-left col-sm-12">
<div class="form-group">
<label>Topic:</label>
</div>
<div class="form-group ">
<div style="width: 300px">
<select name="mySelectTopic" chosen
ng-model="selectedTopic"
ng-options="item for item in allTopicList"
required>
<option value=""></option>
</select>
</div>
</div>
<div class="form-group">
<label>MessageId:</label>
<input class="form-control" style="width: 450px" type="text" ng-model="messageId"
required/>
</div>
<button type="button" class="btn btn-raised btn-sm btn-primary" data-toggle="modal"
ng-click="queryMessageByMessageId(messageId,selectedTopic)">
<span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
</button>
</form>
<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">Operation</th>
</tr>
<tr ng-repeat="item in queryMessageByMessageIdResult">
<td class="text-center">{{item.msgId}}</td>
<td class="text-center">{{item.properties.TAGS}}</td>
<td class="text-center">{{item.properties.KEYS}}</td>
<td class="text-center">{{item.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}
</td>
<td class="text-center">
<button class="btn btn-raised btn-sm btn-primary" type="button"
ng-click="queryMessageTraceByMessageId(item.msgId,item.topic)">Message Trace Detail
</button>
</td>
</tr>
</table>
</md-content>
</md-tab>
</md-tabs>
</md-content>
</div>
</div>
</div>
<script type="text/ng-template" id="messageTraceDetailViewDialog">
<md-content class="md-padding">
<div>
<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">costTime</th>
<th class="text-center">status</th>
<th class="text-center">traceType</th>
</tr>
<tr ng-repeat="item in ngDialogData">
<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.costTime}}ms</td>
<td class="text-center">{{item.status}}</td>
<th class="text-center">{{item.msgType}}</th>
</tr>
</table>
</div>
</md-content>
<div class="ngdialog-buttons">
<button type="button" class="ngdialog-button ngdialog-button-secondary"
ng-click="closeThisDialog('Cancel')">{{ 'CLOSE' | translate }}
</button>
</div>
</script>
<script type="text/ng-template" id="operationResultDialog">
<div class="modal-header">
<h4 class="modal-title">Result</h4>
</div>
<div class="modal-body ">
<form class="form-horizontal" novalidate>
{{ngDialogData.result}}
</form>
</div>
<div class="modal-footer">
<div class="ngdialog-buttons">
<button type="button" class="ngdialog-button ngdialog-button-secondary"
ng-click="closeThisDialog('Cancel')">{{ 'CLOSE' | translate }}
</button>
</div>
</div>
</script>