Skip to main content

hdds-logger

Aggregate and centralize logs from distributed DDS participants with multiple output formats and destinations.

Overview

hdds-logger subscribes to DDS log topics (such as ROS 2 rt/rosout) and collects log messages from all participants in the network. Features include:

  • Log Collection -- Subscribe to DDS log topics with wildcard filtering
  • Multiple Formats -- Plain text, JSON (ELK-ready), JSON Lines, syslog (RFC 5424)
  • Flexible Output -- stdout, file (with rotation), syslog daemon
  • Filtering -- By log level, participant GUID, node name, topic pattern

Installation

cargo install hdds-logger

Or build from source:

cd crates/hdds-logger
cargo build --release

Quick Start

# Log to stdout in plain text
hdds-logger --domain 0

# Log to file in JSON format
hdds-logger --output logs/hdds.log --format json

# Log with rotation (10 MB max per file, keep 5 backups)
hdds-logger --output logs/hdds.log --format json --rotate 10M --rotate-keep 5

# Filter by level
hdds-logger --level warn

# Filter by topic pattern
hdds-logger --topic "rt/rosout"

# Output to syslog
hdds-logger --syslog --facility local0

CLI Reference

hdds-logger [OPTIONS]

Options:
-d, --domain <ID> DDS domain ID to monitor [default: 0]
-t, --topic <PATTERN> Log topic pattern (supports wildcards) [default: rt/rosout]
-o, --output <FILE> Output file path (omit for stdout)
-f, --format <FMT> Output format: text, json, json-lines, syslog [default: text]
-l, --level <LEVEL> Minimum log level: debug, info, warn, error, fatal [default: info]
--rotate <SIZE> File rotation max size (e.g., 10M, 100K, 1G)
--rotate-keep <N> Maximum rotated files to keep [default: 5]
--syslog Output to syslog daemon
--facility <FAC> Syslog facility [default: local0]
--participant <PAT> Filter by participant GUID pattern
--node <PAT> Filter by node name pattern
--colors <BOOL> Use colors in text output [default: true]
-v, --verbose Show internal debug logs
-h, --help Print help
-V, --version Print version

Output Formats

Plain Text

Human-readable format with optional ANSI colors:

2024-01-15 10:30:00.123 [INFO ] [01020304] [rt/rosout] [/my_node] Navigation started
2024-01-15 10:30:01.456 [WARN ] [01020304] [rt/rosout] [/sensor] Low battery: 15%
2024-01-15 10:30:02.789 [ERROR] [05060708] [rt/rosout] [/motor] Motor timeout

JSON

ELK/structured logging compatible. Each entry includes @timestamp for Elasticsearch:

{
"@timestamp": "2024-01-15T10:30:00.123Z",
"level": "INFO",
"message": "Navigation started",
"topic": "rt/rosout",
"node": "/my_node",
"participant_id": "01020304-0506-0708-090a-0b0c0d0e0f10",
"source": "hdds-logger"
}

Syslog (RFC 5424)

Standard syslog format with structured data:

<134>1 2024-01-15T10:30:00.123456Z myhost hdds-logger 12345 rt/rosout [hdds node="/my_node" participant="01020304"] Navigation started

Log Levels

Compatible with ROS 2 rcl_interfaces/Log severity levels:

LevelNumericDescription
DEBUG10Development/diagnostic messages
INFO20Normal operational messages
WARN30Potential issues
ERROR40Recoverable errors
FATAL50Unrecoverable errors

File Rotation

When writing to files, rotation prevents unbounded disk usage:

# Rotate at 10 MB, keep 5 backups
hdds-logger --output /var/log/hdds/dds.log --rotate 10M --rotate-keep 5

Rotation creates numbered backup files: dds.1.log, dds.2.log, etc. When --rotate-keep is reached, the oldest file is deleted.

ROS 2 Log Parsing

hdds-logger natively parses ROS 2 rcl_interfaces/Log messages from the rt/rosout topic. It decodes CDR-encoded fields including:

  • Timestamp (sec + nanosec)
  • Severity level
  • Logger name (node name)
  • Log message
  • Source file, function, and line number

Library API

use hdds_logger::{LogCollector, LogConfig, LogFilter, LogLevel, OutputFormat, OutputConfig};

let config = LogConfig {
format: OutputFormat::Json,
output: OutputConfig::File {
path: "logs/hdds.log".into(),
rotation: None,
},
filter: LogFilter::min_level(LogLevel::Warn),
domain_id: 0,
topic_pattern: "rt/rosout".to_string(),
};

let mut collector = LogCollector::new(config)?;

// Get a handle to stop from another thread
let stop_handle = collector.stop_handle();

// Run collector (blocking)
collector.run()?;

let stats = collector.stats();
println!("Received: {}, Written: {}, Filtered: {}",
stats.logs_received, stats.logs_written, stats.logs_filtered);

Integration with ELK Stack

Pipe JSON output to Logstash or Filebeat:

# Direct pipe to Logstash
hdds-logger --format json | nc logstash-host 5044

# Or write to file for Filebeat
hdds-logger --output /var/log/hdds/dds.json --format json --rotate 50M

Filebeat configuration:

filebeat.inputs:
- type: log
paths:
- /var/log/hdds/dds.json
json.keys_under_root: true
json.add_error_key: true