use burn::train::{logger::MetricLogger, metric::MetricEntry};
use burn_telemetry::MultiLogger;
#[cfg(all(feature = "otel", feature = "rerun"))]
use std::{env, sync::Arc, time::Duration};
#[cfg(all(feature = "otel", feature = "rerun"))]
use anyhow::{anyhow, Result};
#[cfg(all(feature = "otel", feature = "rerun"))]
use burn_telemetry::{
otel::{OpenTelemetryConfig, OpenTelemetryLogger},
rerun::{RerunConfig, RerunLogger},
};
const EPOCHS: usize = 3;
const STEPS_PER_EPOCH: usize = 4;
#[cfg(not(all(feature = "otel", feature = "rerun")))]
fn main() {
eprintln!(
"Enable both `otel` and `rerun` features to run this example:\n \
cargo run --example multi_logger --features otel,rerun"
);
}
#[cfg(all(feature = "otel", feature = "rerun"))]
#[tokio::main]
async fn main() -> Result<()> {
let mut logger = build_multi_logger()?;
println!("Logging to OpenTelemetry + Rerun");
println!("================================\n");
for epoch in 1..=EPOCHS {
println!("Epoch {epoch}/{EPOCHS}");
for step in 0..STEPS_PER_EPOCH {
emit_train_metrics(&mut logger, epoch, step);
tokio::time::sleep(Duration::from_millis(200)).await;
}
logger.end_epoch(epoch);
println!();
}
println!("Flushing exporters (give the backends a second)...");
tokio::time::sleep(Duration::from_secs(2)).await;
println!("Done! Inspect your OTLP backend and the Rerun viewer.");
Ok(())
}
#[cfg(all(feature = "otel", feature = "rerun"))]
fn build_multi_logger() -> Result<MultiLogger> {
let otel_logger = OpenTelemetryLogger::new(otel_config(), "multi-logger-job", Some("device-0"))
.map_err(|err| anyhow!(err))?;
let rerun_logger =
RerunLogger::new(rerun_config(), "multi-logger-job").map_err(|err| anyhow!(err))?;
Ok(MultiLogger::new()
.add_logger(otel_logger)
.add_logger(rerun_logger))
}
#[cfg(all(feature = "otel", feature = "rerun"))]
fn otel_config() -> OpenTelemetryConfig {
let mut config = OpenTelemetryConfig::new("burn-telemetry-multi")
.with_export_interval(Duration::from_secs(2))
.with_export_timeout(Duration::from_secs(5))
.with_max_batch_size(8)
.with_async_export(true)
.with_resource_attribute("example", "multi_logger");
if let Ok(endpoint) = env::var("OTEL_EXPORTER_OTLP_ENDPOINT") {
config = config.with_endpoint(endpoint);
}
config
}
#[cfg(all(feature = "otel", feature = "rerun"))]
fn rerun_config() -> RerunConfig {
RerunConfig::new("burn-telemetry-multi")
.with_spawn_viewer(true)
.with_default_timeline("epoch")
}
#[cfg(all(feature = "otel", feature = "rerun"))]
fn emit_train_metrics(logger: &mut MultiLogger, epoch: usize, step: usize) {
let global_step = (epoch - 1) * STEPS_PER_EPOCH + step + 1;
let loss_value = 1.0 / global_step as f64;
let accuracy_value = (global_step as f64 * 5.0).min(100.0) / 100.0;
logger.log(&metric(
"train/loss",
format!("{loss_value:.4}"),
format!("{loss_value}"),
));
logger.log(&metric(
"train/accuracy",
format!("{:.2}%", accuracy_value * 100.0),
format!("{accuracy_value}"),
));
}
#[cfg(all(feature = "otel", feature = "rerun"))]
fn metric(name: &str, formatted: String, serialized: String) -> MetricEntry {
MetricEntry::new(Arc::new(name.to_string()), formatted, serialized)
}