Skip to content

Logging

Learn how to configure logging and tracing in SlateDB

SlateDB uses the tracing library for logging and diagnostics. This provides structured, contextual logging that helps debug and monitor your database operations.

Here’s a basic example showing how to consume tracing logs with tracing_subscriber and SlateDB:

main.rs
use slatedb::{bytes::Bytes, object_store::memory::InMemory, Db};
use std::sync::Arc;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Initialize tracing subscriber to see the logs
tracing_subscriber::fmt::init();
// Setup
let object_store = Arc::new(InMemory::new());
let db = Db::open("/tmp/slatedb_tracing_subscriber", object_store).await?;
// Put
let key = b"test_key";
let value = b"test_value";
db.put(key, value).await?;
// Get
assert_eq!(db.get(key).await?, Some(Bytes::from_static(value)));
// Delete
db.delete(key).await?;
assert!(db.get(key).await?.is_none());
// Close
db.close().await?;
Ok(())
}

SlateDB provides structured tracing spans for read operations, allowing you to track and measure the performance of individual reads. When enabled, SlateDB creates a hierarchy of spans that wrap different parts of the read path:

SpanDescriptionFields
slatedb.readRoot span for a read operation (get, scan, or scan_prefix_by_recency).trace_id
slatedb.read.memtableRecords each memtable lookup.trace_id
slatedb.read.read_filtersRecords reading filters for an SST.trace_id, sst_id, sst_level, cached
slatedb.read.evaluate_filterRecords evaluating a named filter for an SST.trace_id, sst_id, sst_level, filter_name, result
slatedb.read.read_indexRecords reading the index for an SST.trace_id, sst_id, sst_level, cached

All slatedb.read.* spans are child spans of the root slatedb.read span.

Field meanings:

  • trace_id — The value from TracingOptions, used to correlate spans from the same read operation.
  • sst_id — The identifier of the SST involved in the span.
  • sst_level: The level that contains the SST is l0 or sorted_run:{id}.
  • cached: true if the filter or index came from the cache or a concurrent cache lookup fetched it. Otherwise, the value is false. The field can be absent if the read stops before the filter or index is read.
  • filter_name: The name of the filter policy being evaluated.
  • result — The boolean result of evaluating the filter.

These spans are only created when you provide TracingOptions to the read operation.

All spans listed above are created at tracing level INFO, except for slatedb.read.memtable. Due to the potential huge number of spans slatedb.read.memtable created during scan operations, slatedb.read.memtable spans are created at tracing level DEBUG.

Read operations (get, scan, scan_prefix_by_recency) can be traced by passing TracingOptions via the corresponding options struct. Use ReadOptions::with_tracing_options() for point reads and ScanOptions::with_tracing_options() for scans.

The trace_id field in TracingOptions allows you to correlate spans of the same read operation.

Here’s an example of enabling tracing for a get operation:

main.rs
use slatedb::config::{ReadOptions, TracingOptions};
use slatedb::{bytes::Bytes, object_store::memory::InMemory, Db};
use std::sync::Arc;
use tracing_chrome::{ChromeLayerBuilder, TraceStyle};
use tracing_subscriber::filter::LevelFilter;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::Layer;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Setup
let object_store = Arc::new(InMemory::new());
let db = Db::open("/tmp/slatedb_tracing_subscriber", object_store).await?;
// Put
let key = b"test_key";
let value = b"test_value";
db.put(key, value).await?;
// Create tracing options with a custom trace ID
let tracing_options = TracingOptions::new("my-trace-id");
// Pass it to the read operation
let read_options = ReadOptions::default().with_tracing_options(Some(tracing_options));
// Register tracing-chrome subscriber.
// Subscriber tracing-chrome outputs traces in Chrome’s trace viewer format.
// The generated file `trace-<unix timestamp>` with the traces can be found in
// the root directory of the project. Dump that file into
// https://ui.perfetto.dev/ to visualize the spans.
let (chrome_layer, _chrome_guard) = ChromeLayerBuilder::new()
.include_args(true)
.trace_style(TraceStyle::Async)
.build();
let subscriber =
tracing_subscriber::registry().with(chrome_layer.with_filter(LevelFilter::DEBUG));
let _subscriber_guard = tracing::subscriber::set_default(subscriber);
//Get
let result = db.get_with_options(key, &read_options).await?;
assert_eq!(result, Some(Bytes::from_static(value)));
// Close
db.close().await?;
Ok(())
}