diff --git a/src/ros2_medkit_log_bridge/README.md b/src/ros2_medkit_log_bridge/README.md index 1c2098eb..6557a906 100644 --- a/src/ros2_medkit_log_bridge/README.md +++ b/src/ros2_medkit_log_bridge/README.md @@ -94,6 +94,7 @@ ros2 launch ros2_medkit_log_bridge log_bridge.launch.py | `include_only_nodes` | `[]` | if set, only promote nodes whose FQN matches | | `max_tracked_nodes` | `512` | cap on per-node reporters; least-recently-used nodes evicted past this | | `report_cooldown_sec` | `5.0` | per-fault_code forward debounce; `0.0` disables | +| `exclude_medkit_stack` | `true` | skip medkit's own infrastructure nodes (`fault_manager`, gateway, the other bridges) so their logs do not feed back as faults; set `false` to debug medkit's own logs | `exclude_nodes` / `include_only_nodes` match as **unanchored substrings** against the node FQN: `planner` matches `/planner_server` and diff --git a/src/ros2_medkit_log_bridge/config/log_bridge.yaml b/src/ros2_medkit_log_bridge/config/log_bridge.yaml index 951265c7..991d6600 100644 --- a/src/ros2_medkit_log_bridge/config/log_bridge.yaml +++ b/src/ros2_medkit_log_bridge/config/log_bridge.yaml @@ -21,3 +21,7 @@ log_bridge: # forwarded immediately; the same code within the window is suppressed. # 0.0 disables. Tames ERROR/FATAL floods, which bypass the per-node filter. report_cooldown_sec: 5.0 + # Skip the medkit stack's own infrastructure nodes (fault_manager, gateway, + # the other bridges). Their /rosout lines would otherwise feed back as + # faults about medkit itself. Set false only to debug medkit's own logs. + exclude_medkit_stack: true diff --git a/src/ros2_medkit_log_bridge/include/ros2_medkit_log_bridge/log_bridge_node.hpp b/src/ros2_medkit_log_bridge/include/ros2_medkit_log_bridge/log_bridge_node.hpp index a8f9e850..be8d18e7 100644 --- a/src/ros2_medkit_log_bridge/include/ros2_medkit_log_bridge/log_bridge_node.hpp +++ b/src/ros2_medkit_log_bridge/include/ros2_medkit_log_bridge/log_bridge_node.hpp @@ -63,6 +63,11 @@ class LogBridgeNode : public rclcpp::Node { /// include/exclude lists. Exposed for unit testing. bool node_is_eligible(const std::string & source_id) const; + /// Whether a logger name belongs to the medkit stack's own infrastructure + /// (fault_manager, gateway, the other bridges). Matched on the raw logger + /// name so namespaced nodes are caught. Exposed for unit testing. + static bool is_medkit_stack_logger(const std::string & logger_name); + /// Map an rcl_interfaces/msg/Log.name (a logger name, e.g. "bt_navigator" or /// "controller_manager.resource_manager") to the originating node's /// fully-qualified name ("/bt_navigator", "/controller_manager"). The gateway @@ -121,6 +126,10 @@ class LogBridgeNode : public rclcpp::Node { int max_tracked_nodes_; double report_cooldown_sec_; std::string own_node_name_; + // When true (default), never promote logs from the medkit stack's own + // infrastructure nodes (fault_manager, gateway, the other bridges) - else + // their /rosout lines feed back into faults about medkit itself. + bool exclude_medkit_stack_; }; } // namespace ros2_medkit_log_bridge diff --git a/src/ros2_medkit_log_bridge/src/log_bridge_node.cpp b/src/ros2_medkit_log_bridge/src/log_bridge_node.cpp index e9bcff08..4ac2100e 100644 --- a/src/ros2_medkit_log_bridge/src/log_bridge_node.cpp +++ b/src/ros2_medkit_log_bridge/src/log_bridge_node.cpp @@ -88,6 +88,7 @@ void LogBridgeNode::load_parameters() { if (report_cooldown_sec_ < 0.0) { report_cooldown_sec_ = 0.0; } + exclude_medkit_stack_ = declare_parameter("exclude_medkit_stack", true); } void LogBridgeNode::log_callback(const rcl_interfaces::msg::Log::ConstSharedPtr & msg) { @@ -99,6 +100,12 @@ void LogBridgeNode::log_callback(const rcl_interfaces::msg::Log::ConstSharedPtr if (source_id == own_node_name_ || source_id == "/log_bridge") { return; } + // Skip the medkit stack's own infrastructure nodes (else fault_manager's own + // /rosout lines feed back as faults about medkit). Matched on the raw logger + // name, which keeps the namespace (node_source_id collapses it to the ns). + if (exclude_medkit_stack_ && is_medkit_stack_logger(msg->name)) { + return; + } if (!node_is_eligible(source_id)) { return; } @@ -147,6 +154,15 @@ std::string LogBridgeNode::node_source_id(const std::string & log_name) { return node; } +bool LogBridgeNode::is_medkit_stack_logger(const std::string & logger_name) { + // Match medkit's own infrastructure against the raw logger name so a + // namespaced node (e.g. "robot1.fault_manager") is still caught, even though + // node_source_id would collapse its FQN to the namespace. + static const std::vector kMedkitStack = {"fault_manager", "ros2_medkit_gateway", "diagnostic_bridge", + "action_status_bridge"}; + return contains_substr(kMedkitStack, logger_name); +} + bool LogBridgeNode::node_is_eligible(const std::string & source_id) const { if (!include_only_nodes_.empty() && !contains_substr(include_only_nodes_, source_id)) { return false; diff --git a/src/ros2_medkit_log_bridge/test/test_log_bridge.cpp b/src/ros2_medkit_log_bridge/test/test_log_bridge.cpp index 37aafabe..173d2a58 100644 --- a/src/ros2_medkit_log_bridge/test/test_log_bridge.cpp +++ b/src/ros2_medkit_log_bridge/test/test_log_bridge.cpp @@ -219,6 +219,23 @@ TEST_F(LogBridgeTest, NodeEligibility_IncludeOnlySubstring) { EXPECT_FALSE(node->node_is_eligible("/amcl")); } +// Matched on the raw logger name (msg->name), which is what log_callback feeds +// it - including the namespaced form "robot1.fault_manager" that node_source_id +// would otherwise collapse to "/robot1". +TEST_F(LogBridgeTest, MedkitStackLogger_MatchesOwnInfra) { + EXPECT_TRUE(LogBridgeNode::is_medkit_stack_logger("fault_manager")); + EXPECT_TRUE(LogBridgeNode::is_medkit_stack_logger("robot1.fault_manager")); + EXPECT_TRUE(LogBridgeNode::is_medkit_stack_logger("ros2_medkit_gateway")); + EXPECT_TRUE(LogBridgeNode::is_medkit_stack_logger("diagnostic_bridge")); + EXPECT_TRUE(LogBridgeNode::is_medkit_stack_logger("action_status_bridge")); +} + +TEST_F(LogBridgeTest, MedkitStackLogger_AllowsApplicationNodes) { + EXPECT_FALSE(LogBridgeNode::is_medkit_stack_logger("bt_navigator")); + EXPECT_FALSE(LogBridgeNode::is_medkit_stack_logger("robot1.controller_server")); + EXPECT_FALSE(LogBridgeNode::is_medkit_stack_logger("amcl")); +} + // --- source_id normalization to node FQN (entity association) --- TEST_F(LogBridgeTest, NodeSourceId_PrependsLeadingSlash) {