Constellation, Spacedust, Slingshot, UFOs: atproto crates and services for microcosm

metrics

+33 -7
Cargo.lock
··· 2726 2726 "indexmap 2.9.0", 2727 2727 "ipnet", 2728 2728 "metrics", 2729 - "metrics-util", 2729 + "metrics-util 0.19.0", 2730 2730 "quanta", 2731 2731 "thiserror 1.0.69", 2732 2732 "tokio", ··· 2735 2735 2736 2736 [[package]] 2737 2737 name = "metrics-exporter-prometheus" 2738 - version = "0.17.1" 2738 + version = "0.17.2" 2739 2739 source = "registry+https://github.com/rust-lang/crates.io-index" 2740 - checksum = "989903b4c7abfa6827a8d1128ef42faf83f8969d429797c5431f236f2cae8b8b" 2740 + checksum = "2b166dea96003ee2531cf14833efedced545751d800f03535801d833313f8c15" 2741 2741 dependencies = [ 2742 2742 "base64 0.22.1", 2743 2743 "http-body-util", ··· 2747 2747 "indexmap 2.9.0", 2748 2748 "ipnet", 2749 2749 "metrics", 2750 - "metrics-util", 2750 + "metrics-util 0.20.0", 2751 2751 "quanta", 2752 2752 "thiserror 2.0.12", 2753 2753 "tokio", ··· 2782 2782 "metrics", 2783 2783 "quanta", 2784 2784 "rand 0.8.5", 2785 - "rand_xoshiro", 2785 + "rand_xoshiro 0.6.0", 2786 + "sketches-ddsketch", 2787 + ] 2788 + 2789 + [[package]] 2790 + name = "metrics-util" 2791 + version = "0.20.0" 2792 + source = "registry+https://github.com/rust-lang/crates.io-index" 2793 + checksum = "fe8db7a05415d0f919ffb905afa37784f71901c9a773188876984b4f769ab986" 2794 + dependencies = [ 2795 + "crossbeam-epoch", 2796 + "crossbeam-utils", 2797 + "hashbrown 0.15.2", 2798 + "metrics", 2799 + "quanta", 2800 + "rand 0.9.1", 2801 + "rand_xoshiro 0.7.0", 2786 2802 "sketches-ddsketch", 2787 2803 ] 2788 2804 ··· 3399 3415 ] 3400 3416 3401 3417 [[package]] 3418 + name = "rand_xoshiro" 3419 + version = "0.7.0" 3420 + source = "registry+https://github.com/rust-lang/crates.io-index" 3421 + checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41" 3422 + dependencies = [ 3423 + "rand_core 0.9.3", 3424 + ] 3425 + 3426 + [[package]] 3402 3427 name = "ratelimit" 3403 3428 version = "0.10.0" 3404 3429 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4162 4187 "links", 4163 4188 "log", 4164 4189 "metrics", 4165 - "metrics-exporter-prometheus 0.17.1", 4190 + "metrics-exporter-prometheus 0.17.2", 4166 4191 "rand 0.9.1", 4167 4192 "schemars", 4168 4193 "semver", ··· 4752 4777 "log", 4753 4778 "lsm-tree", 4754 4779 "metrics", 4755 - "metrics-exporter-prometheus 0.17.1", 4780 + "metrics-exporter-prometheus 0.17.2", 4756 4781 "schemars", 4757 4782 "semver", 4758 4783 "serde", ··· 5063 5088 "handlebars", 5064 5089 "hickory-resolver", 5065 5090 "metrics", 5091 + "metrics-exporter-prometheus 0.17.2", 5066 5092 "rand 0.9.1", 5067 5093 "reqwest", 5068 5094 "serde",
+1
who-am-i/Cargo.toml
··· 17 17 handlebars = { version = "6.3.2", features = ["dir_source"] } 18 18 hickory-resolver = "0.25.2" 19 19 metrics = "0.24.2" 20 + metrics-exporter-prometheus = { version = "0.17.2", features = ["http-listener"] } 20 21 rand = "0.9.1" 21 22 reqwest = { version = "0.12.22", features = ["native-tls-vendored"] } 22 23 serde = { version = "1.0.219", features = ["derive"] }
+4 -1
who-am-i/src/expiring_task_map.rs
··· 49 49 .run_until_cancelled(sleep(expiration)) 50 50 .await 51 51 .is_some() 52 - // is Some if the (sleep) task completed first 53 52 { 53 + // is Some if the (sleep) task completed first 54 54 map.remove(&k); 55 55 cancel.cancel(); 56 + metrics::counter!("whoami_task_map_completions", "result" => "expired") 57 + .increment(1); 56 58 } 57 59 }); 58 60 ··· 60 62 } 61 63 62 64 pub fn take(&self, key: &str) -> Option<JoinHandle<T>> { 65 + metrics::counter!("whoami_task_map_completions", "result" => "retrieved").increment(1); 63 66 // when the _guard drops, the token gets cancelled for us 64 67 self.0.map.remove(key).map(|(_, (_guard, handle))| handle) 65 68 }
+21 -1
who-am-i/src/main.rs
··· 1 1 use clap::{ArgAction, Parser}; 2 + use metrics_exporter_prometheus::PrometheusBuilder; 2 3 use tokio_util::sync::CancellationToken; 3 4 use who_am_i::serve; 4 5 ··· 35 36 let args = Args::parse(); 36 37 37 38 if args.allowed_hosts.is_empty() { 38 - panic!("at least one --one-click host must be set"); 39 + panic!("at least one --allowed-host host must be set"); 39 40 } 40 41 41 42 println!("starting with allowed_hosts hosts:"); 42 43 for host in &args.allowed_hosts { 43 44 println!(" - {host}"); 44 45 } 46 + 47 + if let Err(e) = install_metrics_server() { 48 + eprintln!("failed to install metrics server: {e:?}"); 49 + }; 45 50 46 51 serve(shutdown, args.app_secret, args.allowed_hosts, args.dev).await; 47 52 } 53 + 54 + fn install_metrics_server() -> Result<(), metrics_exporter_prometheus::BuildError> { 55 + println!("installing metrics server..."); 56 + let host = [0, 0, 0, 0]; 57 + let port = 8765; 58 + PrometheusBuilder::new() 59 + .set_enable_unit_suffix(false) 60 + .with_http_listener((host, port)) 61 + .install()?; 62 + println!( 63 + "metrics server installed! listening on http://{}.{}.{}.{}:{port}", 64 + host[0], host[1], host[2], host[3] 65 + ); 66 + Ok(()) 67 + }
+10 -7
who-am-i/src/oauth.rs
··· 198 198 &self, 199 199 query: &str, 200 200 ) -> core::result::Result<Vec<String>, Box<dyn std::error::Error + Send + Sync>> { 201 - Ok(self 202 - .0 203 - .txt_lookup(query) 204 - .await? 205 - .iter() 206 - .map(|txt| txt.to_string()) 207 - .collect()) 201 + match self.0.txt_lookup(query).await { 202 + Ok(r) => { 203 + metrics::counter!("whoami_resolve_dns_txt", "success" => "true").increment(1); 204 + Ok(r.iter().map(|r| r.to_string()).collect()) 205 + } 206 + Err(e) => { 207 + metrics::counter!("whoami_resolve_dns_txt", "success" => "false").increment(1); 208 + Err(e.into()) 209 + } 210 + } 208 211 } 209 212 }
+25 -6
who-am-i/src/server.rs
··· 172 172 headers: HeaderMap, 173 173 ) -> impl IntoResponse { 174 174 let err = |reason, check_frame| { 175 + metrics::counter!("whoami_auth_prompt", "ok" => "false", "reason" => reason).increment(1); 175 176 let info = json!({ "reason": reason, "check_frame": check_frame }); 176 177 let html = RenderHtml("prompt-error", engine.clone(), info); 177 178 (StatusCode::BAD_REQUEST, html).into_response() ··· 222 223 shutdown.child_token(), 223 224 ); 224 225 226 + metrics::counter!("whoami_auth_prompt", "ok" => "true", "known" => "true").increment(1); 225 227 let info = json!({ 226 228 "did": did, 227 229 "fetch_key": fetch_key, 228 230 "parent_host": parent_host, 229 231 "parent_origin": parent_origin, 230 232 }); 231 - 232 233 (frame_headers, jar, RenderHtml("prompt", engine, info)).into_response() 233 234 } else { 235 + metrics::counter!("whoami_auth_prompt", "ok" => "true", "known" => "false").increment(1); 234 236 let info = json!({ 235 237 "parent_host": parent_host, 236 238 "parent_origin": parent_origin, ··· 250 252 }): State<AppState>, 251 253 Query(params): Query<UserInfoParams>, 252 254 ) -> impl IntoResponse { 253 - let err = |status, reason| (status, Json(json!({ "reason": reason }))).into_response(); 255 + let err = |status, reason: &str| { 256 + metrics::counter!("whoami_user_info", "found" => "false", "reason" => reason.to_string()) 257 + .increment(1); 258 + (status, Json(json!({ "reason": reason }))).into_response() 259 + }; 254 260 255 261 let Some(task_handle) = resolve_handles.take(&params.fetch_key) else { 256 262 return err(StatusCode::NOT_FOUND, "fetch key does not exist or expired"); ··· 279 285 StatusCode::INTERNAL_SERVER_ERROR, 280 286 &format!("handle appears invalid: {reason}"), 281 287 ), 282 - Ok(Ok(handle)) => Json(json!({ "handle": handle })).into_response(), 288 + Ok(Ok(handle)) => { 289 + metrics::counter!("whoami_user_info", "found" => "true").increment(1); 290 + Json(json!({ "handle": handle })).into_response() 291 + } 283 292 } 284 293 } 285 294 ··· 299 308 use atrium_identity::Error as IdError; 300 309 use atrium_oauth::Error as OAuthError; 301 310 302 - let err = |code, reason| { 311 + let err = |code, reason: &str| { 312 + metrics::counter!("whoami_auth_start", "ok" => "false", "reason" => reason.to_string()) 313 + .increment(1); 303 314 let info = json!({ 304 315 "result": "fail", 305 316 "reason": reason, ··· 308 319 }; 309 320 310 321 match oauth.begin(&params.handle).await { 311 - Ok(auth_url) => (jar, Redirect::to(&auth_url)).into_response(), 312 322 Err(OAuthError::Identity( 313 323 IdError::NotFound | IdError::HttpStatus(StatusCode::NOT_FOUND), 314 324 )) => err(StatusCode::NOT_FOUND, "handle not found"), ··· 316 326 Err(e) => { 317 327 eprintln!("begin auth failed: {e:?}"); 318 328 err(StatusCode::INTERNAL_SERVER_ERROR, "unknown") 329 + } 330 + Ok(auth_url) => { 331 + metrics::counter!("whoami_auth_start", "ok" => "true").increment(1); 332 + (jar, Redirect::to(&auth_url)).into_response() 319 333 } 320 334 } 321 335 } ··· 331 345 Query(params): Query<OAuthCallbackParams>, 332 346 jar: SignedCookieJar, 333 347 ) -> Response { 334 - let err = |code, result, reason| { 348 + let err = |code, result, reason: &str| { 349 + metrics::counter!("whoami_auth_complete", "ok" => "false", "reason" => reason.to_string()) 350 + .increment(1); 335 351 let info = json!({ 336 352 "result": result, 337 353 "reason": reason, ··· 378 394 }, 379 395 shutdown.child_token(), 380 396 ); 397 + 398 + metrics::counter!("whoami_auth_complete", "ok" => "true").increment(1); 381 399 let info = json!({ 382 400 "did": did, 383 401 "fetch_key": fetch_key, ··· 386 404 } 387 405 388 406 async fn disconnect(jar: SignedCookieJar) -> impl IntoResponse { 407 + metrics::counter!("whoami_disconnect").increment(1); 389 408 let jar = jar.remove(DID_COOKIE_KEY); 390 409 (jar, Json(json!({ "ok": true }))) 391 410 }