34 releases (15 breaking)
| new 0.16.3 | Jan 12, 2026 |
|---|---|
| 0.16.2 | Dec 22, 2025 |
| 0.16.1 | Nov 21, 2025 |
| 0.10.1 | Jul 23, 2025 |
| 0.4.1 | Mar 24, 2025 |
#189 in Compression
8,718 downloads per month
Used in foxglove_data_loader
695KB
15K
SLoC
Foxglove
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.
Get Started
See docs.rs.
Supported Rust Versions
The current MSRV (minimum supported rust version) is 1.83.0.
lib.rs:
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::schemas::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::schemas::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::schemas::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 schemas. 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. This currently uses
protobuf encoding.
#[derive(foxglove::Encode)]
struct Custom<'a> {
msg: &'a str,
count: u32,
}
let channel = foxglove::Channel::new("/custom");
channel.log(&Custom {
msg: "custom",
count: 42,
});
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::schemas::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::schemas::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");
Live visualization server
You can use the SDK to publish messages to the Foxglove app.
Note: this requires the live_visualization feature, which is enabled by default.
Use WebSocketServer::new to create a new live visualization 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 visualization server");
// Log stuff here.
server.stop();
Feature flags
The Foxglove SDK defines the following feature flags:
chrono: enables chrono conversions forDurationandTimestamp.derive: enables the use of#[derive(Encode)]to derive theEncodetrait for logging custom structs. Enabled by default.live_visualization: enables the live visualization server and client, and adds dependencies on tokio. Enabled by default.lz4: enables support for the LZ4 compression algorithm for mcap files. Enabled by default.schemars: provides a blanket implementation of theEncodetrait for types that implementSerializeandJsonSchema.unstable: features which are under active development and likely to change in an upcoming version.zstd: enables support for the zstd compression algorithm for mcap files. Enabled by default.
If you do not require live visualization features, you can disable that flag to reduce the compiled size of the SDK.
Requirements
With the live_visualization feature (enabled by default), the Foxglove SDK depends on tokio
as its async runtime. See WebSocketServer for more information. Refer to the tokio
documentation for more information about how to configure your application to use tokio.
Dependencies
~10–42MB
~700K SLoC