use cano::prelude::*;
use metrics_tracing_context::{MetricsLayer, TracingContextLayer};
use metrics_util::debugging::{DebugValue, DebuggingRecorder, Snapshotter};
use metrics_util::layers::Layer;
use std::sync::Arc;
use std::time::Duration;
use tracing::info_span;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Step {
Fetch,
Process,
Done,
}
struct FetchTask;
#[task]
impl Task<Step> for FetchTask {
async fn run_bare(&self) -> Result<TaskResult<Step>, CanoError> {
tokio::time::sleep(Duration::from_millis(5)).await;
Ok(TaskResult::Single(Step::Process))
}
}
struct ProcessTask;
#[task]
impl Task<Step> for ProcessTask {
async fn run_bare(&self) -> Result<TaskResult<Step>, CanoError> {
tokio::time::sleep(Duration::from_millis(3)).await;
Ok(TaskResult::Single(Step::Done))
}
}
fn workflow() -> Workflow<Step> {
Workflow::bare()
.with_observer(Arc::new(MetricsObserver::new()))
.register(Step::Fetch, FetchTask)
.register(Step::Process, ProcessTask)
.add_exit_state(Step::Done)
}
fn install_metrics_with_tracing_context() -> Snapshotter {
let inner = DebuggingRecorder::new();
let snapshotter = inner.snapshotter();
let recorder = TracingContextLayer::all().layer(inner);
metrics::set_global_recorder(recorder).expect("install metrics recorder");
cano::metrics::describe();
tracing_subscriber::registry()
.with(MetricsLayer::new())
.with(tracing_subscriber::fmt::layer().with_target(false))
.init();
snapshotter
}
#[tokio::main]
async fn main() {
let snapshotter = install_metrics_with_tracing_context();
workflow()
.with_workflow_id("demo-run-1")
.orchestrate(Step::Fetch)
.await
.expect("workflow run");
{
let span = info_span!("api_request", request_id = "abc");
let _enter = span.enter();
workflow()
.orchestrate(Step::Fetch)
.await
.expect("workflow run");
}
println!("\n=== Cano metrics (labels include span context) ===");
let mut rows = snapshotter.snapshot().into_vec();
rows.sort_by(|a, b| a.0.key().name().cmp(b.0.key().name()));
for (ck, _unit, _desc, value) in rows {
let key = ck.key();
let labels: Vec<String> = key
.labels()
.map(|l| format!("{}={}", l.key(), l.value()))
.collect();
let label_str = if labels.is_empty() {
String::new()
} else {
format!("{{{}}}", labels.join(","))
};
match value {
DebugValue::Counter(v) => println!(" {}{label_str} = {v}", key.name()),
DebugValue::Gauge(v) => println!(" {}{label_str} = {}", key.name(), v.into_inner()),
DebugValue::Histogram(s) => {
let n = s.len();
let sum: f64 = s.iter().map(|x| x.into_inner()).sum();
println!(" {}{label_str} = {{count={n}, sum={sum:.6}s}}", key.name());
}
}
}
}