Examples
Learn by example! This page shows real-world usage patterns.
Basic Examples
Simple Task Tracking
use async_inspect::prelude::*;
#[async_inspect::trace]
async fn fetch_data(id: u64) -> String {
// Simulated async work
tokio::time::sleep(Duration::from_millis(100)).await;
format!("Data for {}", id)
}
#[tokio::main]
async fn main() {
let data = fetch_data(42).await;
println!("{}", data);
// Print summary
Reporter::global().print_summary();
}
Output:
📊 Statistics:
Total Tasks: 1
Completed: 1
Avg Duration: 100ms
Multiple Tasks
use async_inspect::runtime::tokio::spawn_tracked;
#[tokio::main]
async fn main() {
let mut handles = vec![];
for i in 0..10 {
let handle = spawn_tracked(
format!("worker_{}", i),
async move {
tokio::time::sleep(Duration::from_millis(i * 10)).await;
i * 2
}
);
handles.push(handle);
}
for handle in handles {
handle.await.unwrap();
}
Reporter::global().print_summary();
}
Web Server Example
Axum Integration
use axum::{Router, routing::get, extract::Path, Json};
use async_inspect::prelude::*;
#[derive(serde::Serialize)]
struct User {
id: u64,
name: String,
}
#[async_inspect::trace]
async fn get_user(Path(id): Path<u64>) -> Json<User> {
// Simulated database query
tokio::time::sleep(Duration::from_millis(50)).await;
Json(User {
id,
name: format!("User {}", id),
})
}
#[async_inspect::trace]
async fn list_users() -> Json<Vec<User>> {
// Simulated database query
tokio::time::sleep(Duration::from_millis(100)).await;
Json(vec![
User { id: 1, name: "Alice".to_string() },
User { id: 2, name: "Bob".to_string() },
])
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/users", get(list_users))
.route("/users/:id", get(get_user));
println!("Server running on http://localhost:3000");
println!("Monitor with: async-inspect monitor");
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
Monitor output:
Active Tasks: 23
#145 ⏳ get_user(42) 45ms
#146 ⏳ get_user(43) 32ms
#147 ✅ list_users 102ms
Database Example
SQLx Integration
use sqlx::PgPool;
use async_inspect::prelude::*;
#[async_inspect::trace]
async fn fetch_user_from_db(pool: &PgPool, id: i64) -> Result<User, sqlx::Error> {
sqlx::query_as!(
User,
"SELECT id, name, email FROM users WHERE id = $1",
id
)
.fetch_one(pool)
.await
}
#[async_inspect::trace]
async fn create_user(pool: &PgPool, name: String, email: String) -> Result<User, sqlx::Error> {
sqlx::query_as!(
User,
"INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id, name, email",
name,
email
)
.fetch_one(pool)
.await
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let pool = PgPool::connect("postgresql://localhost/mydb").await?;
let user = create_user(&pool, "Alice".into(), "alice@example.com".into()).await?;
println!("Created user: {:?}", user);
// Check performance
Reporter::global().print_summary();
Ok(())
}
Concurrent Operations
Parallel Requests
use async_inspect::prelude::*;
#[async_inspect::trace]
async fn fetch_profile(id: u64) -> Profile {
tokio::time::sleep(Duration::from_millis(100)).await;
Profile { id }
}
#[async_inspect::trace]
async fn fetch_posts(id: u64) -> Vec<Post> {
tokio::time::sleep(Duration::from_millis(150)).await;
vec![Post { id, title: "Hello".into() }]
}
#[async_inspect::trace]
async fn fetch_friends(id: u64) -> Vec<Friend> {
tokio::time::sleep(Duration::from_millis(80)).await;
vec![Friend { id: id + 1 }]
}
#[async_inspect::trace]
async fn get_user_data(id: u64) -> UserData {
// All three run concurrently!
let (profile, posts, friends) = tokio::join!(
fetch_profile(id),
fetch_posts(id),
fetch_friends(id)
);
UserData { profile, posts, friends }
}
#[tokio::main]
async fn main() {
let data = get_user_data(42).await;
// See that all three ran in parallel
Reporter::global().print_summary();
}
Output shows parallel execution:
📊 Timeline:
fetch_profile ████████░░░░░░░░ 100ms
fetch_posts ████████████░░░░ 150ms (overlapped!)
fetch_friends ██████░░░░░░░░░░ 80ms (overlapped!)
Total: 150ms (not 330ms!)
Error Handling
With Results
use async_inspect::prelude::*;
#[async_inspect::trace]
async fn risky_operation() -> Result<String, Box<dyn std::error::Error>> {
if rand::random() {
Ok("Success!".into())
} else {
Err("Failed!".into())
}
}
#[tokio::main]
async fn main() {
for i in 0..10 {
match risky_operation().await {
Ok(result) => println!("#{} {}", i, result),
Err(e) => eprintln!("#{} Error: {}", i, e),
}
}
// See success/failure ratio
let stats = Inspector::global().stats();
println!("Success rate: {}/{}",
stats.completed_tasks,
stats.total_tasks
);
}
Performance Analysis
Finding Slow Operations
use async_inspect::prelude::*;
#[async_inspect::trace]
async fn slow_operation(id: u64) {
tokio::time::sleep(Duration::from_millis(id * 100)).await;
}
#[async_inspect::trace]
async fn fast_operation(id: u64) {
tokio::time::sleep(Duration::from_millis(id)).await;
}
#[tokio::main]
async fn main() {
for i in 1..=5 {
tokio::join!(
slow_operation(i),
fast_operation(i)
);
}
// Export for analysis
JsonExporter::export_to_file(&Inspector::global(), "profile.json").unwrap();
println!("Analyze with:");
println!(" cat profile.json | jq '.tasks | sort_by(.duration_ms) | reverse | .[0:5]'");
}
Deadlock Detection
Finding Circular Dependencies
use async_inspect::prelude::*;
use async_inspect::graph::TaskGraph;
use std::sync::Arc;
use tokio::sync::Mutex;
#[tokio::main]
async fn main() {
let mutex_a = Arc::new(Mutex::new(0));
let mutex_b = Arc::new(Mutex::new(0));
let a = mutex_a.clone();
let b = mutex_b.clone();
let task1 = tokio::spawn(async move {
let _lock_a = a.lock().await;
tokio::time::sleep(Duration::from_millis(100)).await;
let _lock_b = b.lock().await; // Might deadlock!
});
let a = mutex_a.clone();
let b = mutex_b.clone();
let task2 = tokio::spawn(async move {
let _lock_b = b.lock().await;
tokio::time::sleep(Duration::from_millis(100)).await;
let _lock_a = a.lock().await; // Might deadlock!
});
tokio::time::sleep(Duration::from_secs(1)).await;
// Check for deadlocks
let graph = TaskGraph::from_inspector(&Inspector::global());
let deadlocks = graph.detect_potential_deadlocks();
if !deadlocks.is_empty() {
eprintln!("⚠️ Detected {} potential deadlocks!", deadlocks.len());
for cycle in deadlocks {
eprintln!(" Cycle: {:?}", cycle);
}
}
}
Resource Contention
Analyzing Shared Resources
use async_inspect::graph::*;
use std::sync::Arc;
use tokio::sync::Mutex;
#[tokio::main]
async fn main() {
let db_pool = Arc::new(Mutex::new(DatabasePool::new()));
// Spawn many workers
let mut handles = vec![];
for i in 0..100 {
let pool = db_pool.clone();
handles.push(tokio::spawn(async move {
let _conn = pool.lock().await; // Contention point!
query_database().await;
}));
}
for handle in handles {
handle.await.unwrap();
}
// Analyze contention
let graph = TaskGraph::from_inspector(&Inspector::global());
let shared = graph.find_tasks_sharing_resource("db_pool");
println!("Tasks contending for db_pool: {}", shared.len());
println!("Consider increasing pool size!");
}
Testing
Debugging Flaky Tests
#[tokio::test]
#[async_inspect::trace]
async fn test_user_creation() {
let user = create_user("Alice").await;
assert!(user.is_valid());
// If this fails, async-inspect shows where time was spent
Reporter::global().print_summary();
}
#[tokio::test(flavor = "multi_thread")]
#[async_inspect::trace]
async fn test_concurrent_access() {
let mut handles = vec![];
for i in 0..100 {
handles.push(tokio::spawn(async move {
access_shared_resource(i).await
}));
}
for handle in handles {
handle.await.unwrap();
}
// Check for race conditions
let stats = Inspector::global().stats();
assert_eq!(stats.failed_tasks, 0, "Some tasks failed!");
}
More Examples
Check out the examples/ directory in the repository for more:
basic_inspection.rs- Simple task trackingtokio_integration.rs- Tokio runtime integrationdeadlock_detection.rs- Finding deadlocksperformance_analysis.rs- Profiling slow coderelationship_graph.rs- Task relationshipsecosystem_integration.rs- Prometheus, OpenTelemetryproduction_ready.rs- Production configurationtui_monitor.rs- Terminal UI usage
Run any example:
cargo run --example basic_inspection
cargo run --example tui_monitor --features cli