Class LogManager
Defined in File log_manager.hpp
Class Documentation
-
class LogManager
Manages log collection via the default ring-buffer backend.
Pure C++ application service. Middleware-side ingestion is performed by an injected LogSource adapter (typically Ros2LogSource) that subscribes to /rosout and converts each message into a neutral LogEntry. The manager body keeps its ring-buffer storage, per-entity config map, monotonic id counter, the plugin observer pattern, and the manages_ingestion short-circuit. None of those depend on ROS types.
Plugin integration (two modes):
Observer mode (default): If a LogProvider plugin is registered, get_logs() and get_config() delegate to it. on_log_entry() is called on ALL LogProvider observers for every LogEntry produced by the source. Observers returning true suppress ring-buffer storage.
Full ingestion (manages_ingestion() == true): The primary LogProvider owns the entire log pipeline. LogManager never calls source->start(), so no subscription is created. All queries and config operations delegate to the plugin.
FQN normalization:
entity.fqn from the entity cache has a leading ‘/’ (e.g. “/powertrain/engine/temp_sensor”)
Source-emitted entries DO NOT have a leading ‘/’ (rcl_node_get_logger_name convention)
Callers pass raw FQNs from entity.fqn; LogManager strips leading ‘/’ internally.
Public Types
-
using LogSink = std::function<void(int level, std::string_view message)>
Sink for internal diagnostics (plugin provider exceptions, etc.). Keeps LogManager middleware-neutral while preserving observability when the gateway adapter forwards to /rosout. May be null in tests.
-
using NodeToEntityFn = std::function<std::string(const std::string&)>
Callback that maps a logger FQN to a manifest entity ID. Returns empty string if the FQN cannot be resolved.
Public Functions
Construct LogManager.
If the primary LogProvider’s manages_ingestion() returns true, source->start() is never called, so no subscription is created. Otherwise the source is started with on_log_entry() as the entry-point callback.
- Parameters:
source – LogSource adapter (typically Ros2LogSource). Manager takes shared ownership.
provider_registry – LogProviderRegistry port for LogProvider lookup (typically the PluginManager). May be nullptr - equivalent to “no plugins registered”.
max_buffer_size – Ring buffer size per node (override for unit testing)
log_sink – Callback for internal diagnostics. The gateway passes an adapter that forwards to RCLCPP_WARN/ERROR; unit tests may capture calls or pass nullptr.
-
~LogManager()
-
LogManager(const LogManager&) = delete
-
LogManager &operator=(const LogManager&) = delete
-
LogManager(LogManager&&) = delete
-
LogManager &operator=(LogManager&&) = delete
-
tl::expected<json, std::string> get_logs(const std::vector<std::string> &node_fqns, bool prefix_match, const std::string &min_severity, const std::string &context_filter, const std::string &entity_id)
Query log entries for a set of node FQNs.
If a LogProvider plugin is registered, delegates to it. Otherwise uses the local ring buffer.
- Parameters:
node_fqns – Node FQNs from entity.fqn (WITH leading ‘/’ - normalized internally)
prefix_match – If true, match all buffered nodes whose name starts with the given prefix (used for Component queries). If false, exact match (App queries).
min_severity – Additional severity filter from query parameter. Empty = no override.
context_filter – Substring filter applied to log entry’s name (logger name). Empty = no filter.
entity_id – Entity ID for config lookup. Empty = use defaults.
- Returns:
JSON array of LogEntry objects sorted by id ascending, capped at entity config max_entries.
-
tl::expected<LogConfig, std::string> get_config(const std::string &entity_id) const
Get current log configuration for entity (returns defaults if unconfigured)
-
std::string update_config(const std::string &entity_id, const std::optional<std::string> &severity_filter, const std::optional<size_t> &max_entries)
Update log configuration for an entity.
- Returns:
Empty string on success, error message on validation failure
-
void add_log_entry(const std::string &entity_id, const std::string &severity, const std::string &message, const nlohmann::json &metadata)
Programmatically add a log entry (e.g. from trigger log_settings)
Creates a LogEntry and pushes it to the internal ring buffer using the same path as on_log_entry(). If a ResourceChangeNotifier is set, emits a “logs” CREATED notification so triggers can observe log changes.
- Parameters:
entity_id – Entity to associate the log with (used as logger name)
severity – SOVD severity string (debug, info, warning, error, fatal)
message – Human-readable log message
metadata – Additional JSON metadata stored in the message (appended)
-
void set_notifier(ResourceChangeNotifier *notifier)
Set the ResourceChangeNotifier for emitting log change events. Called by GatewayNode after both LogManager and the notifier are available.
-
void set_node_to_entity_resolver(NodeToEntityFn resolver)
Set the node-to-entity resolver for trigger notifications. When set, on_log_entry() resolves logger names to manifest entity IDs before notifying the ResourceChangeNotifier.
-
void inject_entry_for_testing(LogEntry entry)
Inject a log entry directly into the ring buffer (bypasses the source)
Used by unit tests to populate the buffer without a live source feed. In production the buffer is populated exclusively by source-emitted entries.
-
std::size_t dropped_entries_count() const noexcept
Total number of log entries dropped because the per-node-buffer cap (max_buffer_size_ * 10) was exceeded.
Each “new node beyond the cap” entry counts as one drop. Per-node ring-buffer pops (oldest entries replaced inside an existing buffer) are NOT counted here - this counter tracks only entries fully rejected from being buffered. Used by tests and (eventually) a /server-info or /diagnostics endpoint to surface buffer pressure.
Public Static Functions
-
static std::string level_to_severity(uint8_t level)
Convert ROS 2 uint8 log level -> SOVD severity string (“debug” for unknown levels)
-
static uint8_t severity_to_level(const std::string &severity)
Convert SOVD severity string -> ROS 2 uint8 log level (0 for invalid/empty)
-
static bool is_valid_severity(const std::string &severity)
Check if a severity string is valid (one of: debug, info, warning, error, fatal)
-
static json entry_to_json(const LogEntry &entry)
Format a LogEntry as SOVD JSON (id, timestamp, severity, message, context)
-
static std::string normalize_fqn(const std::string &fqn)
Strip leading ‘/’ from a node FQN for ring-buffer key normalization.
Public Static Attributes
-
static constexpr size_t kDefaultBufferSize = 200
Default maximum number of entries retained per node in the ring buffer.
-
static constexpr int kLogLevelWarn = 30
Severity used by the LogManager’s log_sink callback. Numeric values match rcl/rcutils log levels (30=WARN, 40=ERROR) so an adapter can forward to RCLCPP_* macros without translation.
-
static constexpr int kLogLevelError = 40