use cheetah_string::CheetahString;
use rocketmq_model::common::message::message_enum::MessageType;
use rocketmq_model::common::message::message_ext::MessageExt;
use rocketmq_transport::api::NetworkUtil;
use crate::trace::trace_data_encoder::TraceDataEncoder;
static LOCAL_ADDRESS: std::sync::LazyLock<CheetahString> = std::sync::LazyLock::new(|| {
NetworkUtil::get_local_address()
.map(CheetahString::from_string)
.unwrap_or_else(CheetahString::empty)
});
#[derive(Debug, Clone)]
pub struct TraceView {
pub msg_id: CheetahString,
pub tags: CheetahString,
pub keys: CheetahString,
pub store_host: CheetahString,
pub client_host: CheetahString,
pub cost_time: i64,
pub msg_type: Option<MessageType>,
pub offset_msg_id: CheetahString,
pub time_stamp: i64,
pub born_time: i64,
pub topic: CheetahString,
pub group_name: CheetahString,
pub status: CheetahString,
}
impl Default for TraceView {
fn default() -> Self {
TraceView {
msg_id: CheetahString::default(),
tags: CheetahString::default(),
keys: CheetahString::default(),
store_host: LOCAL_ADDRESS.clone(),
client_host: LOCAL_ADDRESS.clone(),
cost_time: 0,
msg_type: None,
offset_msg_id: CheetahString::default(),
time_stamp: 0,
born_time: 0,
topic: CheetahString::default(),
group_name: CheetahString::default(),
status: CheetahString::default(),
}
}
}
impl TraceView {
pub fn decode_from_trace_trans_data(key: &str, message_ext: &MessageExt) -> Vec<TraceView> {
let Some(body) = message_ext.body() else {
return Vec::new();
};
if body.is_empty() {
return Vec::new();
}
let message_body = String::from_utf8_lossy(&body);
TraceDataEncoder::decoder_from_trace_data_string(&message_body)
.into_iter()
.filter_map(|context| {
let trace_bean = context.trace_beans.as_ref()?.first()?;
if trace_bean.msg_id != key {
return None;
}
Some(TraceView {
msg_id: trace_bean.msg_id.clone(),
tags: trace_bean.tags.clone(),
keys: trace_bean.keys.clone(),
store_host: trace_bean.store_host.clone(),
client_host: CheetahString::from_string(message_ext.born_host().to_string()),
cost_time: i64::from(context.cost_time),
msg_type: trace_bean.msg_type,
offset_msg_id: trace_bean.offset_msg_id.clone(),
time_stamp: i64::try_from(context.time_stamp).unwrap_or(i64::MAX),
born_time: 0,
topic: trace_bean.topic.clone(),
group_name: context.group_name.clone(),
status: if context.is_success {
CheetahString::from_static_str("success")
} else {
CheetahString::from_static_str("failed")
},
})
})
.collect()
}
}
#[cfg(test)]
mod tests {
use bytes::Bytes;
use cheetah_string::CheetahString;
use rocketmq_model::common::message::message_ext::MessageExt;
use rocketmq_model::common::message::MessageTrait;
use super::*;
use crate::trace::trace_bean::TraceBean;
use crate::trace::trace_context::TraceContext;
use crate::trace::trace_data_encoder::TraceDataEncoder;
use crate::trace::trace_type::TraceType;
#[test]
fn trace_view_default_values() {
let trace_view = TraceView::default();
assert_eq!(trace_view.msg_id, CheetahString::default());
assert_eq!(trace_view.tags, CheetahString::default());
assert_eq!(trace_view.keys, CheetahString::default());
assert_eq!(trace_view.store_host, LOCAL_ADDRESS.clone());
assert_eq!(trace_view.client_host, LOCAL_ADDRESS.clone());
assert_eq!(trace_view.cost_time, 0);
assert_eq!(trace_view.msg_type, None);
assert_eq!(trace_view.offset_msg_id, CheetahString::default());
assert_eq!(trace_view.time_stamp, 0);
assert_eq!(trace_view.born_time, 0);
assert_eq!(trace_view.topic, CheetahString::default());
assert_eq!(trace_view.group_name, CheetahString::default());
assert_eq!(trace_view.status, CheetahString::default());
}
#[test]
fn trace_view_with_values() {
let trace_view = TraceView {
msg_id: CheetahString::from("msg_id"),
tags: CheetahString::from("tags"),
keys: CheetahString::from("keys"),
store_host: CheetahString::from("127.0.0.1"),
client_host: CheetahString::from("127.0.0.1"),
cost_time: 1734784743,
msg_type: Some(MessageType::NormalMsg),
offset_msg_id: CheetahString::from("offset_msg_id"),
time_stamp: 1734784743,
born_time: 1734784743,
topic: CheetahString::from("topic"),
group_name: CheetahString::from("group"),
status: CheetahString::from("status"),
};
assert_eq!(trace_view.msg_id, CheetahString::from("msg_id"));
assert_eq!(trace_view.tags, CheetahString::from("tags"));
assert_eq!(trace_view.keys, CheetahString::from("keys"));
assert_eq!(trace_view.store_host, CheetahString::from("127.0.0.1"));
assert_eq!(trace_view.client_host, CheetahString::from("127.0.0.1"));
assert_eq!(trace_view.cost_time, 1734784743);
assert_eq!(trace_view.msg_type, Some(MessageType::NormalMsg));
assert_eq!(trace_view.offset_msg_id, CheetahString::from("offset_msg_id"));
assert_eq!(trace_view.time_stamp, 1734784743);
assert_eq!(trace_view.born_time, 1734784743);
assert_eq!(trace_view.topic, CheetahString::from("topic"));
assert_eq!(trace_view.group_name, CheetahString::from("group"));
assert_eq!(trace_view.status, CheetahString::from("status"));
}
#[test]
fn decode_from_trace_trans_data_matches_java_filter_and_projection() {
let matching_bean = TraceBean {
topic: CheetahString::from("topic_a"),
msg_id: CheetahString::from("msg-a"),
offset_msg_id: CheetahString::from("offset-a"),
tags: CheetahString::from("tag-a"),
keys: CheetahString::from("key-a"),
store_host: CheetahString::from("store-host"),
msg_type: Some(MessageType::NormalMsg),
..Default::default()
};
let other_bean = TraceBean {
msg_id: CheetahString::from("msg-b"),
..matching_bean.clone()
};
let context = TraceContext {
trace_type: Some(TraceType::Pub),
time_stamp: 12345,
group_name: CheetahString::from("group-a"),
cost_time: 17,
is_success: true,
trace_beans: Some(vec![matching_bean, other_bean]),
..Default::default()
};
let transfer = TraceDataEncoder::encoder_from_context_bean(&context).expect("trace context should encode");
let mut message_ext = MessageExt::default();
message_ext.set_body(Bytes::from(transfer.trans_data.to_string()));
let views = TraceView::decode_from_trace_trans_data("msg-a", &message_ext);
assert_eq!(views.len(), 1);
let view = &views[0];
assert_eq!(view.msg_id, CheetahString::from("msg-a"));
assert_eq!(view.topic, CheetahString::from("topic_a"));
assert_eq!(view.group_name, CheetahString::from("group-a"));
assert_eq!(view.status, CheetahString::from("success"));
assert_eq!(view.cost_time, 17);
assert_eq!(view.time_stamp, 12345);
assert_eq!(view.offset_msg_id, CheetahString::from("offset-a"));
assert_eq!(
view.client_host,
CheetahString::from_string(message_ext.born_host().to_string())
);
assert!(TraceView::decode_from_trace_trans_data("missing", &message_ext).is_empty());
}
}