Expand description
The official Foxglove SDK.
This crate provides support for integrating with the Foxglove platform. It can be used to log events to local MCAP files or a local visualization server that communicates with the Foxglove app.
§Getting started
The easiest way to get started is to install the foxglove crate with default features, which
will allow logging messages to the Foxglove app and to an MCAP file.
cargo add foxgloveThe following sections illustrate how to use the SDK. For a more hands-on walk-through, see https://docs.foxglove.dev/docs/sdk/example.
§Recording messages
To record messages, you need to initialize either an MCAP file writer or a WebSocket server for
live visualization. In this example, we create an MCAP writer, and record a
Log message on a topic called /log. We write one log message and
close the file.
use foxglove::messages::Log;
use foxglove::{log, McapWriter};
// Create a new MCAP file named 'test.mcap'.
let mcap = McapWriter::new()
.create_new_buffered_file("test.mcap")
.expect("create failed");
log!(
"/log",
Log {
message: "Hello, Foxglove!".to_string(),
..Default::default()
}
);
// Flush and close the MCAP file.
mcap.close().expect("close failed");§Concepts
§Context
A Context is the binding between channels and sinks. Each channel and sink belongs to
exactly one context. Sinks receive advertisements about channels on the context, and can
optionally subscribe to receive logged messages on those channels.
When the context goes out of scope, its corresponding channels and sinks will be disconnected from one another, and logging will stop. Attempts to log further messages on the channels will elicit throttled warning messages.
Since many applications only need a single context, the SDK provides a static default context for convenience. This default context is the one used in the example above. If we wanted to use an explicit context instead, we’d write:
use foxglove::messages::Log;
use foxglove::Context;
// Create a new context.
let ctx = Context::new();
// Create a new MCAP file named 'test.mcap'.
let mcap = ctx
.mcap_writer()
.create_new_buffered_file("test.mcap")
.expect("create failed");
// Create a new channel for the topic "/log" for `Log` messages.
let channel = ctx.channel_builder("/log").build();
channel.log(&Log {
message: "Hello, Foxglove!".to_string(),
..Default::default()
});
// Flush and close the MCAP file.
mcap.close().expect("close failed");§Channels
A Channel gives a way to log related messages which have the same type, or Schema. Each
channel is instantiated with a unique “topic”, or name, which is typically prefixed by a /. If
you’re familiar with MCAP, it’s the same concept as an MCAP channel.
A channel is always associated with exactly one Context throughout its lifecycle. The
channel remains attached to the context until it is either explicitly closed with
Channel::close, or the context is dropped. Attempting to log a message on a closed channel
will elicit a throttled warning.
In the example above, log! creates a Channel<Log> behind the scenes on
the first call. The example could be equivalently written as:
use foxglove::messages::Log;
use foxglove::{Channel, McapWriter};
// Create a new MCAP file named 'test.mcap'.
let mcap = McapWriter::new()
.create_new_buffered_file("test.mcap")
.expect("create failed");
// Create a new channel for the topic "/log" for `Log` messages.
let channel = Channel::new("/log");
channel.log(&Log {
message: "Hello, Foxglove!".to_string(),
..Default::default()
});
// Flush and close the MCAP file.
mcap.close().expect("close failed");log! can be mixed and matched with manually created channels in the default Context, as
long as the types are exactly the same.
§Well-known types
The SDK provides structs for well-known message types. These can be used in conjunction
with Channel for type-safe logging, which ensures at compile time that messages logged to a
channel all share a common schema.
§Custom data
You can also define your own custom data types by implementing the Encode trait.
The easiest way to do this is to enable the derive feature and derive the Encode trait,
which will generate a schema and allow you to log your struct to a channel. The underlying
serialization format is an implementation detail and may change across SDK versions.
#[derive(foxglove::Encode)]
struct Custom<'a> {
msg: &'a str,
count: u32,
}
let channel = foxglove::Channel::new("/custom");
channel.log(&Custom {
msg: "custom",
count: 42,
});Note: The Encode derive macro is a convenience for getting data into Foxglove with
minimal friction. The generated schema is not designed for evolution — reordering, inserting,
or removing fields will silently break compatibility with previously recorded data. If you need
backwards-compatible schema evolution, maintain an explicit .proto file and implement
Encode manually for your generated types. See
logging with protobuf schemas
for an example.
If you’d like to use JSON encoding for integration with particular tooling, you can enable the
schemars feature, which will provide a blanket Encode implementation for types that
implement Serialize and JsonSchema.
§Lazy Channels
A common pattern is to create the channels once as static variables, and then use them
throughout the application. But because channels do not have a const initializer, they must be
initialized lazily. LazyChannel and LazyRawChannel provide a convenient way to do this.
Be careful when using this pattern. The channel will not be advertised to sinks until it is
initialized, which is guaranteed to happen when the channel is first used. If you need to ensure
the channel is initialized before using it, you can use LazyChannel::init.
In this example, we create two lazy channels on the default context:
use foxglove::messages::SceneUpdate;
use foxglove::{LazyChannel, LazyRawChannel};
static BOXES: LazyChannel<SceneUpdate> = LazyChannel::new("/boxes");
static MSG: LazyRawChannel = LazyRawChannel::new("/msg", "json");It is also possible to bind lazy channels to an explicit LazyContext:
use foxglove::messages::SceneUpdate;
use foxglove::{LazyChannel, LazyContext, LazyRawChannel};
static CTX: LazyContext = LazyContext::new();
static BOXES: LazyChannel<SceneUpdate> = CTX.channel("/boxes");
static MSG: LazyRawChannel = CTX.raw_channel("/msg", "json");§Sinks
A “sink” is a destination for logged messages. If you do not configure a sink, log messages will simply be dropped without being recorded. You can configure multiple sinks, and you can create or destroy them dynamically at runtime.
A sink is typically associated with exactly one Context throughout its lifecycle. Details
about the how the sink is registered and unregistered from the context are sink-specific.
§MCAP file
Use McapWriter::new() to register a new MCAP writer. As long as the handle remains in scope,
events will be logged to the MCAP file. When the handle is closed or dropped, the sink will be
unregistered from the Context, and the file will be finalized and flushed.
let mcap = foxglove::McapWriter::new()
.create_new_buffered_file("test.mcap")
.expect("create failed");You can override the MCAP writer’s configuration using McapWriter::with_options. See
WriteOptions for more detail about these parameters:
let options = mcap::WriteOptions::default()
.chunk_size(Some(1024 * 1024))
.compression(Some(mcap::Compression::Lz4));
let mcap = foxglove::McapWriter::with_options(options)
.create_new_buffered_file("test.mcap")
.expect("create failed");§WebSocket server
You can use the SDK to publish messages to the Foxglove app over a local WebSocket connection.
Note: this requires the websocket feature, which is enabled by default.
Use WebSocketServer::new to create a new WebSocket server. By default, the server
listens on 127.0.0.1:8765. Once the server is configured, call WebSocketServer::start to
start the server, and begin accepting WebSocket connections from the Foxglove app.
Each client that connects to the WebSocket server is its own independent sink. The sink is
dynamically added to the Context associated with the server when the client connects, and
removed from the context when the client disconnects.
See the “Connect” documentation for how to connect the Foxglove app to your running server.
Note that the server remains running until the process exits, even if the handle is dropped. Use
stop to shut down the server explicitly.
let server = foxglove::WebSocketServer::new()
.name("Wall-E")
.bind("127.0.0.1", 9999)
.start()
.await
.expect("Failed to start WebSocket server");
// Log stuff here.
server.stop().wait().await;§Remote access gateway
You can use the SDK to publish messages to the Foxglove app over a remote WebRTC connection, using the Foxglove remote access service.
Note: this requires the remote-access feature, which is not enabled by default.
Use remote_access::Gateway::new to create a new remote access gateway. You must provide a
device token to authenticate with the Foxglove API; this can be set via the builder or with
the FOXGLOVE_DEVICE_TOKEN environment variable.
Once started, the gateway connects to the Foxglove platform and makes the device available for
remote visualization and teleop. The gateway acts as a single sink on the Context, registered
when the gateway starts and unregistered when it stops.
The gateway handle can safely be dropped and the connection will continue to run. Use
stop to shut down the gateway explicitly.
use foxglove::remote_access::Gateway;
let gateway = Gateway::new()
.name("R2-D2")
.start()
.expect("Failed to start remote access gateway");
// Log stuff here.
gateway.stop().await.expect("Failed to stop remote access gateway");§NVENC hardware acceleration
When available, NVIDIA NVENC is used to accelerate H.264 video encoding for the remote access gateway. Without it, the gateway falls back to software H.264 encoding, which is slower and lower quality.
NVENC is available on Linux for x86_64, arm, and aarch64. It requires cuda.h to be present
at /usr/local/cuda/include/cuda.h or at $CUDA_HOME/include/cuda.h if you set CUDA_HOME.
On Ubuntu you can install the headers with apt install nvidia-cuda-toolkit or apt install nvidia-cuda-dev.
If that places cuda.h at /usr/include/cuda.h rather than /usr/local/cuda/include/,
you will also need to set CUDA_HOME=/usr so the build can find it.
You can enable the require-cuda feature on this crate to make it a build error if
remote-access is disabled, the target does not support NVENC, or cuda.h is not
found on a target where NVENC would be built.
§Feature flags
The Foxglove SDK defines the following feature flags:
aws-lc-rs: selects aws-lc-rs as the rustls crypto backend used for TLS. Enabled by default. Mutually exclusive withring. See Crypto backend.chrono: enables chrono conversions forDurationandTimestamp.derive: enables the use of#[derive(Encode)]to derive theEncodetrait for logging custom structs. Enabled by default.full: the full set of supported features, with opinionated picks for mutually exclusive choices.lz4: enables support for the LZ4 compression algorithm for mcap files. Enabled by default.remote-access: enables the remote access gateway for live visualization and teleop via WebRTC. Requires a crypto backend;aws-lc-rsis enabled by default.require-cuda: opts into a build-time check thatcuda.his present on targets where webrtc-sys would build NVENC support. Requiresremote-accessto also be enabled. See NVENC hardware acceleration.ring: selects ring as the rustls crypto backend used for TLS. Alternative toaws-lc-rs; mutually exclusive with it. See Crypto backend.schemars: provides a blanket implementation of theEncodetrait for types that implementSerializeandJsonSchema.serde: derivesSerializeandDeserializefor all message types.unstable: features which are under active development and likely to change in an upcoming version.websocket: enables the WebSocket server and client for live visualization. Enabled by default.zstd: enables support for the zstd compression algorithm for mcap files. Enabled by default.
If you do not require WebSocket features, you can disable that flag to reduce the compiled size of the SDK.
§Crypto backend
The remote-access and websocket-tls features rely on rustls for TLS, which requires
a crypto provider to be installed as the process-wide default. The aws-lc-rs and ring
crate features select which backend Foxglove installs and are mutually exclusive:
enabling both is a compile error. aws-lc-rs is in the default feature set, so opting
into TLS with default features just works:
foxglove = { version = "...", features = ["remote-access"] }ring is offered as an alternative for targets where building aws-lc-sys is impractical
(e.g. the iOS simulator). To use it, disable default features and select ring explicitly:
foxglove = { version = "...", default-features = false, features = ["remote-access", "ring", ...] }Foxglove installs the selected provider before opening any TLS connections. Applications
that want to install a different provider should call
rustls::crypto::CryptoProvider::install_default themselves before any Foxglove TLS code
runs.
§Requirements
With the websocket or remote-access features, the Foxglove SDK depends on tokio
as its async runtime. Refer to the tokio documentation for more information about how to
configure your application to use tokio.
Re-exports§
pub use remote_data_loader_backend as data_provider;data_providerpub use bytes;
Modules§
- convert
- Traits for conversions between types.
- library_
version - Provides an identifier for the library used as a log source.
- messages
- Types implementing well-known Foxglove message types.
- remote_
access remote-access - Remote access implementation.
- remote_
common remote-accessorwebsocket - Types shared between the WebSocket server and the remote-access gateway.
- remote_
data_ loader_ backend data_providerorremote_data_loader_backend - Types and utilities for building remote data loader manifests.
- schemas
Deprecated - Deprecated: Use
messagesinstead. - stream
stream - MCAP Stream
- system_
info sysinfo - Optional publisher that reports process and system statistics.
- websocket
websocket - WebSocket functionality
Macros§
- log
- Log a message for a topic to the default Context.
Structs§
- AppUrl
- A foxglove app URL.
- BTree
Map - An ordered map based on a B-Tree.
- Channel
- A channel for messages that implement
Encode. - Channel
Builder - A builder for creating a
ChannelorRawChannel. - Channel
Descriptor - Information about a Channel.
- Channel
Id - Uniquely identifies a channel in the context of this program.
- Context
- A context is the binding between channels and sinks.
- Lazy
Channel - A channel that is initialized lazily upon first use.
- Lazy
Context - A context that is initialized lazily upon first use.
- Lazy
RawChannel - A raw channel that is initialized lazily upon first use.
- Mcap
Attachment - An attachment to store in an MCAP file.
- Mcap
Write Options - Options for use with an
McapWriter. - Mcap
Writer - An MCAP writer for logging events.
- Mcap
Writer Handle - A handle to an MCAP file writer.
- Metadata
- Metadata is the metadata associated with a log message.
- Partial
Metadata - PartialMetadata is
Metadatawith all optional fields. - RawChannel
- A log channel that can be used to log binary messages.
- Schema
- A Schema is a description of the data format of messages in a channel.
- SinkId
- Uniquely identifies a [
Sink] in the context of this program. - WebSocket
Server websocket - A WebSocket server for live visualization in Foxglove.
- WebSocket
Server Handle websocket - A handle to the WebSocket server.
Enums§
- Foxglove
Error - An error type for errors generated by this crate.
- Mcap
Compression - Compression options for content in an MCAP file Compression options for chunks of channels, schemas, and messages in an MCAP file
Traits§
- Encode
- A trait representing a message that can be logged to a channel.
- Sink
Channel Filter - A filter for channels that can be used to subscribe to or unsubscribe from channels.
- ToUnix
Nanos - A trait for converting a time value to a u64 nanoseconds since epoch.