Skip to main content

API Reference

Complete reference for MCAP logging APIs in Cerulion Core.
For a quick start guide with examples, see the Quickstart page.

Quick Reference

MethodDescriptionCategory
create()Create a new MCAP recorderInitialization
register_channel()Register a topic/channel for loggingSetup
log_message()Log a message to the MCAP fileRecording
record_from_subscriber()Record from a subscriber (standalone)Recording
finish()Close the MCAP file gracefullyCleanup
as_arc()Wrap recorder in Arc for sharingUtilities
enable_mcap_logging()Enable logging on pub/subIntegration

Configuration

Description

Configuration struct for MCAP recorder settings.

Fields

compression
Option<Compression>
Optional compression setting. Can be None, Some(Compression::Lz4), or Some(Compression::Zstd).
schema_registry
Option<Arc<SchemaRegistry>>
Optional schema registry for automatic schema lookup when registering channels.
include_patterns
Option<Vec<String>>
Optional list of topic patterns to include. If None, all topics are included (unless excluded).
exclude_patterns
Option<Vec<String>>
Optional list of topic patterns to exclude from recording.

Example

let config = McapRecorderConfig {
    compression: Some(Compression::Lz4),
    schema_registry: Some(Arc::new(schema_registry)),
    include_patterns: Some(vec!["sensors/*".to_string()]),
    exclude_patterns: Some(vec!["debug/*".to_string()]),
};

Notes

Topic filtering via McapRecorderConfig is currently defined in the API but not yet fully integrated into the recorder creation. This feature is planned for future implementation.

Initialization

Signature

pub fn create<P: AsRef<Path>>(
    file_path: P,
    compression: Option<Compression>,
    schema_registry: Option<Arc<SchemaRegistry>>,
) -> Result<Self, Box<dyn std::error::Error>>

Description

Creates a new MCAP recorder that writes to a file with automatic background writing. The recorder automatically starts a background thread for asynchronous message writing to avoid blocking your application.

Parameters

file_path
P: AsRef<Path>
required
Path to the MCAP file to create (e.g., "log.mcap"). The directory must exist.
compression
Option<Compression>
Optional compression setting:
  • None - No compression (fastest, larger files)
  • Some(Compression::Lz4) - LZ4 compression (balanced)
  • Some(Compression::Zstd) - Zstandard compression (best compression)
schema_registry
Option<Arc<SchemaRegistry>>
Optional schema registry for automatic schema lookup when registering channels with message types.

Returns

Result
Result<McapRecorder, Box<dyn std::error::Error>>

Examples

// No compression (faster, larger files)
let recorder = McapRecorder::create("log.mcap", None, None)?;

// With LZ4 compression
let recorder = McapRecorder::create("log.mcap", Some(Compression::Lz4), None)?;

// With Zstandard compression
let recorder = McapRecorder::create("log.mcap", Some(Compression::Zstd), None)?;

// With schema registry
let schema_registry = Arc::new(/* your schema registry */);
let recorder = McapRecorder::create("log.mcap", None, Some(schema_registry))?;

Notes

The recorder automatically starts a background thread for asynchronous message writing to avoid blocking your application.

Channel Management

Signature

pub fn register_channel(
    &self,
    topic: &str,
    message_type: Option<&str>,
    encoding: &str,
) -> Result<(), Box<dyn std::error::Error>>

Description

Registers a channel (topic) with optional schema for logging. Channels are automatically registered when using log_message() if not already registered, but manual registration is useful when you want to specify a schema upfront.

Parameters

topic
&str
required
Topic name to register. Should follow standard naming conventions (e.g., "sensors/imu").
message_type
Option<&str>
Optional message type name for schema lookup. If provided and a schema registry is configured, the recorder will attempt to load the schema for this message type.
encoding
&str
required
Message encoding format. Common values:
  • "raw" - Raw binary data (no schema)
  • "protobuf" - Protocol Buffers encoding
  • "json" - JSON encoding
  • "cdr" - CDR encoding (ROS2)

Returns

Result
Result<(), Box<dyn std::error::Error>>

Examples

// Register channel with raw encoding (no schema)
recorder.register_channel("sensors/imu", None, "raw")?;

// Register channel with message type for schema lookup
recorder.register_channel("sensors/image", Some("sensor_msgs/Image"), "protobuf")?;

Notes

Channels are automatically registered when using log_message() if not already registered. Manual registration is useful when you want to specify a schema upfront.

Recording Messages

Signature

pub fn log_message(
    &self,
    topic: &str,
    data: Vec<u8>,
    timestamp: Option<u64>,
) -> Result<(), Box<dyn std::error::Error>>

Description

Logs a message to the MCAP file asynchronously. Messages are queued and written by a background thread, so this call returns quickly without blocking your application.

Parameters

topic
&str
required
Topic name for the message. The channel will be auto-registered if not already registered.
data
Vec<u8>
required
Message data as a byte vector. Should match the encoding specified during channel registration.
timestamp
Option<u64>
Optional timestamp in nanoseconds since Unix epoch. If None, the current time is used automatically.

Returns

Result
Result<(), Box<dyn std::error::Error>>

Examples

// Log message with automatic timestamp
let data = vec![1, 2, 3, 4];
recorder.log_message("sensors/data", data, None)?;

// Log message with custom timestamp
use cerulion_core::timing::nanos_since_epoch;
let timestamp = nanos_since_epoch();
recorder.log_message("sensors/data", data, Some(timestamp))?;

Notes

Messages are queued and written asynchronously by a background thread, so this call returns quickly without blocking your application.

Signature

pub fn record_from_subscriber<F>(
    &self,
    subscriber: &Subscriber,
    topic: &str,
    mut should_continue: F,
) -> Result<(), Box<dyn std::error::Error>>
where
    F: FnMut() -> bool,

Description

Records messages from a subscriber, wrapping its receive loop (standalone recorder pattern). This method automatically registers the channel if not already registered and polls the subscriber for messages.

Parameters

subscriber
&Subscriber
required
Subscriber to record messages from. The subscriber should already be created and connected.
topic
&str
required
Topic name for the MCAP channel. This is the topic name that will appear in the MCAP file.
should_continue
F: FnMut() -> bool
required
Mutable closure that returns true to continue recording or false to stop. Called periodically during the recording loop.

Returns

Result
Result<(), Box<dyn std::error::Error>>

Example

use std::time::{Duration, Instant};

let recorder = McapRecorder::create("log.mcap", None, None)?;
let subscriber = Subscriber::create("sensors/data", None)?;

// Record for 10 seconds
let start = Instant::now();
recorder.record_from_subscriber(&subscriber, "sensors/data", || {
    start.elapsed() < Duration::from_secs(10)
})?;

// Finish recording
recorder.finish()?;

Notes

This method automatically registers the channel if not already registered and polls the subscriber for messages. It’s a convenience method for standalone recording scenarios.

Cleanup

Signature

pub fn finish(mut self) -> Result<(), Box<dyn std::error::Error>>

Description

Finishes writing and closes the MCAP file gracefully. This method signals the background thread to shut down, waits for all queued messages to be written, and then closes the file.

Returns

Result
Result<(), Box<dyn std::error::Error>>

Example

let recorder = McapRecorder::create("log.mcap", None, None)?;

// ... log messages ...

// Explicitly finish and close the file
recorder.finish()?;

Notes

The finish() method consumes the recorder. If you don’t call it explicitly, the file will be closed automatically when the recorder is dropped, but errors during cleanup won’t be reported.
Always call finish() explicitly if you need to handle write errors. The Drop implementation will attempt to finish writing, but errors during drop cannot be reported.

Utilities

Signature

pub fn as_arc(self) -> Arc<Self>

Description

Wraps the recorder in an Arc for sharing across threads or multiple publishers/subscribers. This is a convenience method for when you need to share the recorder.

Returns

Arc<McapRecorder>
Arc<McapRecorder>
Shared reference to the recorder that can be cloned and shared across threads.

Example

let recorder = McapRecorder::create("log.mcap", None, None)?.as_arc();

// Share with publishers
publisher.enable_mcap_logging(recorder.clone());
subscriber.enable_mcap_logging(recorder.clone());

See Also


Integration

Publisher Signature

pub fn enable_mcap_logging(&mut self, recorder: Arc<McapRecorder>)

Subscriber Signature

pub fn enable_mcap_logging(&mut self, recorder: Arc<McapRecorder>)

Description

Enables MCAP logging on a publisher or subscriber. Once enabled, all messages published or received will be automatically logged to the provided recorder.

Parameters

recorder
Arc<McapRecorder>
required
Shared reference to an MCAP recorder. All messages published/received will be automatically logged to this recorder.

Examples

// Enable logging on publisher
let recorder = Arc::new(McapRecorder::create("log.mcap", None, None)?);
publisher.enable_mcap_logging(recorder.clone());

// Enable logging on subscriber
subscriber.enable_mcap_logging(recorder.clone());

// Or use as_arc() for convenience
let recorder = McapRecorder::create("log.mcap", None, None)?.as_arc();
publisher.enable_mcap_logging(recorder.clone());

Notes

The recorder must be wrapped in Arc to allow sharing across multiple publishers/subscribers. Use as_arc() for convenience.

Compression Options

MCAP supports optional compression to reduce file size.

Compression Types

None
No Compression
Speed: Fastest
Compression Ratio: 1:1 (no compression)
Best For: Real-time logging, fast storage, systems with limited CPU
Compression::Lz4
LZ4 Compression
Speed: Fast
Compression Ratio: 2-3:1 typical
Best For: Balanced real-time logging with moderate compression
Compression::Zstd
Zstandard Compression
Speed: Slower
Compression Ratio: 3-5:1 typical
Best For: Archival storage, offline analysis, maximizing storage efficiency

Examples

// No compression (faster, larger files)
let recorder = McapRecorder::create("log.mcap", None, None)?;

// LZ4 compression (good balance)
let recorder = McapRecorder::create("log.mcap", Some(Compression::Lz4), None)?;

// Zstandard compression (better compression, slower)
let recorder = McapRecorder::create("log.mcap", Some(Compression::Zstd), None)?;

Performance Comparison

For a typical sensor data stream (100 Hz, 1KB messages):
CompressionFile SizeCPU UsageLatency Impact
None360 MB0.1%< 1 μs
LZ4120 MB2.5%~10 μs
Zstandard72 MB8.5%~50 μs
For real-time logging, use no compression or LZ4. For archival storage, use Zstandard for better compression ratios.

Topic Filtering

The McapRecorderConfig supports topic filtering through include and exclude patterns.

Filter Patterns

Filter patterns support simple string matching:
  • Exact match: "sensors/imu" matches only that topic
  • Substring match: "sensors/*" matches topics containing “sensors/“

Configuration

include_patterns
Option<Vec<String>>
exclude_patterns
Option<Vec<String>>

Filter Logic

  1. Exclude First: If a topic matches any exclude pattern, it’s excluded
  2. Include Check: If include patterns are specified, topic must match at least one
  3. Default Behavior: If no include patterns specified, all topics are included (unless excluded)

Examples

// Include all topics (default)
let config = McapRecorderConfig::default();

// Include only sensor topics
let config = McapRecorderConfig {
    include_patterns: Some(vec!["sensors/".to_string()]),
    ..Default::default()
};

// Exclude debug topics, include everything else
let config = McapRecorderConfig {
    exclude_patterns: Some(vec!["debug/".to_string()]),
    ..Default::default()
};

// Include sensors, but exclude debug sensors
let config = McapRecorderConfig {
    include_patterns: Some(vec!["sensors/".to_string()]),
    exclude_patterns: Some(vec!["sensors/debug/".to_string()]),
    ..Default::default()
};

Schema Registry Integration

MCAP can use an existing schema registry for automatic schema lookup.

Purpose

Schema registry integration is useful when:
  • Logging ROS2 messages via RCL Hooks
  • Ensuring proper schema registration for ROS2 message types
  • Maintaining compatibility with ROS2 tooling
  • Automatic schema conversion from YAML to JSON format

Schema Registry Parameter

schema_registry
Option<Arc<SchemaRegistry>>

Example

use cerulion_core::prelude::*;
use std::sync::Arc;

// Create schema registry (if you have ROS2 message definitions)
let schema_registry = Arc::new(SchemaRegistry::new()?);

// Create recorder with schema registry
let recorder = McapRecorder::create("log.mcap", None, Some(schema_registry))?;

// When registering channels, the recorder will automatically lookup schemas
recorder.register_channel("sensors/imu", Some("sensor_msgs/Imu"), "protobuf")?;
Schema registry integration is optional. For Cerulion-native message types, schemas are automatically registered when channels are created. The recorder automatically converts YAML schemas to JSON format for MCAP.

Background Writing

The MCAP recorder uses a background thread for asynchronous message writing to avoid blocking your application.

How It Works

message_queuing
Step 1
When you call log_message(), messages are queued in memory with minimal overhead
background_thread
Step 2
A dedicated thread processes the queue and writes messages to disk in batches
non_blocking
Step 3
Your application continues without waiting for disk I/O operations
graceful_shutdown
Step 4
The finish() method or Drop implementation ensures all queued messages are written before closing

Performance Benefits

low_latency
Performance
log_message() typically returns in microseconds, with no disk I/O blocking
high_throughput
Performance
Batched writes improve disk performance and reduce system call overhead
no_blocking
Performance
Real-time publishers/subscribers aren’t affected by disk speed or filesystem latency

Example

let recorder = McapRecorder::create("log.mcap", None, None)?;

// These calls return immediately (messages queued)
for i in 0..1000 {
    recorder.log_message("data", vec![i], None)?;
}

// Ensure all messages are written before closing
recorder.finish()?;

Error Handling

All MCAP logging functions return Result types for comprehensive error handling.

Common Error Scenarios

file_creation_errors
I/O Errors
logging_errors
Runtime Errors
finish_errors
Cleanup Errors

Examples

// Handle file creation errors
match McapRecorder::create("log.mcap", None, None) {
    Ok(recorder) => {
        let recorder = recorder.as_arc();
        publisher.enable_mcap_logging(recorder);
    }
    Err(e) => {
        eprintln!("Failed to create MCAP recorder: {}", e);
        // Continue without logging
    }
}

// Handle logging errors
if let Err(e) = recorder.log_message("topic", data, None) {
    eprintln!("Failed to log message: {}", e);
}

// Handle finish errors
if let Err(e) = recorder.finish() {
    eprintln!("Failed to finish recording: {}", e);
}

Next Steps