Skip to main content

Crate foxglove

Crate foxglove 

Source
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 foxglove

The 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 with ring. See Crypto backend.
  • chrono: enables chrono conversions for Duration and Timestamp.
  • derive: enables the use of #[derive(Encode)] to derive the Encode trait 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-rs is enabled by default.
  • require-cuda: opts into a build-time check that cuda.h is present on targets where webrtc-sys would build NVENC support. Requires remote-access to also be enabled. See NVENC hardware acceleration.
  • ring: selects ring as the rustls crypto backend used for TLS. Alternative to aws-lc-rs; mutually exclusive with it. See Crypto backend.
  • schemars: provides a blanket implementation of the Encode trait for types that implement Serialize and JsonSchema.
  • serde: derives Serialize and Deserialize for 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_provider
pub 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_accessremote-access
Remote access implementation.
remote_commonremote-access or websocket
Types shared between the WebSocket server and the remote-access gateway.
remote_data_loader_backenddata_provider or remote_data_loader_backend
Types and utilities for building remote data loader manifests.
schemasDeprecated
Deprecated: Use messages instead.
streamstream
MCAP Stream
system_infosysinfo
Optional publisher that reports process and system statistics.
websocketwebsocket
WebSocket functionality

Macros§

log
Log a message for a topic to the default Context.

Structs§

AppUrl
A foxglove app URL.
BTreeMap
An ordered map based on a B-Tree.
Channel
A channel for messages that implement Encode.
ChannelBuilder
A builder for creating a Channel or RawChannel.
ChannelDescriptor
Information about a Channel.
ChannelId
Uniquely identifies a channel in the context of this program.
Context
A context is the binding between channels and sinks.
LazyChannel
A channel that is initialized lazily upon first use.
LazyContext
A context that is initialized lazily upon first use.
LazyRawChannel
A raw channel that is initialized lazily upon first use.
McapAttachment
An attachment to store in an MCAP file.
McapWriteOptions
Options for use with an McapWriter.
McapWriter
An MCAP writer for logging events.
McapWriterHandle
A handle to an MCAP file writer.
Metadata
Metadata is the metadata associated with a log message.
PartialMetadata
PartialMetadata is Metadata with 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.
WebSocketServerwebsocket
A WebSocket server for live visualization in Foxglove.
WebSocketServerHandlewebsocket
A handle to the WebSocket server.

Enums§

FoxgloveError
An error type for errors generated by this crate.
McapCompression
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.
SinkChannelFilter
A filter for channels that can be used to subscribe to or unsubscribe from channels.
ToUnixNanos
A trait for converting a time value to a u64 nanoseconds since epoch.