This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new a7e26555 fix(trace): parse batched contexts and EndTransaction state 
(#1504)
a7e26555 is described below

commit a7e265558a0f83a800de2062d93ce30651977dd8
Author: majialong <[email protected]>
AuthorDate: Tue Aug 11 20:30:42 2026 +0800

    fix(trace): parse batched contexts and EndTransaction state (#1504)
---
 .../provider/apache/RocketMQMessageProvider.java   | 32 ++++++++-------
 .../apache/RocketMQMessageProviderTest.java        | 45 +++++++++++++---------
 2 files changed, 44 insertions(+), 33 deletions(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProvider.java
 
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProvider.java
index 64ad563c..01433857 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProvider.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProvider.java
@@ -20,6 +20,7 @@ import org.apache.rocketmq.client.QueryResult;
 import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
 import org.apache.rocketmq.client.consumer.PullResult;
 import org.apache.rocketmq.client.consumer.PullStatus;
+import org.apache.rocketmq.client.trace.TraceConstants;
 import org.apache.rocketmq.common.message.MessageDecoder;
 import org.apache.rocketmq.common.message.MessageExt;
 import org.apache.rocketmq.common.message.MessageId;
@@ -69,7 +70,6 @@ import java.util.Set;
 public class RocketMQMessageProvider implements MessageProvider {
 
     private static final String TRACE_TOPIC = "RMQ_SYS_TRACE_TOPIC";
-    private static final char FIELD_SEPARATOR = '\u0001';
     private static final int KEY_QUERY_MAX = 64;
     private static final int TRACE_QUERY_MAX = 64;
     private static final int DEFAULT_TOPIC_LIMIT = 200;
@@ -334,8 +334,9 @@ public class RocketMQMessageProvider implements 
MessageProvider {
     }
 
     /**
-     * Parse a trace message body. Each line is one trace context whose fields 
are separated by
-     * the SOH character ({@code \u0001}); the first field is the trace type.
+     * Parse a trace message body. Trace contexts are separated by STX ({@code 
\u0002}) and the
+     * fields in each context are separated by SOH ({@code \u0001}); the first 
field is the trace
+     * type.
      */
     private void parseTraceBody(byte[] body, String targetMsgId, 
List<TraceNodeVO> nodes,
                                 List<ConsumerStatusVO> consumerStatus) {
@@ -343,17 +344,17 @@ public class RocketMQMessageProvider implements 
MessageProvider {
             return;
         }
         String data = new String(body, StandardCharsets.UTF_8);
-        for (String line : data.split("\n")) {
-            if (!StringUtils.hasText(line)) {
+        for (String context : 
data.split(String.valueOf(TraceConstants.FIELD_SPLITOR))) {
+            if (!StringUtils.hasText(context)) {
                 continue;
             }
-            String[] fields = line.split(String.valueOf(FIELD_SEPARATOR), -1);
+            String[] fields = 
context.split(String.valueOf(TraceConstants.CONTENT_SPLITOR), -1);
             if (fields.length == 0) {
                 continue;
             }
             String traceType = fields[0].trim();
-            // The message id column differs by trace type: Pub/EndTransaction 
encode the trace bean
-            // (msgId at index 5), while SubAfter in RocketMQ 5.3.3 places 
msgId at index 2.
+            // The message ID column differs by trace type: Pub/EndTransaction 
place msgId at
+            // index 5, while SubAfter places it at index 2 in RocketMQ 5.5.0.
             int msgIdIndex = "SubAfter".equals(traceType) ? 2 : 5;
             if (!targetMsgId.equals(field(fields, msgIdIndex))) {
                 continue;
@@ -375,12 +376,13 @@ public class RocketMQMessageProvider implements 
MessageProvider {
                         break;
                 }
             } catch (Exception e) {
-                log.debug("Skipping unparseable trace line: {}", 
e.getMessage());
+                log.debug("Skipping unparseable trace context: {}", 
e.getMessage());
             }
         }
     }
 
-    // Pub layout (RocketMQ 5.3.3 TraceDataEncoder): type, time, region, 
group, topic, msgId,
+    // Pub layout (RocketMQ 5.5.0 TraceDataEncoder):
+    //             type, time, region, group, topic, msgId,
     //             tags, keys, storeHost, bodyLength, costTime, msgType, 
offsetMsgId, isSuccess
     private TraceNodeVO buildProduceNode(String[] f) {
         return TraceNodeVO.builder()
@@ -392,7 +394,8 @@ public class RocketMQMessageProvider implements 
MessageProvider {
                 .build();
     }
 
-    // SubAfter layout (RocketMQ 5.3.3 TraceDataEncoder): type, requestId, 
msgId, costTime,
+    // SubAfter layout (RocketMQ 5.5.0 TraceDataEncoder):
+    //                type, requestId, msgId, costTime,
     //                isSuccess, keys, contextCode, timeStamp, groupName. The 
trailing
     //                timeStamp/groupName columns may be absent when the trace 
has no region info,
     //                so lookups tolerate short lines.
@@ -415,15 +418,16 @@ public class RocketMQMessageProvider implements 
MessageProvider {
                 .build();
     }
 
-    // EndTransaction layout (RocketMQ 5.3.3): type, time, region, group, 
topic, msgId, tags,
-    //                     keys, storeHost, bodyLength, costTime, msgType, 
transactionId, txState
+    // EndTransaction layout (RocketMQ 5.5.0 TraceDataEncoder):
+    //                     type, time, region, group, topic, msgId, tags,
+    //                     keys, storeHost, msgType, transactionId, txState, 
fromTransactionCheck
     private TraceNodeVO buildTransactionNode(String[] f) {
         return TraceNodeVO.builder()
                 .title("endTransaction")
                 .timestamp(parseLong(field(f, 1)))
                 .status("finish")
                 .costTime(0L)
-                .description("group=" + field(f, 3) + ", transactionState=" + 
field(f, 13))
+                .description("group=" + field(f, 3) + ", transactionState=" + 
field(f, 11))
                 .build();
     }
 
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProviderTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProviderTest.java
index cc57db38..159572c1 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProviderTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQMessageProviderTest.java
@@ -20,6 +20,7 @@ import org.apache.rocketmq.client.QueryResult;
 import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
 import org.apache.rocketmq.client.consumer.PullResult;
 import org.apache.rocketmq.client.consumer.PullStatus;
+import org.apache.rocketmq.client.trace.TraceConstants;
 import org.apache.rocketmq.common.message.MessageExt;
 import org.apache.rocketmq.common.message.MessageQueue;
 import org.apache.rocketmq.studio.cluster.broker.MqAdminExtFactory;
@@ -206,24 +207,20 @@ class RocketMQMessageProviderTest {
         assertThat(record.isBodyTruncated()).isTrue();
     }
 
-    @Test
-    void getMessageTraceParsesPubAndSubAfterPerRocketMq533Layout() throws 
Exception {
-        // Field order follows RocketMQ 5.3.3 TraceDataEncoder: Pub = type, 
time, region, group,
+    void getMessageTraceParsesBatchedPubAndSubAfterContexts() throws Exception 
{
+        // Field order follows RocketMQ 5.5.0 TraceDataEncoder:
+        // Pub = type, time, region, group,
         // topic, msgId, tags, keys, storeHost, bodyLength, costTime, msgType, 
offsetMsgId, isSuccess.
         // SubAfter = type, requestId, msgId, costTime, isSuccess, keys, 
contextCode, timeStamp,
         // groupName.
-        String pub = "Pub" + '' + "1000" + '' + "cn" + '' + "prod-group"
-                + '' + "TopicA" + '' + "msg-123" + '' + "tag1" + '' + 
"key1"
-                + '' + "broker:10911" + '' + "15" + '' + "50" + '' + "0"
-                + '' + "offset-1" + '' + "true";
-        String subAfter = "SubAfter" + '' + "req-1" + '' + "msg-123" + '' + 
"20"
-                + '' + "true" + '' + "key1" + '' + "3" + '' + "3000"
-                + '' + "cons-group";
-        String otherMessage = "SubAfter" + '' + "req-2" + '' + "other-msg" + 
'' + "5"
-                + '' + "false" + '' + "key-other" + '' + "0" + '' + "0"
-                + '' + "other-group";
+        String pub = traceContext("Pub", "1000", "cn", "prod-group", "TopicA", 
"msg-123",
+                "tag1", "key1", "broker:10911", "15", "50", "0", "offset-1", 
"true");
+        String subAfter = traceContext("SubAfter", "req-1", "msg-123", "20", 
"true", "key1",
+                "3", "3000", "cons-group");
+        String otherMessage = traceContext("SubAfter", "req-2", "other-msg", 
"5", "false",
+                "key-other", "0", "0", "other-group");
         MessageExt traceMessage = new MessageExt();
-        traceMessage.setBody(String.join("\n", pub, subAfter, 
otherMessage).getBytes(StandardCharsets.UTF_8));
+        traceMessage.setBody(traceBody(pub, subAfter, 
otherMessage).getBytes(StandardCharsets.UTF_8));
         QueryResult queryResult = new QueryResult(0L, List.of(traceMessage));
         when(adminExt.queryMessage(anyString(), anyString(), anyInt(), 
anyLong(), anyLong()))
                 .thenReturn(queryResult);
@@ -252,10 +249,8 @@ class RocketMQMessageProviderTest {
 
     @Test
     void getMessageTraceParsesEndTransactionState() throws Exception {
-        String body = "EndTransaction" + '' + "2000" + '' + "cn" + '' + 
"tx-group"
-                + '' + "TopicA" + '' + "msg-tx" + '' + "tag2" + '' + "key2"
-                + '' + "broker:10911" + '' + "10" + '' + "40" + '' + "0"
-                + '' + "tx-1" + '' + "COMMIT_MESSAGE";
+        String body = traceBody(traceContext("EndTransaction", "2000", "cn", 
"tx-group", "TopicA",
+                "msg-tx", "tag2", "key2", "broker:10911", "0", "tx-1", 
"COMMIT_MESSAGE", "false"));
         MessageExt traceMessage = new MessageExt();
         traceMessage.setBody(body.getBytes(StandardCharsets.UTF_8));
         QueryResult queryResult = new QueryResult(0L, List.of(traceMessage));
@@ -267,7 +262,10 @@ class RocketMQMessageProviderTest {
         assertThat(record.getNodes()).hasSize(1);
         TraceNodeVO transaction = record.getNodes().get(0);
         assertThat(transaction.getTitle()).isEqualTo("endTransaction");
-        
assertThat(transaction.getDescription()).contains("tx-group").contains("COMMIT_MESSAGE");
+        assertThat(transaction.getDescription())
+                .contains("tx-group")
+                .contains("transactionState=COMMIT_MESSAGE")
+                .doesNotContain("transactionState=false");
         assertThat(record.getConsumerStatus()).isEmpty();
     }
 
@@ -283,4 +281,13 @@ class RocketMQMessageProviderTest {
 
         verify(queryHistoryService, never()).recordTraceQuery(anyString(), 
anyString(), any(), anyInt(), anyInt());
     }
+
+    private static String traceContext(String... fields) {
+        return String.join(String.valueOf(TraceConstants.CONTENT_SPLITOR), 
fields);
+    }
+
+    private static String traceBody(String... contexts) {
+        return String.join(String.valueOf(TraceConstants.FIELD_SPLITOR), 
contexts)
+                + TraceConstants.FIELD_SPLITOR;
+    }
 }

Reply via email to