From 11b7c6e9d6e48d5d2ff253d126b5192824571592 Mon Sep 17 00:00:00 2001 From: Leynos Date: Tue, 22 Jul 2025 02:56:38 +0100 Subject: [PATCH 01/10] Add metrics instrumentation and Prometheus example --- Cargo.lock | 1572 ++++++++++++++++- Cargo.toml | 8 + ...eframe-a-guide-to-production-resilience.md | 18 + docs/roadmap.md | 6 +- ...-set-philosophy-and-capability-maturity.md | 13 + src/app.rs | 5 + src/connection.rs | 9 +- src/lib.rs | 2 + src/metrics.rs | 45 + tests/cucumber.rs | 12 + tests/features/metrics.feature | 5 + tests/metrics.rs | 23 + tests/steps/mod.rs | 32 + wireframe_testing/Cargo.toml | 1 + 14 files changed, 1682 insertions(+), 69 deletions(-) create mode 100644 src/metrics.rs create mode 100644 tests/cucumber.rs create mode 100644 tests/features/metrics.feature create mode 100644 tests/metrics.rs create mode 100644 tests/steps/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 2c1e50a9..cf16178b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,18 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -26,6 +38,62 @@ dependencies = [ "memchr", ] +[[package]] +name = "anstream" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + [[package]] name = "async-stream" version = "0.3.6" @@ -59,12 +127,41 @@ dependencies = [ "syn", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "aws-lc-rs" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08b5d4e069cbc868041a64bd68dc8cb39a0d79585cd6c5a24caa8c2d622121be" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "backtrace" version = "0.3.75" @@ -80,6 +177,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bincode" version = "2.0.1" @@ -100,6 +203,29 @@ dependencies = [ "virtue", ] +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + [[package]] name = "bit-set" version = "0.8.0" @@ -121,6 +247,28 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "bstr" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "bytecount" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" + [[package]] name = "bytes" version = "1.10.1" @@ -133,15 +281,206 @@ version = "1.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" dependencies = [ + "jobserver", + "libc", "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", + "terminal_size", +] + +[[package]] +name = "clap_derive" +version = "4.5.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.59.0", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "cucumber" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cd12917efc3a8b069a4975ef3cb2f2d835d42d04b3814d90838488f9dd9bf69" +dependencies = [ + "anyhow", + "clap", + "console", + "cucumber-codegen", + "cucumber-expressions", + "derive_more", + "drain_filter_polyfill", + "either", + "futures", + "gherkin", + "globwalk", + "humantime", + "inventory", + "itertools 0.13.0", + "lazy-regex", + "linked-hash-map", + "once_cell", + "pin-project", + "regex", + "sealed", + "smart-default", +] + +[[package]] +name = "cucumber-codegen" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e19cd9e8e7cfd79fbf844eb6a7334117973c01f6bad35571262b00891e60f1c" +dependencies = [ + "cucumber-expressions", + "inflections", + "itertools 0.13.0", + "proc-macro2", + "quote", + "regex", + "syn", + "synthez", +] + +[[package]] +name = "cucumber-expressions" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d794fed319eea24246fb5f57632f7ae38d61195817b7eb659455aa5bdd7c1810" +dependencies = [ + "derive_more", + "either", + "nom", + "nom_locate", + "regex", + "regex-syntax 0.7.5", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -155,6 +494,53 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "derive_more" +version = "0.99.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "drain_filter_polyfill" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "errno" version = "0.3.13" @@ -177,6 +563,18 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" version = "0.3.31" @@ -246,81 +644,407 @@ checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" name = "futures-task" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generator" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gherkin" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20b79820c0df536d1f3a089a2fa958f61cb96ce9e0f3f8f507f5a31179567755" +dependencies = [ + "heck 0.4.1", + "peg", + "quote", + "serde", + "serde_json", + "syn", + "textwrap", + "thiserror 1.0.69", + "typed-builder", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "globset" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "globwalk" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" +dependencies = [ + "bitflags", + "ignore", + "walkdir", +] + +[[package]] +name = "h2" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +dependencies = [ + "foldhash", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" + +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "libc", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "ignore" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.9", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown 0.15.4", +] + +[[package]] +name = "inflections" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" + +[[package]] +name = "inventory" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab08d7cd2c5897f2c949e5383ea7c7db03fb19130ffcfbf7eda795137ae3cb83" +dependencies = [ + "rustversion", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] -name = "futures-timer" -version = "3.0.3" +name = "is_terminal_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] -name = "futures-util" -version = "0.3.31" +name = "itertools" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", + "either", ] [[package]] -name = "generator" -version = "0.8.5" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ - "cc", - "cfg-if", - "libc", - "log", - "rustversion", - "windows", + "either", ] [[package]] -name = "getrandom" -version = "0.3.3" +name = "itoa" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasi 0.14.2+wasi-0.2.4", -] +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] -name = "gimli" -version = "0.31.1" +name = "jobserver" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] [[package]] -name = "glob" -version = "0.3.2" +name = "js-sys" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] [[package]] -name = "hashbrown" -version = "0.14.5" +name = "lazy-regex" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126" +dependencies = [ + "lazy-regex-proc_macros", + "once_cell", + "regex", +] [[package]] -name = "hashbrown" -version = "0.15.4" +name = "lazy-regex-proc_macros" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn", +] [[package]] name = "lazy_static" @@ -328,6 +1052,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "leaky-bucket" version = "1.1.2" @@ -345,6 +1075,28 @@ version = "0.2.173" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -408,6 +1160,63 @@ version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +[[package]] +name = "metrics" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dea7ac8057892855ec285c440160265225438c3c45072613c25a4b26e98ef5" +dependencies = [ + "ahash", + "portable-atomic", +] + +[[package]] +name = "metrics-exporter-prometheus" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b166dea96003ee2531cf14833efedced545751d800f03535801d833313f8c15" +dependencies = [ + "base64", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "indexmap", + "ipnet", + "metrics", + "metrics-util", + "quanta", + "thiserror 2.0.12", + "tokio", + "tracing", +] + +[[package]] +name = "metrics-util" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe8db7a05415d0f919ffb905afa37784f71901c9a773188876984b4f769ab986" +dependencies = [ + "aho-corasick", + "crossbeam-epoch", + "crossbeam-utils", + "hashbrown 0.15.4", + "indexmap", + "metrics", + "ordered-float", + "quanta", + "radix_trie", + "rand", + "rand_xoshiro", + "sketches-ddsketch", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -428,6 +1237,36 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom_locate" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" +dependencies = [ + "bytecount", + "memchr", + "nom", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -462,6 +1301,27 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + [[package]] name = "overload" version = "0.1.1" @@ -491,6 +1351,53 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "peg" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f76678828272f177ac33b7e2ac2e3e73cc6c1cd1e3e387928aa69562fa51367" +dependencies = [ + "peg-macros", + "peg-runtime", +] + +[[package]] +name = "peg-macros" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "636d60acf97633e48d266d7415a9355d4389cea327a193f87df395d88cd2b14d" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] + +[[package]] +name = "peg-runtime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555b1514d2d99d78150d3c799d4c357a3e2c2a8062cd108e93a06d9057629c5" + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -503,6 +1410,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -512,6 +1425,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -541,6 +1464,21 @@ dependencies = [ "unarray", ] +[[package]] +name = "quanta" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi 0.11.1+wasi-snapshot-preview1", + "web-sys", + "winapi", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -562,6 +1500,16 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.9.1" @@ -588,7 +1536,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom", + "getrandom 0.3.3", ] [[package]] @@ -600,6 +1548,24 @@ dependencies = [ "rand_core", ] +[[package]] +name = "rand_xoshiro" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41" +dependencies = [ + "rand_core", +] + +[[package]] +name = "raw-cpuid" +version = "11.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_syscall" version = "0.5.13" @@ -647,6 +1613,12 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + [[package]] name = "regex-syntax" version = "0.8.5" @@ -659,6 +1631,20 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rstest" version = "0.18.2" @@ -689,31 +1675,97 @@ dependencies = [ ] [[package]] -name = "rustc-demangle" -version = "0.1.25" +name = "rustc-demangle" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] [[package]] -name = "rustc_version" -version = "0.4.1" +name = "rustls-native-certs" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" dependencies = [ - "semver", + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", ] [[package]] -name = "rustix" -version = "1.0.7" +name = "rustls-pki-types" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.59.0", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", ] [[package]] @@ -734,6 +1786,21 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scc" version = "2.3.4" @@ -743,6 +1810,15 @@ dependencies = [ "sdd", ] +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -761,6 +1837,41 @@ version = "3.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "584e070911c7017da6cb2eb0788d09f43d789029b5877d3e5ecc8acf86ceee21" +[[package]] +name = "sealed" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a8caec23b7800fb97971a1c6ae365b6239aaeddfb934d6265f8505e795699d" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.26" @@ -787,6 +1898,18 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "serial_test" version = "3.2.0" @@ -836,6 +1959,12 @@ dependencies = [ "libc", ] +[[package]] +name = "sketches-ddsketch" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1e9a774a6c28142ac54bb25d25562e6bcf957493a184f15ad4eebccb23e410a" + [[package]] name = "slab" version = "0.4.9" @@ -851,6 +1980,23 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "smart-default" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + [[package]] name = "socket2" version = "0.5.10" @@ -861,6 +2007,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.103" @@ -872,6 +2030,39 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synthez" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d2c2202510a1e186e63e596d9318c91a8cbe85cd1a56a7be0c333e5f59ec8d" +dependencies = [ + "syn", + "synthez-codegen", + "synthez-core", +] + +[[package]] +name = "synthez-codegen" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f724aa6d44b7162f3158a57bccd871a77b39a4aef737e01bcdff41f4772c7746" +dependencies = [ + "syn", + "synthez-core", +] + +[[package]] +name = "synthez-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bfa6ec52465e2425fd43ce5bbbe0f0b623964f7c63feb6b10980e816c654ea" +dependencies = [ + "proc-macro2", + "quote", + "sealed", + "syn", +] + [[package]] name = "tempfile" version = "3.20.0" @@ -879,12 +2070,73 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom", + "getrandom 0.3.3", "once_cell", - "rustix", + "rustix 1.0.7", + "windows-sys 0.59.0", +] + +[[package]] +name = "terminal_size" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" +dependencies = [ + "rustix 1.0.7", "windows-sys 0.59.0", ] +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.9" @@ -922,6 +2174,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.15" @@ -937,6 +2199,12 @@ dependencies = [ "tokio", ] +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.41" @@ -999,6 +2267,32 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typed-builder" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe83c85a85875e8c4cb9ce4a890f05b23d38cd0d47647db7895d3d2a79566d2" +dependencies = [ + "typed-builder-macro", +] + +[[package]] +name = "typed-builder-macro" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29a3151c41d0b13e3d011f98adc24434560ef06673a155a6c7f66b9879eecce2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unarray" version = "0.1.4" @@ -1011,12 +2305,36 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-width" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "unty" version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "valuable" version = "0.1.1" @@ -1029,6 +2347,12 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "virtue" version = "0.0.18" @@ -1044,6 +2368,25 @@ dependencies = [ "libc", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -1059,6 +2402,85 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1075,6 +2497,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -1282,12 +2713,16 @@ dependencies = [ "async-trait", "bincode", "bytes", + "cucumber", "dashmap", "futures", "leaky-bucket", "log", "logtest", "loom", + "metrics", + "metrics-exporter-prometheus", + "metrics-util", "proptest", "rstest", "serde", @@ -1306,6 +2741,7 @@ dependencies = [ "bytes", "log", "logtest", + "metrics-util", "rstest", "tokio", "wireframe", @@ -1339,3 +2775,9 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index 181e5c76..8e775abd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,8 @@ log = "0.4" dashmap = "5" leaky-bucket = "1.1" tracing = { version = ">=0.1.40, <0.2.0", features = ["log", "log-always"] } +metrics = "0.24" +metrics-exporter-prometheus = { version = "0.17", optional = true, features = ["http-listener"] } [dev-dependencies] rstest = "0.18.2" @@ -25,9 +27,15 @@ loom = "^0.7" async-stream = "0.3" tokio = { version = "1", default-features = false, features = ["test-util"] } serial_test = "3.1" +cucumber = "0.21" +metrics-util = "0.20" [features] advanced-tests = [] [lints.clippy] pedantic = "warn" + +[[test]] +name = "cucumber" +harness = false diff --git a/docs/hardening-wireframe-a-guide-to-production-resilience.md b/docs/hardening-wireframe-a-guide-to-production-resilience.md index bbef9224..9c0ca2b7 100644 --- a/docs/hardening-wireframe-a-guide-to-production-resilience.md +++ b/docs/hardening-wireframe-a-guide-to-production-resilience.md @@ -347,6 +347,24 @@ A separate part of the application is then responsible for consuming from the DLQ's receiver to inspect, log, and re-process these failed messages, ensuring zero message loss even under transient high load. +## 5. Metrics and Observability + +Operational visibility is critical in production. `wireframe` updates counters +and gauges through the `metrics` crate. A Prometheus recorder can be installed +as shown below: + +```rust +use metrics_exporter_prometheus::PrometheusBuilder; + +let handle = PrometheusBuilder::new() + .install_recorder() + .expect("recorder install"); +println!("{}", handle.render()); +``` + +This exposes processed frame counts, error totals and the active connection +gauge for scraping by monitoring systems. + By systematically implementing these hardening strategies, `wireframe` will provide the guarantees of stability, security, and reliability expected of a foundational piece of network infrastructure. diff --git a/docs/roadmap.md b/docs/roadmap.md index 6beba8d3..6c1fd0a5 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -150,12 +150,12 @@ production environments. - [x] Create a helper crate for test logging setup (`wireframe_testing/src/logging.rs`). -- [ ] **Metrics & Observability:** +- [x] **Metrics & Observability:** - - [ ] Expose key operational metrics (e.g., active connections, messages per + - [x] Expose key operational metrics (e.g., active connections, messages per second, error rates). - - [ ] Provide an integration guide for popular monitoring systems (e.g., + - [x] Provide an integration guide for popular monitoring systems (e.g., Prometheus). - [ ] **Advanced Error Handling:** diff --git a/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md b/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md index a319beca..42ed920e 100644 --- a/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md +++ b/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md @@ -285,6 +285,19 @@ A production system is a black box without good instrumentation. `wireframe` - `wireframe_reassembly_errors_total` (Counter) +Metrics are emitted using the `metrics` crate. A `PrometheusBuilder` can +install a global recorder so external systems may scrape the values. The +example below exposes a minimal recorder: + +```rust +use metrics_exporter_prometheus::PrometheusBuilder; + +let handle = PrometheusBuilder::new() + .install_recorder() + .expect("install recorder"); +println!("{}", handle.render()); +``` + ```mermaid sequenceDiagram participant Client diff --git a/src/app.rs b/src/app.rs index 063d3b39..0328f9b0 100644 --- a/src/app.rs +++ b/src/app.rs @@ -654,11 +654,13 @@ where let (env, _) = match self.parse_envelope(frame) { Ok(result) => { *deser_failures = 0; + crate::metrics::inc_frames(crate::metrics::Direction::Inbound); result } Err(e) => { *deser_failures += 1; tracing::warn!(error = ?e, "failed to deserialize message"); + crate::metrics::inc_errors(); if *deser_failures >= MAX_DESER_FAILURES { return Err(io::Error::new( io::ErrorKind::InvalidData, @@ -679,14 +681,17 @@ where }; if let Err(e) = self.send_response(stream, &response).await { tracing::warn!(error = %e, "failed to send response"); + crate::metrics::inc_errors(); } } Err(e) => { tracing::warn!(id = env.id, error = ?e, "handler error"); + crate::metrics::inc_errors(); } } } else { tracing::warn!("no handler for message id {}", env.id); + crate::metrics::inc_errors(); } Ok(()) diff --git a/src/connection.rs b/src/connection.rs index a6bdab70..ddb3537e 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -29,12 +29,16 @@ struct ActiveConnection; impl ActiveConnection { fn new() -> Self { ACTIVE_CONNECTIONS.fetch_add(1, Ordering::Relaxed); + crate::metrics::inc_connections(); Self } } impl Drop for ActiveConnection { - fn drop(&mut self) { ACTIVE_CONNECTIONS.fetch_sub(1, Ordering::Relaxed); } + fn drop(&mut self) { + ACTIVE_CONNECTIONS.fetch_sub(1, Ordering::Relaxed); + crate::metrics::dec_connections(); + } } /// Return the current number of active connections. @@ -322,6 +326,7 @@ where let mut frame = frame; self.hooks.before_send(&mut frame, &mut self.ctx); out.push(frame); + crate::metrics::inc_frames(crate::metrics::Direction::Outbound); } /// Common logic for handling closed receivers. @@ -422,12 +427,14 @@ where Some(Ok(mut frame)) => { self.hooks.before_send(&mut frame, &mut self.ctx); out.push(frame); + crate::metrics::inc_frames(crate::metrics::Direction::Outbound); } Some(Err(WireframeError::Protocol(e))) => { warn!(error = ?e, "protocol error"); self.hooks.handle_error(e, &mut self.ctx); state.mark_closed(); self.hooks.on_command_end(&mut self.ctx); + crate::metrics::inc_errors(); } Some(Err(e)) => return Err(e), None => { diff --git a/src/lib.rs b/src/lib.rs index 4b6e4015..34fb32a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ pub mod extractor; pub mod frame; pub mod hooks; pub mod message; +pub mod metrics; pub mod middleware; pub mod preamble; pub mod push; @@ -22,5 +23,6 @@ pub mod session; pub use connection::ConnectionActor; pub use hooks::{ConnectionContext, ProtocolHooks, WireframeProtocol}; +pub use metrics::{CONNECTIONS_ACTIVE, Direction, ERRORS_TOTAL, FRAMES_PROCESSED}; pub use response::{FrameStream, Response, WireframeError}; pub use session::{ConnectionId, SessionRegistry}; diff --git a/src/metrics.rs b/src/metrics.rs new file mode 100644 index 00000000..df761594 --- /dev/null +++ b/src/metrics.rs @@ -0,0 +1,45 @@ +//! Metric helpers for `wireframe`. +//! +//! This module defines metric names and simple helper functions +//! wrapping the [`metrics`](https://docs.rs/metrics) crate. + +use metrics::{counter, gauge}; + +/// Name of the gauge tracking active connections. +pub const CONNECTIONS_ACTIVE: &str = "wireframe_connections_active"; +/// Name of the counter tracking processed frames. +pub const FRAMES_PROCESSED: &str = "wireframe_frames_processed_total"; +/// Name of the counter tracking error occurrences. +pub const ERRORS_TOTAL: &str = "wireframe_errors_total"; + +/// Direction of frame processing. +#[derive(Clone, Copy)] +pub enum Direction { + /// Inbound frames received from a client. + Inbound, + /// Outbound frames sent to a client. + Outbound, +} + +impl Direction { + fn as_str(self) -> &'static str { + match self { + Direction::Inbound => "inbound", + Direction::Outbound => "outbound", + } + } +} + +/// Increment the active connections gauge. +pub fn inc_connections() { gauge!(CONNECTIONS_ACTIVE).increment(1.0); } + +/// Decrement the active connections gauge. +pub fn dec_connections() { gauge!(CONNECTIONS_ACTIVE).decrement(1.0); } + +/// Record a processed frame for the given direction. +pub fn inc_frames(direction: Direction) { + counter!(FRAMES_PROCESSED, "direction" => direction.as_str()).increment(1); +} + +/// Record an error occurrence. +pub fn inc_errors() { counter!(ERRORS_TOTAL).increment(1); } diff --git a/tests/cucumber.rs b/tests/cucumber.rs new file mode 100644 index 00000000..6b660139 --- /dev/null +++ b/tests/cucumber.rs @@ -0,0 +1,12 @@ +use cucumber::World as _; + +#[derive(Debug, Default, cucumber::World)] +pub struct MetricsWorld { + pub exporter: Option, + pub output: Option, +} + +mod steps; + +#[tokio::main] +async fn main() { MetricsWorld::run("tests/features").await; } diff --git a/tests/features/metrics.feature b/tests/features/metrics.feature new file mode 100644 index 00000000..07ed7db7 --- /dev/null +++ b/tests/features/metrics.feature @@ -0,0 +1,5 @@ +Feature: metrics exporter + Scenario: active connections metric recorded + Given a metrics recorder + When a connection runs with no frames + Then the exporter output includes active connections gauge diff --git a/tests/metrics.rs b/tests/metrics.rs new file mode 100644 index 00000000..3fbd2b22 --- /dev/null +++ b/tests/metrics.rs @@ -0,0 +1,23 @@ +use metrics_util::debugging::DebuggingRecorder; +use tokio_util::sync::CancellationToken; +use wireframe::{connection::ConnectionActor, push::PushQueues}; + +#[tokio::test] +async fn outbound_frame_metric_increments() { + let recorder = DebuggingRecorder::new(); + let snapshotter = recorder.snapshotter(); + recorder.install().expect("install"); + + let (queues, handle) = PushQueues::::bounded(1, 1); + handle.push_high_priority(1).await.unwrap(); + let token = CancellationToken::new(); + let mut actor: ConnectionActor<_, ()> = ConnectionActor::new(queues, handle, None, token); + let mut out = Vec::new(); + actor.run(&mut out).await.unwrap(); + + let metrics = snapshotter.snapshot().into_vec(); + let found = metrics + .iter() + .any(|(k, ..)| k.key().name() == wireframe::metrics::FRAMES_PROCESSED); + assert!(found, "frames_processed metric not recorded"); +} diff --git a/tests/steps/mod.rs b/tests/steps/mod.rs new file mode 100644 index 00000000..aa5eda1e --- /dev/null +++ b/tests/steps/mod.rs @@ -0,0 +1,32 @@ +use cucumber::{given, then, when}; +use tokio_util::sync::CancellationToken; +use wireframe::{connection::ConnectionActor, push::PushQueues}; + +use crate::MetricsWorld; + +#[given("a metrics recorder")] +fn metrics_recorder(world: &mut MetricsWorld) { + let handle = metrics_exporter_prometheus::PrometheusBuilder::new() + .install_recorder() + .expect("recorder install"); + world.exporter = Some(handle); +} + +#[when("a connection runs with no frames")] +async fn run_connection(world: &mut MetricsWorld) { + let (queues, handle) = PushQueues::::bounded(1, 1); + let token = CancellationToken::new(); + let mut actor: ConnectionActor<_, ()> = ConnectionActor::new(queues, handle, None, token); + let mut out = Vec::new(); + actor.run(&mut out).await.unwrap(); + if let Some(h) = &world.exporter { + h.run_upkeep(); + world.output = Some(h.render()); + } +} + +#[then("the exporter output includes active connections gauge")] +fn check_output(world: &mut MetricsWorld) { + let out = world.output.as_ref().expect("no output"); + assert!(out.contains("wireframe_connections_active"), "{out}"); +} diff --git a/wireframe_testing/Cargo.toml b/wireframe_testing/Cargo.toml index 832e269c..57643b84 100644 --- a/wireframe_testing/Cargo.toml +++ b/wireframe_testing/Cargo.toml @@ -11,3 +11,4 @@ bytes = "^1.0" rstest = "0.18.2" logtest = "2" log = "0.4" +metrics-util = "0.20" From cd5becd5a6e324930d5b1f6fdc0f63efaefbee31 Mon Sep 17 00:00:00 2001 From: Leynos Date: Tue, 22 Jul 2025 07:16:37 +0100 Subject: [PATCH 02/10] Gate metrics behind feature --- Cargo.lock | 783 +++--------------- Cargo.toml | 8 +- ...eframe-a-guide-to-production-resilience.md | 17 +- ...-set-philosophy-and-capability-maturity.md | 15 +- src/app.rs | 10 +- src/connection.rs | 2 +- src/metrics.rs | 44 +- tests/cucumber.rs | 12 - tests/features/metrics.feature | 5 - tests/metrics.rs | 52 +- tests/steps/mod.rs | 32 - 11 files changed, 217 insertions(+), 763 deletions(-) delete mode 100644 tests/cucumber.rs delete mode 100644 tests/features/metrics.feature delete mode 100644 tests/steps/mod.rs diff --git a/Cargo.lock b/Cargo.lock index cf16178b..97cfd39b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,62 +38,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "anstream" -version = "0.6.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" - -[[package]] -name = "anstyle-parse" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.59.0", -] - -[[package]] -name = "anyhow" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" - [[package]] name = "async-stream" version = "0.3.6" @@ -135,9 +79,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" @@ -174,7 +118,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -212,7 +156,7 @@ dependencies = [ "bitflags", "cexpr", "clang-sys", - "itertools 0.12.1", + "itertools", "lazy_static", "lazycell", "log", @@ -247,28 +191,12 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" -[[package]] -name = "bstr" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "bumpalo" version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" -[[package]] -name = "bytecount" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" - [[package]] name = "bytes" version = "1.10.1" @@ -277,9 +205,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.27" +version = "1.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" dependencies = [ "jobserver", "libc", @@ -312,47 +240,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "clap" -version = "4.5.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", - "terminal_size", -] - -[[package]] -name = "clap_derive" -version = "4.5.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" - [[package]] name = "cmake" version = "0.1.54" @@ -362,25 +249,6 @@ dependencies = [ "cc", ] -[[package]] -name = "colorchoice" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" - -[[package]] -name = "console" -version = "0.15.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" -dependencies = [ - "encode_unicode", - "libc", - "once_cell", - "unicode-width", - "windows-sys 0.59.0", -] - [[package]] name = "core-foundation" version = "0.10.1" @@ -397,16 +265,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - [[package]] name = "crossbeam-epoch" version = "0.9.18" @@ -422,65 +280,6 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[package]] -name = "cucumber" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cd12917efc3a8b069a4975ef3cb2f2d835d42d04b3814d90838488f9dd9bf69" -dependencies = [ - "anyhow", - "clap", - "console", - "cucumber-codegen", - "cucumber-expressions", - "derive_more", - "drain_filter_polyfill", - "either", - "futures", - "gherkin", - "globwalk", - "humantime", - "inventory", - "itertools 0.13.0", - "lazy-regex", - "linked-hash-map", - "once_cell", - "pin-project", - "regex", - "sealed", - "smart-default", -] - -[[package]] -name = "cucumber-codegen" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e19cd9e8e7cfd79fbf844eb6a7334117973c01f6bad35571262b00891e60f1c" -dependencies = [ - "cucumber-expressions", - "inflections", - "itertools 0.13.0", - "proc-macro2", - "quote", - "regex", - "syn", - "synthez", -] - -[[package]] -name = "cucumber-expressions" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d794fed319eea24246fb5f57632f7ae38d61195817b7eb659455aa5bdd7c1810" -dependencies = [ - "derive_more", - "either", - "nom", - "nom_locate", - "regex", - "regex-syntax 0.7.5", -] - [[package]] name = "dashmap" version = "5.5.3" @@ -494,23 +293,6 @@ dependencies = [ "parking_lot_core", ] -[[package]] -name = "derive_more" -version = "0.99.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "drain_filter_polyfill" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408" - [[package]] name = "dunce" version = "1.0.5" @@ -523,12 +305,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "encode_unicode" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" - [[package]] name = "endian-type" version = "0.1.2" @@ -548,7 +324,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -707,23 +483,6 @@ dependencies = [ "wasi 0.14.2+wasi-0.2.4", ] -[[package]] -name = "gherkin" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20b79820c0df536d1f3a089a2fa958f61cb96ce9e0f3f8f507f5a31179567755" -dependencies = [ - "heck 0.4.1", - "peg", - "quote", - "serde", - "serde_json", - "syn", - "textwrap", - "thiserror 1.0.69", - "typed-builder", -] - [[package]] name = "gimli" version = "0.31.1" @@ -736,30 +495,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" -[[package]] -name = "globset" -version = "0.4.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "globwalk" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" -dependencies = [ - "bitflags", - "ignore", - "walkdir", -] - [[package]] name = "h2" version = "0.4.11" @@ -794,18 +529,6 @@ dependencies = [ "foldhash", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - [[package]] name = "home" version = "0.5.11" @@ -861,12 +584,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humantime" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" - [[package]] name = "hyper" version = "1.6.0" @@ -926,22 +643,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "ignore" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata 0.4.9", - "same-file", - "walkdir", - "winapi-util", -] - [[package]] name = "indexmap" version = "2.10.0" @@ -953,18 +654,14 @@ dependencies = [ ] [[package]] -name = "inflections" -version = "1.1.1" +name = "io-uring" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" - -[[package]] -name = "inventory" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab08d7cd2c5897f2c949e5383ea7c7db03fb19130ffcfbf7eda795137ae3cb83" +checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" dependencies = [ - "rustversion", + "bitflags", + "cfg-if", + "libc", ] [[package]] @@ -973,12 +670,6 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - [[package]] name = "itertools" version = "0.12.1" @@ -988,15 +679,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.15" @@ -1023,29 +705,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "lazy-regex" -version = "3.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126" -dependencies = [ - "lazy-regex-proc_macros", - "once_cell", - "regex", -] - -[[package]] -name = "lazy-regex-proc_macros" -version = "3.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1" -dependencies = [ - "proc-macro2", - "quote", - "regex", - "syn", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1071,9 +730,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.173" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libloading" @@ -1082,15 +741,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets", + "windows-targets 0.53.2", ] -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -1186,7 +839,7 @@ dependencies = [ "metrics", "metrics-util", "quanta", - "thiserror 2.0.12", + "thiserror", "tokio", "tracing", ] @@ -1256,17 +909,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nom_locate" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" -dependencies = [ - "bytecount", - "memchr", - "nom", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1301,12 +943,6 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "once_cell_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" - [[package]] name = "openssl-probe" version = "0.1.6" @@ -1348,54 +984,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", -] - -[[package]] -name = "peg" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f76678828272f177ac33b7e2ac2e3e73cc6c1cd1e3e387928aa69562fa51367" -dependencies = [ - "peg-macros", - "peg-runtime", -] - -[[package]] -name = "peg-macros" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "636d60acf97633e48d266d7415a9355d4389cea327a193f87df395d88cd2b14d" -dependencies = [ - "peg-runtime", - "proc-macro2", - "quote", -] - -[[package]] -name = "peg-runtime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555b1514d2d99d78150d3c799d4c357a3e2c2a8062cd108e93a06d9057629c5" - -[[package]] -name = "pin-project" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "windows-targets 0.52.6", ] [[package]] @@ -1427,9 +1016,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.34" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" +checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" dependencies = [ "proc-macro2", "syn", @@ -1512,9 +1101,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", "rand_core", @@ -1568,9 +1157,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec" dependencies = [ "bitflags", ] @@ -1613,12 +1202,6 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - [[package]] name = "regex-syntax" version = "0.8.5" @@ -1710,15 +1293,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1786,21 +1369,6 @@ dependencies = [ "wait-timeout", ] -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "scc" version = "2.3.4" @@ -1833,21 +1401,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sdd" -version = "3.0.8" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584e070911c7017da6cb2eb0788d09f43d789029b5877d3e5ecc8acf86ceee21" - -[[package]] -name = "sealed" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a8caec23b7800fb97971a1c6ae365b6239aaeddfb934d6265f8505e795699d" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn", -] +checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" [[package]] name = "security-framework" @@ -1898,18 +1454,6 @@ dependencies = [ "syn", ] -[[package]] -name = "serde_json" -version = "1.0.141" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - [[package]] name = "serial_test" version = "3.2.0" @@ -1967,12 +1511,9 @@ checksum = "c1e9a774a6c28142ac54bb25d25562e6bcf957493a184f15ad4eebccb23e410a" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" @@ -1980,23 +1521,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "smart-default" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "smawk" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" - [[package]] name = "socket2" version = "0.5.10" @@ -2007,12 +1531,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "subtle" version = "2.6.1" @@ -2021,48 +1539,15 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.103" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "synthez" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d2c2202510a1e186e63e596d9318c91a8cbe85cd1a56a7be0c333e5f59ec8d" -dependencies = [ - "syn", - "synthez-codegen", - "synthez-core", -] - -[[package]] -name = "synthez-codegen" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f724aa6d44b7162f3158a57bccd871a77b39a4aef737e01bcdff41f4772c7746" -dependencies = [ - "syn", - "synthez-core", -] - -[[package]] -name = "synthez-core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bfa6ec52465e2425fd43ce5bbbe0f0b623964f7c63feb6b10980e816c654ea" -dependencies = [ - "proc-macro2", - "quote", - "sealed", - "syn", -] - [[package]] name = "tempfile" version = "3.20.0" @@ -2072,58 +1557,17 @@ dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.7", + "rustix 1.0.8", "windows-sys 0.59.0", ] -[[package]] -name = "terminal_size" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" -dependencies = [ - "rustix 1.0.7", - "windows-sys 0.59.0", -] - -[[package]] -name = "textwrap" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" -dependencies = [ - "smawk", - "unicode-linebreak", - "unicode-width", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - [[package]] name = "thiserror" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.12", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "thiserror-impl", ] [[package]] @@ -2148,16 +1592,18 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.1" +version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "pin-project-lite", "signal-hook-registry", + "slab", "socket2", "tokio-macros", "windows-sys 0.52.0", @@ -2273,26 +1719,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "typed-builder" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe83c85a85875e8c4cb9ce4a890f05b23d38cd0d47647db7895d3d2a79566d2" -dependencies = [ - "typed-builder-macro", -] - -[[package]] -name = "typed-builder-macro" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29a3151c41d0b13e3d011f98adc24434560ef06673a155a6c7f66b9879eecce2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "unarray" version = "0.1.4" @@ -2305,18 +1731,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" -[[package]] -name = "unicode-linebreak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" - -[[package]] -name = "unicode-width" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" - [[package]] name = "untrusted" version = "0.9.0" @@ -2329,12 +1743,6 @@ version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - [[package]] name = "valuable" version = "0.1.1" @@ -2368,16 +1776,6 @@ dependencies = [ "libc", ] -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "want" version = "0.3.1" @@ -2497,15 +1895,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -2620,7 +2009,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2629,7 +2018,16 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", ] [[package]] @@ -2638,14 +2036,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -2663,48 +2077,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "wireframe" version = "0.1.0" @@ -2713,7 +2175,6 @@ dependencies = [ "async-trait", "bincode", "bytes", - "cucumber", "dashmap", "futures", "leaky-bucket", diff --git a/Cargo.toml b/Cargo.toml index 8e775abd..6f37ba24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ log = "0.4" dashmap = "5" leaky-bucket = "1.1" tracing = { version = ">=0.1.40, <0.2.0", features = ["log", "log-always"] } -metrics = "0.24" +metrics = { version = "0.24", optional = true } metrics-exporter-prometheus = { version = "0.17", optional = true, features = ["http-listener"] } [dev-dependencies] @@ -27,15 +27,13 @@ loom = "^0.7" async-stream = "0.3" tokio = { version = "1", default-features = false, features = ["test-util"] } serial_test = "3.1" -cucumber = "0.21" metrics-util = "0.20" [features] +default = ["metrics"] +metrics = ["dep:metrics", "dep:metrics-exporter-prometheus"] advanced-tests = [] [lints.clippy] pedantic = "warn" -[[test]] -name = "cucumber" -harness = false diff --git a/docs/hardening-wireframe-a-guide-to-production-resilience.md b/docs/hardening-wireframe-a-guide-to-production-resilience.md index 9c0ca2b7..8d1e2ba6 100644 --- a/docs/hardening-wireframe-a-guide-to-production-resilience.md +++ b/docs/hardening-wireframe-a-guide-to-production-resilience.md @@ -350,20 +350,9 @@ zero message loss even under transient high load. ## 5. Metrics and Observability Operational visibility is critical in production. `wireframe` updates counters -and gauges through the `metrics` crate. A Prometheus recorder can be installed -as shown below: - -```rust -use metrics_exporter_prometheus::PrometheusBuilder; - -let handle = PrometheusBuilder::new() - .install_recorder() - .expect("recorder install"); -println!("{}", handle.render()); -``` - -This exposes processed frame counts, error totals and the active connection -gauge for scraping by monitoring systems. +and gauges through the optional `metrics` feature. See the documentation for +`wireframe::metrics` for a Prometheus recorder example. The exposed metrics +include processed frame counts, error totals and the active connection gauge. By systematically implementing these hardening strategies, `wireframe` will provide the guarantees of stability, security, and reliability expected of a diff --git a/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md b/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md index 42ed920e..02fde516 100644 --- a/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md +++ b/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md @@ -285,18 +285,9 @@ A production system is a black box without good instrumentation. `wireframe` - `wireframe_reassembly_errors_total` (Counter) -Metrics are emitted using the `metrics` crate. A `PrometheusBuilder` can -install a global recorder so external systems may scrape the values. The -example below exposes a minimal recorder: - -```rust -use metrics_exporter_prometheus::PrometheusBuilder; - -let handle = PrometheusBuilder::new() - .install_recorder() - .expect("install recorder"); -println!("{}", handle.render()); -``` +Metrics are emitted using the optional `metrics` feature. See +[`wireframe::metrics`] for a Prometheus recorder example. All instrumentation +is gated behind this feature so users can opt out if metrics are unnecessary. ```mermaid sequenceDiagram diff --git a/src/app.rs b/src/app.rs index 0328f9b0..140b8a84 100644 --- a/src/app.rs +++ b/src/app.rs @@ -651,16 +651,16 @@ where { // Parse the frame first; routing is handled below to avoid duplicating // logic on the success path. + crate::metrics::inc_frames(crate::metrics::Direction::Inbound); let (env, _) = match self.parse_envelope(frame) { Ok(result) => { *deser_failures = 0; - crate::metrics::inc_frames(crate::metrics::Direction::Inbound); result } Err(e) => { *deser_failures += 1; tracing::warn!(error = ?e, "failed to deserialize message"); - crate::metrics::inc_errors(); + crate::metrics::inc_deser_errors(); if *deser_failures >= MAX_DESER_FAILURES { return Err(io::Error::new( io::ErrorKind::InvalidData, @@ -681,17 +681,17 @@ where }; if let Err(e) = self.send_response(stream, &response).await { tracing::warn!(error = %e, "failed to send response"); - crate::metrics::inc_errors(); + crate::metrics::inc_handler_errors(); } } Err(e) => { tracing::warn!(id = env.id, error = ?e, "handler error"); - crate::metrics::inc_errors(); + crate::metrics::inc_handler_errors(); } } } else { tracing::warn!("no handler for message id {}", env.id); - crate::metrics::inc_errors(); + crate::metrics::inc_handler_errors(); } Ok(()) diff --git a/src/connection.rs b/src/connection.rs index ddb3537e..6e8c737e 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -434,7 +434,7 @@ where self.hooks.handle_error(e, &mut self.ctx); state.mark_closed(); self.hooks.on_command_end(&mut self.ctx); - crate::metrics::inc_errors(); + crate::metrics::inc_handler_errors(); } Some(Err(e)) => return Err(e), None => { diff --git a/src/metrics.rs b/src/metrics.rs index df761594..e756f8d1 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -1,8 +1,21 @@ //! Metric helpers for `wireframe`. //! -//! This module defines metric names and simple helper functions -//! wrapping the [`metrics`](https://docs.rs/metrics) crate. +//! This module defines metric names and helper functions wrapping the +//! [`metrics`](https://docs.rs/metrics) crate. All functions become no-ops +//! if the optional `metrics` Cargo feature is disabled. +//! +//! # Prometheus Integration +//! +//! ``` +//! use metrics_exporter_prometheus::PrometheusBuilder; +//! +//! let handle = PrometheusBuilder::new() +//! .install_recorder() +//! .expect("recorder install"); +//! println!("{}", handle.render()); +//! ``` +#[cfg(feature = "metrics")] use metrics::{counter, gauge}; /// Name of the gauge tracking active connections. @@ -31,15 +44,38 @@ impl Direction { } /// Increment the active connections gauge. +#[cfg(feature = "metrics")] pub fn inc_connections() { gauge!(CONNECTIONS_ACTIVE).increment(1.0); } +#[cfg(not(feature = "metrics"))] +pub fn inc_connections() {} + /// Decrement the active connections gauge. +#[cfg(feature = "metrics")] pub fn dec_connections() { gauge!(CONNECTIONS_ACTIVE).decrement(1.0); } +#[cfg(not(feature = "metrics"))] +pub fn dec_connections() {} + /// Record a processed frame for the given direction. +#[cfg(feature = "metrics")] pub fn inc_frames(direction: Direction) { counter!(FRAMES_PROCESSED, "direction" => direction.as_str()).increment(1); } -/// Record an error occurrence. -pub fn inc_errors() { counter!(ERRORS_TOTAL).increment(1); } +#[cfg(not(feature = "metrics"))] +pub fn inc_frames(_direction: Direction) {} + +/// Record a deserialization error. +#[cfg(feature = "metrics")] +pub fn inc_deser_errors() { counter!(ERRORS_TOTAL, "kind" => "deserialization").increment(1); } + +#[cfg(not(feature = "metrics"))] +pub fn inc_deser_errors() {} + +/// Record a handler error. +#[cfg(feature = "metrics")] +pub fn inc_handler_errors() { counter!(ERRORS_TOTAL, "kind" => "handler").increment(1); } + +#[cfg(not(feature = "metrics"))] +pub fn inc_handler_errors() {} diff --git a/tests/cucumber.rs b/tests/cucumber.rs deleted file mode 100644 index 6b660139..00000000 --- a/tests/cucumber.rs +++ /dev/null @@ -1,12 +0,0 @@ -use cucumber::World as _; - -#[derive(Debug, Default, cucumber::World)] -pub struct MetricsWorld { - pub exporter: Option, - pub output: Option, -} - -mod steps; - -#[tokio::main] -async fn main() { MetricsWorld::run("tests/features").await; } diff --git a/tests/features/metrics.feature b/tests/features/metrics.feature deleted file mode 100644 index 07ed7db7..00000000 --- a/tests/features/metrics.feature +++ /dev/null @@ -1,5 +0,0 @@ -Feature: metrics exporter - Scenario: active connections metric recorded - Given a metrics recorder - When a connection runs with no frames - Then the exporter output includes active connections gauge diff --git a/tests/metrics.rs b/tests/metrics.rs index 3fbd2b22..b0b90fee 100644 --- a/tests/metrics.rs +++ b/tests/metrics.rs @@ -1,19 +1,17 @@ -use metrics_util::debugging::DebuggingRecorder; -use tokio_util::sync::CancellationToken; -use wireframe::{connection::ConnectionActor, push::PushQueues}; +use metrics_util::debugging::{DebugValue, DebuggingRecorder, Snapshotter}; -#[tokio::test] -async fn outbound_frame_metric_increments() { +fn snapshotter() -> (Snapshotter, DebuggingRecorder) { let recorder = DebuggingRecorder::new(); let snapshotter = recorder.snapshotter(); - recorder.install().expect("install"); + (snapshotter, recorder) +} - let (queues, handle) = PushQueues::::bounded(1, 1); - handle.push_high_priority(1).await.unwrap(); - let token = CancellationToken::new(); - let mut actor: ConnectionActor<_, ()> = ConnectionActor::new(queues, handle, None, token); - let mut out = Vec::new(); - actor.run(&mut out).await.unwrap(); +#[test] +fn outbound_frame_metric_increments() { + let (snapshotter, recorder) = snapshotter(); + metrics::with_local_recorder(&recorder, || { + wireframe::metrics::inc_frames(wireframe::metrics::Direction::Outbound); + }); let metrics = snapshotter.snapshot().into_vec(); let found = metrics @@ -21,3 +19,33 @@ async fn outbound_frame_metric_increments() { .any(|(k, ..)| k.key().name() == wireframe::metrics::FRAMES_PROCESSED); assert!(found, "frames_processed metric not recorded"); } + +#[test] +fn inbound_frame_metric_increments() { + let (snapshotter, recorder) = snapshotter(); + metrics::with_local_recorder(&recorder, || { + wireframe::metrics::inc_frames(wireframe::metrics::Direction::Inbound); + }); + + let metrics = snapshotter.snapshot().into_vec(); + let found = metrics.iter().any(|(k, _, _, v)| { + k.key().name() == wireframe::metrics::FRAMES_PROCESSED + && k.key().labels().any(|l| l.key() == "direction" && l.value() == "inbound") + && matches!(v, DebugValue::Counter(c) if *c > 0) + }); + assert!(found, "inbound frames metric not recorded"); +} + +#[test] +fn error_metric_increments() { + let (snapshotter, recorder) = snapshotter(); + metrics::with_local_recorder(&recorder, || { + wireframe::metrics::inc_deser_errors(); + }); + + let metrics = snapshotter.snapshot().into_vec(); + let found = metrics + .iter() + .any(|(k, ..)| k.key().name() == wireframe::metrics::ERRORS_TOTAL); + assert!(found, "error metric not recorded"); +} diff --git a/tests/steps/mod.rs b/tests/steps/mod.rs deleted file mode 100644 index aa5eda1e..00000000 --- a/tests/steps/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -use cucumber::{given, then, when}; -use tokio_util::sync::CancellationToken; -use wireframe::{connection::ConnectionActor, push::PushQueues}; - -use crate::MetricsWorld; - -#[given("a metrics recorder")] -fn metrics_recorder(world: &mut MetricsWorld) { - let handle = metrics_exporter_prometheus::PrometheusBuilder::new() - .install_recorder() - .expect("recorder install"); - world.exporter = Some(handle); -} - -#[when("a connection runs with no frames")] -async fn run_connection(world: &mut MetricsWorld) { - let (queues, handle) = PushQueues::::bounded(1, 1); - let token = CancellationToken::new(); - let mut actor: ConnectionActor<_, ()> = ConnectionActor::new(queues, handle, None, token); - let mut out = Vec::new(); - actor.run(&mut out).await.unwrap(); - if let Some(h) = &world.exporter { - h.run_upkeep(); - world.output = Some(h.render()); - } -} - -#[then("the exporter output includes active connections gauge")] -fn check_output(world: &mut MetricsWorld) { - let out = world.output.as_ref().expect("no output"); - assert!(out.contains("wireframe_connections_active"), "{out}"); -} From 18189be074ea3d0593b4deec32394ed44ea53a58 Mon Sep 17 00:00:00 2001 From: Leynos Date: Tue, 22 Jul 2025 07:29:43 +0100 Subject: [PATCH 03/10] Add module docs for metrics tests --- tests/metrics.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/metrics.rs b/tests/metrics.rs index b0b90fee..15a8e4b1 100644 --- a/tests/metrics.rs +++ b/tests/metrics.rs @@ -1,3 +1,7 @@ +//! Tests for `wireframe` metrics helpers. +//! +//! These tests verify that counters and gauges update as expected using +//! `metrics_util::debugging::DebuggingRecorder`. use metrics_util::debugging::{DebugValue, DebuggingRecorder, Snapshotter}; fn snapshotter() -> (Snapshotter, DebuggingRecorder) { @@ -30,7 +34,9 @@ fn inbound_frame_metric_increments() { let metrics = snapshotter.snapshot().into_vec(); let found = metrics.iter().any(|(k, _, _, v)| { k.key().name() == wireframe::metrics::FRAMES_PROCESSED - && k.key().labels().any(|l| l.key() == "direction" && l.value() == "inbound") + && k.key() + .labels() + .any(|l| l.key() == "direction" && l.value() == "inbound") && matches!(v, DebugValue::Counter(c) if *c > 0) }); assert!(found, "inbound frames metric not recorded"); From b0dc75957893b9eab8277bda0691d2c76b6234d4 Mon Sep 17 00:00:00 2001 From: Leynos Date: Tue, 22 Jul 2025 07:47:08 +0100 Subject: [PATCH 04/10] Fix docs wording and format metrics test --- ...eframe-1-0-feature-set-philosophy-and-capability-maturity.md | 2 +- tests/metrics.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md b/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md index 02fde516..706c897d 100644 --- a/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md +++ b/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md @@ -287,7 +287,7 @@ A production system is a black box without good instrumentation. `wireframe` Metrics are emitted using the optional `metrics` feature. See [`wireframe::metrics`] for a Prometheus recorder example. All instrumentation -is gated behind this feature so users can opt out if metrics are unnecessary. +is gated behind this feature, so users can opt out if metrics are unnecessary. ```mermaid sequenceDiagram diff --git a/tests/metrics.rs b/tests/metrics.rs index 15a8e4b1..a4a1255c 100644 --- a/tests/metrics.rs +++ b/tests/metrics.rs @@ -32,6 +32,7 @@ fn inbound_frame_metric_increments() { }); let metrics = snapshotter.snapshot().into_vec(); + let found = metrics.iter().any(|(k, _, _, v)| { k.key().name() == wireframe::metrics::FRAMES_PROCESSED && k.key() From aeb4205983a61f5e72860de461a3a1515292294d Mon Sep 17 00:00:00 2001 From: Leynos Date: Tue, 22 Jul 2025 23:12:29 +0100 Subject: [PATCH 05/10] Add metrics ER diagram --- ...-set-philosophy-and-capability-maturity.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md b/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md index 706c897d..81fce77f 100644 --- a/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md +++ b/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md @@ -285,6 +285,25 @@ A production system is a black box without good instrumentation. `wireframe` - `wireframe_reassembly_errors_total` (Counter) +```mermaid +erDiagram + CONNECTIONS_ACTIVE ||--o{ FRAMES_PROCESSED : tracks + CONNECTIONS_ACTIVE { + string name + float value + } + FRAMES_PROCESSED { + string name + int value + string direction + } + ERRORS_TOTAL { + string name + int value + } + CONNECTIONS_ACTIVE ||--o{ ERRORS_TOTAL : tracks +``` + Metrics are emitted using the optional `metrics` feature. See [`wireframe::metrics`] for a Prometheus recorder example. All instrumentation is gated behind this feature, so users can opt out if metrics are unnecessary. From 817ad0d6167d910473b621944c43cd7212104812 Mon Sep 17 00:00:00 2001 From: Leynos Date: Wed, 23 Jul 2025 20:04:22 +0100 Subject: [PATCH 06/10] Update toolchain and fix unused structs --- examples/metadata_routing.rs | 1 + rust-toolchain.toml | 2 +- src/server.rs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/metadata_routing.rs b/examples/metadata_routing.rs index 914a4ace..946938a6 100644 --- a/examples/metadata_routing.rs +++ b/examples/metadata_routing.rs @@ -60,6 +60,7 @@ impl FrameMetadata for HeaderSerializer { struct Ping; #[derive(bincode::Decode, bincode::Encode)] +#[allow(dead_code)] struct Pong; #[tokio::main] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5f1b4070..47ef098b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-06-10" +channel = "nightly-2025-07-22" components = ["rustfmt", "clippy"] diff --git a/src/server.rs b/src/server.rs index 4c7ba649..95773538 100644 --- a/src/server.rs +++ b/src/server.rs @@ -467,6 +467,7 @@ mod tests { } #[derive(Debug, Clone, PartialEq, Encode, Decode)] + #[allow(dead_code)] struct EmptyPreamble; #[fixture] From 24dab393ac00d13aa0004304f3eb7e7be20045ec Mon Sep 17 00:00:00 2001 From: Leynos Date: Wed, 23 Jul 2025 20:24:01 +0100 Subject: [PATCH 07/10] Refine metrics tests and docs --- ...-set-philosophy-and-capability-maturity.md | 3 ++ tests/metrics.rs | 29 +++++++++++-------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md b/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md index 81fce77f..7255b3db 100644 --- a/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md +++ b/docs/the-road-to-wireframe-1-0-feature-set-philosophy-and-capability-maturity.md @@ -285,6 +285,9 @@ A production system is a black box without good instrumentation. `wireframe` - `wireframe_reassembly_errors_total` (Counter) +The following entity–relationship diagram summarises how the core metrics +relate. + ```mermaid erDiagram CONNECTIONS_ACTIVE ||--o{ FRAMES_PROCESSED : tracks diff --git a/tests/metrics.rs b/tests/metrics.rs index a4a1255c..9f0ba5ba 100644 --- a/tests/metrics.rs +++ b/tests/metrics.rs @@ -4,7 +4,8 @@ //! `metrics_util::debugging::DebuggingRecorder`. use metrics_util::debugging::{DebugValue, DebuggingRecorder, Snapshotter}; -fn snapshotter() -> (Snapshotter, DebuggingRecorder) { +/// Creates a debugging recorder and snapshotter for metrics testing. +fn debugging_recorder_setup() -> (Snapshotter, DebuggingRecorder) { let recorder = DebuggingRecorder::new(); let snapshotter = recorder.snapshotter(); (snapshotter, recorder) @@ -12,27 +13,30 @@ fn snapshotter() -> (Snapshotter, DebuggingRecorder) { #[test] fn outbound_frame_metric_increments() { - let (snapshotter, recorder) = snapshotter(); + let (snapshotter, recorder) = debugging_recorder_setup(); metrics::with_local_recorder(&recorder, || { wireframe::metrics::inc_frames(wireframe::metrics::Direction::Outbound); }); let metrics = snapshotter.snapshot().into_vec(); - let found = metrics - .iter() - .any(|(k, ..)| k.key().name() == wireframe::metrics::FRAMES_PROCESSED); - assert!(found, "frames_processed metric not recorded"); + let found = metrics.iter().any(|(k, _, _, v)| { + k.key().name() == wireframe::metrics::FRAMES_PROCESSED + && k.key() + .labels() + .any(|l| l.key() == "direction" && l.value() == "outbound") + && matches!(v, DebugValue::Counter(c) if *c > 0) + }); + assert!(found, "outbound frames metric not recorded"); } #[test] fn inbound_frame_metric_increments() { - let (snapshotter, recorder) = snapshotter(); + let (snapshotter, recorder) = debugging_recorder_setup(); metrics::with_local_recorder(&recorder, || { wireframe::metrics::inc_frames(wireframe::metrics::Direction::Inbound); }); let metrics = snapshotter.snapshot().into_vec(); - let found = metrics.iter().any(|(k, _, _, v)| { k.key().name() == wireframe::metrics::FRAMES_PROCESSED && k.key() @@ -45,14 +49,15 @@ fn inbound_frame_metric_increments() { #[test] fn error_metric_increments() { - let (snapshotter, recorder) = snapshotter(); + let (snapshotter, recorder) = debugging_recorder_setup(); metrics::with_local_recorder(&recorder, || { wireframe::metrics::inc_deser_errors(); }); let metrics = snapshotter.snapshot().into_vec(); - let found = metrics - .iter() - .any(|(k, ..)| k.key().name() == wireframe::metrics::ERRORS_TOTAL); + let found = metrics.iter().any(|(k, _, _, v)| { + k.key().name() == wireframe::metrics::ERRORS_TOTAL + && matches!(v, DebugValue::Counter(c) if *c > 0) + }); assert!(found, "error metric not recorded"); } From 840bdaafd026dcecc576acaf1b64d45ffdb24c4f Mon Sep 17 00:00:00 2001 From: Leynos Date: Thu, 24 Jul 2025 00:37:25 +0100 Subject: [PATCH 08/10] Format inbound metrics test --- tests/metrics.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/metrics.rs b/tests/metrics.rs index 9f0ba5ba..48bb6081 100644 --- a/tests/metrics.rs +++ b/tests/metrics.rs @@ -32,6 +32,7 @@ fn outbound_frame_metric_increments() { #[test] fn inbound_frame_metric_increments() { let (snapshotter, recorder) = debugging_recorder_setup(); + metrics::with_local_recorder(&recorder, || { wireframe::metrics::inc_frames(wireframe::metrics::Direction::Inbound); }); @@ -44,6 +45,7 @@ fn inbound_frame_metric_increments() { .any(|l| l.key() == "direction" && l.value() == "inbound") && matches!(v, DebugValue::Counter(c) if *c > 0) }); + assert!(found, "inbound frames metric not recorded"); } From b607d09aa0653914036b86a03c5195ba5d41b6e8 Mon Sep 17 00:00:00 2001 From: Leynos Date: Thu, 24 Jul 2025 00:59:27 +0100 Subject: [PATCH 09/10] Use expect for dead code --- examples/metadata_routing.rs | 2 +- src/server.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/metadata_routing.rs b/examples/metadata_routing.rs index 946938a6..bc326c51 100644 --- a/examples/metadata_routing.rs +++ b/examples/metadata_routing.rs @@ -60,7 +60,7 @@ impl FrameMetadata for HeaderSerializer { struct Ping; #[derive(bincode::Decode, bincode::Encode)] -#[allow(dead_code)] +#[expect(dead_code, reason = "used only in documentation example")] struct Pong; #[tokio::main] diff --git a/src/server.rs b/src/server.rs index 95773538..2e79f8e0 100644 --- a/src/server.rs +++ b/src/server.rs @@ -467,7 +467,7 @@ mod tests { } #[derive(Debug, Clone, PartialEq, Encode, Decode)] - #[allow(dead_code)] + #[expect(dead_code, reason = "test helper for unused preamble type")] struct EmptyPreamble; #[fixture] From 6f7125763782f402a1aee4aefd8716be68261165 Mon Sep 17 00:00:00 2001 From: Leynos Date: Thu, 24 Jul 2025 07:49:42 +0100 Subject: [PATCH 10/10] Add doc comment for EmptyPreamble and tidy metrics test --- Cargo.toml | 1 + src/server.rs | 1 + tests/metrics.rs | 2 -- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6f37ba24..32abd532 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ async-stream = "0.3" tokio = { version = "1", default-features = false, features = ["test-util"] } serial_test = "3.1" metrics-util = "0.20" +metrics-exporter-prometheus = "0.17" [features] default = ["metrics"] diff --git a/src/server.rs b/src/server.rs index 2e79f8e0..fd2ba4ea 100644 --- a/src/server.rs +++ b/src/server.rs @@ -466,6 +466,7 @@ mod tests { message: String, } + /// Test helper preamble carrying no data. #[derive(Debug, Clone, PartialEq, Encode, Decode)] #[expect(dead_code, reason = "test helper for unused preamble type")] struct EmptyPreamble; diff --git a/tests/metrics.rs b/tests/metrics.rs index 48bb6081..c246cff2 100644 --- a/tests/metrics.rs +++ b/tests/metrics.rs @@ -32,11 +32,9 @@ fn outbound_frame_metric_increments() { #[test] fn inbound_frame_metric_increments() { let (snapshotter, recorder) = debugging_recorder_setup(); - metrics::with_local_recorder(&recorder, || { wireframe::metrics::inc_frames(wireframe::metrics::Direction::Inbound); }); - let metrics = snapshotter.snapshot().into_vec(); let found = metrics.iter().any(|(k, _, _, v)| { k.key().name() == wireframe::metrics::FRAMES_PROCESSED