From 8a83559cf72b6f22ccd73599633a4bbc8b811c8a Mon Sep 17 00:00:00 2001 From: refcell Date: Tue, 25 Jun 2024 08:43:04 -0400 Subject: [PATCH] feat(examples): Trusted Sync Metrics (#308) * feat: trusted sync metrics * feat: online derive provider metrics * fix: chain provider metrics * feat(derive): blob provider metrics * fix: make lazy-static optional --- Cargo.lock | 505 +++++++++++++++++++- crates/derive/Cargo.toml | 4 + crates/derive/src/online/alloy_providers.rs | 155 +++++- crates/derive/src/online/beacon_client.rs | 64 ++- crates/derive/src/online/blob_provider.rs | 28 +- crates/derive/src/online/metrics.rs | 18 + crates/derive/src/online/mod.rs | 2 + examples/trusted-sync/Cargo.toml | 3 + examples/trusted-sync/src/cli.rs | 56 +++ examples/trusted-sync/src/main.rs | 81 ++-- examples/trusted-sync/src/metrics.rs | 47 ++ examples/trusted-sync/src/telemetry.rs | 15 + 12 files changed, 891 insertions(+), 87 deletions(-) create mode 100644 crates/derive/src/online/metrics.rs create mode 100644 examples/trusted-sync/src/metrics.rs create mode 100644 examples/trusted-sync/src/telemetry.rs diff --git a/Cargo.lock b/Cargo.lock index cde96ccfc..a79721f2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,188 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "actix-codec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" +dependencies = [ + "bitflags 2.5.0", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-http" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae682f693a9cd7b058f2b0b5d9a6d7728a8555779bedbbc35dd88528611d020" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash", + "base64", + "bitflags 2.5.0", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2 0.3.26", + "http 0.2.12", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn 2.0.67", +] + +[[package]] +name = "actix-router" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" +dependencies = [ + "bytestring", + "cfg-if", + "http 0.2.12", + "regex", + "regex-lite", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b02303ce8d4e8be5b855af6cf3c3a08f3eff26880faad82bab679c22d3650cb5" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1988c02af8d2b718c05bc4aeb6a66395b7cdf32858c2c71131e5637a8c05a9ff" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "regex-lite", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2", + "time", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn 2.0.67", +] + [[package]] name = "addr2line" version = "0.22.0" @@ -24,17 +206,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "alloc-no-stdlib" version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "allocator-api2" version = "0.2.18" @@ -751,6 +952,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" dependencies = [ "alloc-no-stdlib", + "alloc-stdlib", "brotli-decompressor", ] @@ -761,6 +963,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" dependencies = [ "alloc-no-stdlib", + "alloc-stdlib", ] [[package]] @@ -790,6 +993,15 @@ dependencies = [ "serde", ] +[[package]] +name = "bytestring" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" +dependencies = [ + "bytes", +] + [[package]] name = "c-kzg" version = "1.0.2" @@ -809,6 +1021,11 @@ name = "cc" version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" +dependencies = [ + "jobserver", + "libc", + "once_cell", +] [[package]] name = "cfg-if" @@ -898,6 +1115,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -923,6 +1151,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "crunchy" version = "0.2.2" @@ -974,6 +1211,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "derivative" version = "2.2.0" @@ -1145,6 +1391,16 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1321,6 +1577,25 @@ dependencies = [ "subtle", ] +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.5" @@ -1332,7 +1607,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.1.0", "indexmap", "slab", "tokio", @@ -1387,6 +1662,17 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.1.0" @@ -1405,7 +1691,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http", + "http 1.1.0", ] [[package]] @@ -1416,7 +1702,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http", + "http 1.1.0", "http-body", "pin-project-lite", ] @@ -1427,6 +1713,12 @@ version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hyper" version = "1.3.1" @@ -1436,8 +1728,8 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2", - "http", + "h2 0.4.5", + "http 1.1.0", "http-body", "httparse", "itoa", @@ -1454,7 +1746,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" dependencies = [ "futures-util", - "http", + "http 1.1.0", "hyper", "hyper-util", "rustls", @@ -1489,7 +1781,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", + "http 1.1.0", "http-body", "hyper", "pin-project-lite", @@ -1576,6 +1868,15 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.69" @@ -1679,9 +1980,11 @@ dependencies = [ "c-kzg", "hashbrown", "kona-primitives", + "lazy_static", "lru", "miniz_oxide", "op-alloy-consensus", + "prometheus", "proptest", "reqwest", "revm", @@ -1816,6 +2119,12 @@ dependencies = [ "superchain-primitives", ] +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + [[package]] name = "lazy_static" version = "1.5.0" @@ -1852,6 +2161,23 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "local-channel" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" +dependencies = [ + "futures-core", + "futures-sink", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" + [[package]] name = "lock_api" version = "0.4.12" @@ -1915,6 +2241,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.48.0", ] @@ -1990,6 +2317,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.46" @@ -2282,6 +2615,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2350,6 +2689,46 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "procfs" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4" +dependencies = [ + "bitflags 2.5.0", + "hex", + "lazy_static", + "procfs-core", + "rustix", +] + +[[package]] +name = "procfs-core" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" +dependencies = [ + "bitflags 2.5.0", + "hex", +] + +[[package]] +name = "prometheus" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "libc", + "memchr", + "parking_lot", + "procfs", + "protobuf", + "thiserror", +] + [[package]] name = "proptest" version = "1.5.0" @@ -2370,6 +2749,12 @@ dependencies = [ "unarray", ] +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + [[package]] name = "quick-error" version = "1.2.3" @@ -2439,6 +2824,35 @@ dependencies = [ "bitflags 2.5.0", ] +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + [[package]] name = "regex-syntax" version = "0.8.4" @@ -2456,8 +2870,8 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2", - "http", + "h2 0.4.5", + "http 1.1.0", "http-body", "http-body-util", "hyper", @@ -2880,6 +3294,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + [[package]] name = "sha2" version = "0.10.8" @@ -3182,6 +3607,37 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -3389,6 +3845,7 @@ dependencies = [ name = "trusted-sync" version = "0.1.0" dependencies = [ + "actix-web", "alloy-primitives", "alloy-provider", "alloy-rpc-types", @@ -3396,6 +3853,8 @@ dependencies = [ "anyhow", "clap", "kona-derive", + "lazy_static", + "prometheus", "reqwest", "serde", "tokio", @@ -3835,3 +4294,31 @@ dependencies = [ "quote", "syn 2.0.67", ] + +[[package]] +name = "zstd" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.11+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75652c55c0b6f3e6f12eb786fe1bc960396bf05a1eb3bf1f3691c3610ac2e6d4" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/crates/derive/Cargo.toml b/crates/derive/Cargo.toml index 195497739..4c90225eb 100644 --- a/crates/derive/Cargo.toml +++ b/crates/derive/Cargo.toml @@ -39,6 +39,8 @@ alloc-no-stdlib = "2.0.4" serde = { version = "1.0.203", default-features = false, features = ["derive"], optional = true } # `online` feature dependencies +lazy_static = { version = "1.5.0", optional = true } +prometheus = { version = "0.13.4", features = ["process"], optional = true } c-kzg = { version = "1.0.2", default-features = false, optional = true } sha2 = { version = "0.10.8", default-features = false, optional = true } alloy-transport = { version = "0.1", default-features = false, optional = true } @@ -81,6 +83,8 @@ online = [ "dep:alloy-transport", "dep:alloy-transport-http", "dep:reqwest", + "dep:prometheus", + "dep:lazy_static", "alloy-provider/reqwest", "alloy-rpc-client/reqwest", "alloy-transport-http/reqwest", diff --git a/crates/derive/src/online/alloy_providers.rs b/crates/derive/src/online/alloy_providers.rs index 6d8ab613c..8183d2729 100644 --- a/crates/derive/src/online/alloy_providers.rs +++ b/crates/derive/src/online/alloy_providers.rs @@ -65,25 +65,61 @@ impl AlloyChainProvider { #[async_trait] impl ChainProvider for AlloyChainProvider { async fn header_by_hash(&mut self, hash: B256) -> Result
{ + let timer = crate::online::metrics::PROVIDER_RESPONSE_TIME + .with_label_values(&["chain_provider", "header_by_hash"]) + .start_timer(); if let Some(header) = self.header_by_hash_cache.get(&hash) { + timer.observe_duration(); return Ok(header.clone()); } let raw_header: TransportResult = self.inner.raw_request("debug_getRawHeader".into(), [hash]).await; - let raw_header: Bytes = raw_header.map_err(|e| anyhow!(e))?; - Header::decode(&mut raw_header.as_ref()).map_err(|e| anyhow!(e)) + let raw_header: Bytes = match raw_header.map_err(|e| anyhow!(e)) { + Ok(b) => b, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; + match Header::decode(&mut raw_header.as_ref()).map_err(|e| anyhow!(e)) { + Ok(header) => { + self.header_by_hash_cache.put(hash, header.clone()); + timer.observe_duration(); + Ok(header) + } + Err(e) => { + timer.observe_duration(); + Err(e) + } + } } async fn block_info_by_number(&mut self, number: u64) -> Result { + let timer = crate::online::metrics::PROVIDER_RESPONSE_TIME + .with_label_values(&["chain_provider", "block_info_by_number"]) + .start_timer(); if let Some(block_info) = self.block_info_by_number_cache.get(&number) { + timer.observe_duration(); return Ok(*block_info); } let raw_header: TransportResult = self.inner.raw_request("debug_getRawHeader".into(), [U64::from(number)]).await; - let raw_header: Bytes = raw_header.map_err(|e| anyhow!(e))?; - let header = Header::decode(&mut raw_header.as_ref()).map_err(|e| anyhow!(e))?; + let raw_header: Bytes = match raw_header.map_err(|e| anyhow!(e)) { + Ok(b) => b, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; + let header = match Header::decode(&mut raw_header.as_ref()).map_err(|e| anyhow!(e)) { + Ok(h) => h, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; let block_info = BlockInfo { hash: header.hash_slow(), @@ -92,19 +128,30 @@ impl ChainProvider for AlloyChainProvider { timestamp: header.timestamp, }; self.block_info_by_number_cache.put(number, block_info); + timer.observe_duration(); Ok(block_info) } async fn receipts_by_hash(&mut self, hash: B256) -> Result> { + let timer = crate::online::metrics::PROVIDER_RESPONSE_TIME + .with_label_values(&["chain_provider", "receipts_by_hash"]) + .start_timer(); if let Some(receipts) = self.receipts_by_hash_cache.get(&hash) { + timer.observe_duration(); return Ok(receipts.clone()); } let raw_receipts: TransportResult> = self.inner.raw_request("debug_getRawReceipts".into(), [hash]).await; - let raw_receipts: Vec = raw_receipts.map_err(|e| anyhow!(e))?; + let raw_receipts: Vec = match raw_receipts.map_err(|e| anyhow!(e)) { + Ok(r) => r, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; - let receipts = raw_receipts + let receipts = match raw_receipts .iter() .map(|r| { let r = &mut r.as_ref(); @@ -116,8 +163,16 @@ impl ChainProvider for AlloyChainProvider { Ok(ReceiptWithBloom::decode(r).map_err(|e| anyhow!(e))?.receipt) }) - .collect::>>()?; + .collect::>>() + { + Ok(r) => r, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; self.receipts_by_hash_cache.put(hash, receipts.clone()); + timer.observe_duration(); Ok(receipts) } @@ -125,15 +180,31 @@ impl ChainProvider for AlloyChainProvider { &mut self, hash: B256, ) -> Result<(BlockInfo, Vec)> { + let timer = crate::online::metrics::PROVIDER_RESPONSE_TIME + .with_label_values(&["chain_provider", "block_info_and_transactions_by_hash"]) + .start_timer(); if let Some(block_info_and_txs) = self.block_info_and_transactions_by_hash_cache.get(&hash) { + timer.observe_duration(); return Ok(block_info_and_txs.clone()); } let raw_block: TransportResult = self.inner.raw_request("debug_getRawBlock".into(), [hash]).await; - let raw_block: Bytes = raw_block.map_err(|e| anyhow!(e))?; - let block = Block::decode(&mut raw_block.as_ref()).map_err(|e| anyhow!(e))?; + let raw_block: Bytes = match raw_block.map_err(|e| anyhow!(e)) { + Ok(b) => b, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; + let block = match Block::decode(&mut raw_block.as_ref()).map_err(|e| anyhow!(e)) { + Ok(b) => b, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; let block_info = BlockInfo { hash: block.header.hash_slow(), @@ -142,6 +213,7 @@ impl ChainProvider for AlloyChainProvider { timestamp: block.header.timestamp, }; self.block_info_and_transactions_by_hash_cache.put(hash, (block_info, block.body.clone())); + timer.observe_duration(); Ok((block_info, block.body)) } } @@ -199,28 +271,62 @@ impl AlloyL2ChainProvider { #[async_trait] impl L2ChainProvider for AlloyL2ChainProvider { async fn l2_block_info_by_number(&mut self, number: u64) -> Result { + let timer = crate::online::metrics::PROVIDER_RESPONSE_TIME + .with_label_values(&["l2_chain_provider", "l2_block_info_by_number"]) + .start_timer(); if let Some(l2_block_info) = self.l2_block_info_by_number_cache.get(&number) { + timer.observe_duration(); return Ok(*l2_block_info); } - let payload = self.payload_by_number(number).await?; - let l2_block_info = payload.to_l2_block_ref(self.rollup_config.as_ref())?; + let payload = match self.payload_by_number(number).await { + Ok(p) => p, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; + let l2_block_info = match payload.to_l2_block_ref(self.rollup_config.as_ref()) { + Ok(b) => b, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; self.l2_block_info_by_number_cache.put(number, l2_block_info); + timer.observe_duration(); Ok(l2_block_info) } async fn payload_by_number(&mut self, number: u64) -> Result { + let timer = crate::online::metrics::PROVIDER_RESPONSE_TIME + .with_label_values(&["l2_chain_provider", "payload_by_number"]) + .start_timer(); if let Some(payload) = self.payload_by_number_cache.get(&number) { + timer.observe_duration(); return Ok(payload.clone()); } let raw_block: TransportResult = self.inner.raw_request("debug_getRawBlock".into(), [U64::from(number)]).await; - let raw_block: Bytes = raw_block.map_err(|e| anyhow!(e))?; - let block = OpBlock::decode(&mut raw_block.as_ref()).map_err(|e| anyhow!(e))?; + let raw_block: Bytes = match raw_block.map_err(|e| anyhow!(e)) { + Ok(b) => b, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; + let block = match OpBlock::decode(&mut raw_block.as_ref()).map_err(|e| anyhow!(e)) { + Ok(b) => b, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; let payload_envelope: L2ExecutionPayloadEnvelope = block.into(); self.payload_by_number_cache.put(number, payload_envelope.clone()); + timer.observe_duration(); Ok(payload_envelope) } @@ -229,11 +335,30 @@ impl L2ChainProvider for AlloyL2ChainProvider { number: u64, rollup_config: Arc, ) -> Result { + let timer = crate::online::metrics::PROVIDER_RESPONSE_TIME + .with_label_values(&["l2_chain_provider", "system_config_by_number"]) + .start_timer(); if let Some(system_config) = self.system_config_by_number_cache.get(&number) { + timer.observe_duration(); return Ok(system_config.clone()); } - let envelope = self.payload_by_number(number).await?; - envelope.to_system_config(&rollup_config) + let envelope = match self.payload_by_number(number).await { + Ok(e) => e, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; + let sys_config = match envelope.to_system_config(&rollup_config) { + Ok(s) => s, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; + self.system_config_by_number_cache.put(number, sys_config.clone()); + timer.observe_duration(); + Ok(sys_config) } } diff --git a/crates/derive/src/online/beacon_client.rs b/crates/derive/src/online/beacon_client.rs index f36a20114..4020e63ec 100644 --- a/crates/derive/src/online/beacon_client.rs +++ b/crates/derive/src/online/beacon_client.rs @@ -56,25 +56,47 @@ impl OnlineBeaconClient { #[async_trait] impl BeaconClient for OnlineBeaconClient { async fn config_spec(&self) -> Result { - self.inner + let timer = crate::online::metrics::PROVIDER_RESPONSE_TIME + .with_label_values(&["beacon_client", "config_spec"]) + .start_timer(); + let first = match self + .inner .get(format!("{}/{}", self.base, SPEC_METHOD)) .send() .await - .map_err(|e| anyhow!(e))? - .json::() - .await .map_err(|e| anyhow!(e)) + { + Ok(response) => response, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; + let res = first.json::().await.map_err(|e| anyhow!(e)); + timer.observe_duration(); + res } async fn beacon_genesis(&self) -> Result { - self.inner + let timer = crate::online::metrics::PROVIDER_RESPONSE_TIME + .with_label_values(&["beacon_client", "beacon_genesis"]) + .start_timer(); + let first = match self + .inner .get(format!("{}/{}", self.base, GENESIS_METHOD)) .send() .await - .map_err(|e| anyhow!(e))? - .json::() - .await .map_err(|e| anyhow!(e)) + { + Ok(response) => response, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; + let res = first.json::().await.map_err(|e| anyhow!(e)); + timer.observe_duration(); + res } async fn beacon_blob_side_cars( @@ -82,15 +104,30 @@ impl BeaconClient for OnlineBeaconClient { slot: u64, hashes: &[IndexedBlobHash], ) -> Result> { - let raw_response = self + let timer = crate::online::metrics::PROVIDER_RESPONSE_TIME + .with_label_values(&["beacon_client", "beacon_blob_side_cars"]) + .start_timer(); + let raw_response = match self .inner .get(format!("{}/{}/{}", self.base, SIDECARS_METHOD_PREFIX, slot)) .send() .await - .map_err(|e| anyhow!(e))? - .json::() - .await - .map_err(|e| anyhow!(e))?; + .map_err(|e| anyhow!(e)) + { + Ok(response) => response, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; + let raw_response = + match raw_response.json::().await.map_err(|e| anyhow!(e)) { + Ok(response) => response, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; let mut sidecars = Vec::with_capacity(hashes.len()); @@ -103,6 +140,7 @@ impl BeaconClient for OnlineBeaconClient { } }); + timer.observe_duration(); Ok(sidecars) } } diff --git a/crates/derive/src/online/blob_provider.rs b/crates/derive/src/online/blob_provider.rs index e8653f5ff..74f3a9d4c 100644 --- a/crates/derive/src/online/blob_provider.rs +++ b/crates/derive/src/online/blob_provider.rs @@ -128,15 +128,27 @@ where block_ref: &BlockInfo, blob_hashes: &[IndexedBlobHash], ) -> Result, BlobProviderError> { + let timer = crate::online::metrics::PROVIDER_RESPONSE_TIME + .with_label_values(&["blob_provider", "get_blobs"]) + .start_timer(); // Fetches the genesis timestamp and slot interval from the // [BeaconGenesis] and [ConfigSpec] if not previously loaded. - self.load_configs().await?; + if let Err(e) = self.load_configs().await { + timer.observe_duration(); + return Err(e); + } // Fetch the blob sidecars for the given block reference and blob hashes. - let sidecars = self.fetch_filtered_sidecars(block_ref, blob_hashes).await?; + let sidecars = match self.fetch_filtered_sidecars(block_ref, blob_hashes).await { + Ok(sidecars) => sidecars, + Err(e) => { + timer.observe_duration(); + return Err(e); + } + }; // Validate the blob sidecars straight away with the `IndexedBlobHash`es. - let blobs = sidecars + let blobs = match sidecars .into_iter() .enumerate() .map(|(i, sidecar)| { @@ -146,8 +158,16 @@ where Err(e) => Err(e), } }) - .collect::>>()?; + .collect::>>() + { + Ok(blobs) => blobs, + Err(e) => { + timer.observe_duration(); + return Err(BlobProviderError::Custom(e)); + } + }; + timer.observe_duration(); Ok(blobs) } } diff --git a/crates/derive/src/online/metrics.rs b/crates/derive/src/online/metrics.rs new file mode 100644 index 000000000..77ded2c09 --- /dev/null +++ b/crates/derive/src/online/metrics.rs @@ -0,0 +1,18 @@ +//! Metrics for the online derivation pipeline. + +use alloc::boxed::Box; +use lazy_static::lazy_static; +use prometheus::{self, register_histogram_vec, HistogramVec}; + +const RESPONSE_TIME_CUSTOM_BUCKETS: &[f64; 14] = + &[0.0005, 0.001, 0.002, 0.005, 0.008, 0.01, 0.02, 0.05, 0.08, 0.1, 0.2, 0.5, 0.8, 1.0]; + +lazy_static! { + pub static ref PROVIDER_RESPONSE_TIME: HistogramVec = register_histogram_vec!( + "provider_response_time_seconds", + "Provider response times", + &["provider", "method"], + RESPONSE_TIME_CUSTOM_BUCKETS.to_vec() + ) + .expect("Failed to register histogram vec"); +} diff --git a/crates/derive/src/online/mod.rs b/crates/derive/src/online/mod.rs index e622ab162..e3d7369c4 100644 --- a/crates/derive/src/online/mod.rs +++ b/crates/derive/src/online/mod.rs @@ -9,6 +9,8 @@ pub use crate::{ types::{BlockInfo, RollupConfig}, }; +mod metrics; + mod pipeline; pub use pipeline::{ new_online_pipeline, OnlineAttributesBuilder, OnlineAttributesQueue, OnlineDataProvider, diff --git a/examples/trusted-sync/Cargo.toml b/examples/trusted-sync/Cargo.toml index 4d11b4d8d..3e91e167f 100644 --- a/examples/trusted-sync/Cargo.toml +++ b/examples/trusted-sync/Cargo.toml @@ -17,7 +17,10 @@ alloy-primitives = { workspace = true, features = ["serde"] } kona-derive = { path = "../../crates/derive", version = "0.0.2", features = ["serde", "k256", "online"] } # Custom dependencies +lazy_static = "1.5.0" reqwest = "0.12" +actix-web = "4.8.0" +prometheus = { version = "0.13.4", features = ["process"] } tokio = { version = "1.37.0", features = ["full"] } tracing-subscriber = "0.3.18" clap = { version = "4.5.4", features = ["derive", "env"] } diff --git a/examples/trusted-sync/src/cli.rs b/examples/trusted-sync/src/cli.rs index 41a940794..e82475bc0 100644 --- a/examples/trusted-sync/src/cli.rs +++ b/examples/trusted-sync/src/cli.rs @@ -1,6 +1,14 @@ //! This module contains all CLI-specific code. +use anyhow::{anyhow, Result}; use clap::{ArgAction, Parser}; +use reqwest::Url; + +const L1_RPC_URL: &str = "L1_RPC_URL"; +const L2_RPC_URL: &str = "L2_RPC_URL"; +const BEACON_URL: &str = "BEACON_URL"; +const DEFAULT_METRICS_SERVER_ADDR: &str = "127.0.0.1"; +const DEFAULT_METRICS_SERVER_PORT: u16 = 9000; /// The host binary CLI application arguments. #[derive(Parser, Clone, serde::Serialize, serde::Deserialize)] @@ -20,4 +28,52 @@ pub struct Cli { /// The l2 block to start from. #[clap(long, short, help = "Starting l2 block, defaults to chain genesis if none specified")] pub start_l2_block: Option, + /// The address of the metrics server. + #[clap(long, help = "Address of the metrics server")] + pub metrics_server_addr: Option, + /// The metrics server port. + #[clap(long, help = "Port of the metrics server")] + pub metrics_server_port: Option, +} + +impl Cli { + /// Returns the full metrics server address string. + pub fn metrics_server_addr(&self) -> String { + format!( + "{}:{}", + self.metrics_server_addr + .clone() + .unwrap_or_else(|| DEFAULT_METRICS_SERVER_ADDR.to_string()), + self.metrics_server_port.unwrap_or(DEFAULT_METRICS_SERVER_PORT) + ) + } + + /// Returns the l1 rpc url from CLI or environment variable. + pub fn l1_rpc_url(&self) -> Result { + let url = if let Some(s) = self.l1_rpc_url.clone() { + s + } else { + std::env::var(L1_RPC_URL).map_err(|e| anyhow!(e))? + }; + Url::parse(&url).map_err(|e| anyhow!(e)) + } + + /// Returns the l2 rpc url from CLI or environment variable. + pub fn l2_rpc_url(&self) -> Result { + let url = if let Some(s) = self.l2_rpc_url.clone() { + s + } else { + std::env::var(L2_RPC_URL).map_err(|e| anyhow!(e))? + }; + Url::parse(&url).map_err(|e| anyhow!(e)) + } + + /// Returns the beacon url from CLI or environment variable. + pub fn beacon_url(&self) -> Result { + Ok(if let Some(s) = self.beacon_url.clone() { + s + } else { + std::env::var(BEACON_URL).map_err(|e| anyhow!(e))? + }) + } } diff --git a/examples/trusted-sync/src/main.rs b/examples/trusted-sync/src/main.rs index 41d7e78fa..53ff181d4 100644 --- a/examples/trusted-sync/src/main.rs +++ b/examples/trusted-sync/src/main.rs @@ -1,45 +1,43 @@ -use anyhow::{anyhow, Result}; +use anyhow::Result; use clap::Parser; use kona_derive::online::*; -use reqwest::Url; use std::sync::Arc; -use tracing::{debug, error, info, warn, Level}; +use tracing::{debug, error, info, warn}; mod cli; +mod metrics; +mod telemetry; mod validation; -// Environment Variables -const L1_RPC_URL: &str = "L1_RPC_URL"; -const L2_RPC_URL: &str = "L2_RPC_URL"; -const BEACON_URL: &str = "BEACON_URL"; - const LOG_TARGET: &str = "trusted-sync"; -#[tokio::main] +#[actix_web::main] async fn main() -> Result<()> { - let cfg = crate::cli::Cli::parse(); - init_tracing_subscriber(cfg.v)?; - sync(cfg).await + let cfg = cli::Cli::parse(); + telemetry::init(cfg.v)?; + let addr = cfg.metrics_server_addr(); + let handle = tokio::spawn(async { sync(cfg).await }); + tokio::select! { + res = metrics::serve_metrics(&addr) => { + error!(target: LOG_TARGET, "Metrics server failed: {:?}", res); + return res.map_err(|e| anyhow::anyhow!(e)); + } + val = handle => { + error!(target: LOG_TARGET, "Sync failed: {:?}", val); + anyhow::bail!("Sync failed: {:?}", val); + } + } } -async fn sync(cli_cfg: crate::cli::Cli) -> Result<()> { - // Parse the CLI arguments and environment variables. - let l1_rpc_url: Url = cli_cfg - .l1_rpc_url - .unwrap_or_else(|| std::env::var(L1_RPC_URL).unwrap()) - .parse() - .expect("valid l1 rpc url"); - let l2_rpc_url: Url = cli_cfg - .l2_rpc_url - .unwrap_or_else(|| std::env::var(L2_RPC_URL).unwrap()) - .parse() - .expect("valid l2 rpc url"); - let beacon_url: String = - cli_cfg.beacon_url.unwrap_or_else(|| std::env::var(BEACON_URL).unwrap()); +async fn sync(cli: cli::Cli) -> Result<()> { + // Parse CLI arguments. + let l1_rpc_url = cli.l1_rpc_url()?; + let l2_rpc_url = cli.l2_rpc_url()?; + let beacon_url = cli.beacon_url()?; // Query for the L2 Chain ID let mut l2_provider = - AlloyL2ChainProvider::new_http(l2_rpc_url.clone(), Arc::new(RollupConfig::default())); + AlloyL2ChainProvider::new_http(l2_rpc_url.clone(), Arc::new(Default::default())); let l2_chain_id = l2_provider.chain_id().await.expect("Failed to fetch chain ID from L2 provider"); let cfg = RollupConfig::from_l2_chain_id(l2_chain_id) @@ -48,7 +46,7 @@ async fn sync(cli_cfg: crate::cli::Cli) -> Result<()> { // Construct the pipeline let mut l1_provider = AlloyChainProvider::new_http(l1_rpc_url); - let start = cli_cfg.start_l2_block.unwrap_or(cfg.genesis.l2.number); + let start = cli.start_l2_block.unwrap_or(cfg.genesis.l2.number); let mut l2_provider = AlloyL2ChainProvider::new_http(l2_rpc_url.clone(), cfg.clone()); let attributes = StatefulAttributesBuilder::new(cfg.clone(), l2_provider.clone(), l1_provider.clone()); @@ -60,6 +58,7 @@ async fn sync(cli_cfg: crate::cli::Cli) -> Result<()> { .l2_block_info_by_number(start) .await .expect("Failed to fetch genesis L2 block info for pipeline cursor"); + metrics::SAFE_L2_HEAD.inc_by(cursor.block_info.number); let tip = l1_provider .block_info_by_number(cursor.l1_origin.number) .await @@ -67,11 +66,10 @@ async fn sync(cli_cfg: crate::cli::Cli) -> Result<()> { let validator = validation::OnlineValidator::new_http(l2_rpc_url.clone(), &cfg); let mut pipeline = new_online_pipeline(cfg, l1_provider, dap, l2_provider.clone(), attributes, tip); - let mut derived_attributes_count = 0; // Continuously step on the pipeline and validate payloads. loop { - info!(target: LOG_TARGET, "Validated payload attributes number {}", derived_attributes_count); + info!(target: LOG_TARGET, "Validated payload attributes number {}", metrics::DERIVED_ATTRIBUTES_COUNT.get()); info!(target: LOG_TARGET, "Pending l2 safe head num: {}", cursor.block_info.number); match pipeline.step(cursor).await { Ok(_) => info!(target: "loop", "Stepped derivation pipeline"), @@ -83,15 +81,19 @@ async fn sync(cli_cfg: crate::cli::Cli) -> Result<()> { error!(target: LOG_TARGET, "Failed payload validation: {}", attributes.parent.block_info.hash); return Ok(()); } - derived_attributes_count += 1; + metrics::DERIVED_ATTRIBUTES_COUNT.inc(); match l2_provider.l2_block_info_by_number(cursor.block_info.number + 1).await { - Ok(bi) => cursor = bi, + Ok(bi) => { + cursor = bi; + metrics::SAFE_L2_HEAD.inc(); + } Err(e) => { error!(target: LOG_TARGET, "Failed to fetch next pending l2 safe head: {}, err: {:?}", cursor.block_info.number + 1, e); } } println!( - "Validated Payload Attributes {derived_attributes_count} [L2 Block Num: {}] [L2 Timestamp: {}] [L1 Origin Block Num: {}]", + "Validated Payload Attributes {} [L2 Block Num: {}] [L2 Timestamp: {}] [L1 Origin Block Num: {}]", + metrics::DERIVED_ATTRIBUTES_COUNT.get(), attributes.parent.block_info.number + 1, attributes.attributes.timestamp, pipeline.origin().unwrap().number, @@ -102,16 +104,3 @@ async fn sync(cli_cfg: crate::cli::Cli) -> Result<()> { } } } - -fn init_tracing_subscriber(v: u8) -> Result<()> { - let subscriber = tracing_subscriber::fmt() - .with_max_level(match v { - 0 => Level::ERROR, - 1 => Level::WARN, - 2 => Level::INFO, - 3 => Level::DEBUG, - _ => Level::TRACE, - }) - .finish(); - tracing::subscriber::set_global_default(subscriber).map_err(|e| anyhow!(e)) -} diff --git a/examples/trusted-sync/src/metrics.rs b/examples/trusted-sync/src/metrics.rs new file mode 100644 index 000000000..4f4a64fe1 --- /dev/null +++ b/examples/trusted-sync/src/metrics.rs @@ -0,0 +1,47 @@ +//! Metrics for the trusted sync example. + +use actix_web::{get, App, HttpServer, Responder}; +use anyhow::Result; +use prometheus::{self, Encoder, IntCounter, TextEncoder}; + +use lazy_static::lazy_static; +use prometheus::register_int_counter; + +lazy_static! { + pub static ref DERIVED_ATTRIBUTES_COUNT: IntCounter = register_int_counter!( + "derived_attributes_count", + "Number of total payload attributes derived" + ) + .unwrap(); + pub static ref SAFE_L2_HEAD: IntCounter = + register_int_counter!("safe_l2_head", "Pending L2 safe head").unwrap(); +} + +/// Starts the metrics server. +pub async fn serve_metrics(bind: &str) -> Result<()> { + let _ = HttpServer::new(|| App::new().service(index).service(metrics)) + .bind(bind) + .map_err(|e| anyhow::anyhow!(e))? + .run() + .await; + Ok(()) +} + +#[get("/")] +async fn index() -> impl Responder { + "trusted-sync-metrics-server: visit /metrics to view metrics" +} + +#[get("/metrics")] +async fn metrics() -> impl Responder { + let encoder = TextEncoder::new(); + let mut buffer = vec![]; + if let Err(e) = encoder.encode(&prometheus::gather(), &mut buffer) { + tracing::error!("Failed to encode prometheus metrics: {:?}", e); + } + + let response = String::from_utf8(buffer.clone()).expect("Failed to convert bytes to string"); + buffer.clear(); + + response +} diff --git a/examples/trusted-sync/src/telemetry.rs b/examples/trusted-sync/src/telemetry.rs new file mode 100644 index 000000000..693ab38c6 --- /dev/null +++ b/examples/trusted-sync/src/telemetry.rs @@ -0,0 +1,15 @@ +use anyhow::{anyhow, Result}; +use tracing::Level; + +pub fn init(v: u8) -> Result<()> { + let subscriber = tracing_subscriber::fmt() + .with_max_level(match v { + 0 => Level::ERROR, + 1 => Level::WARN, + 2 => Level::INFO, + 3 => Level::DEBUG, + _ => Level::TRACE, + }) + .finish(); + tracing::subscriber::set_global_default(subscriber).map_err(|e| anyhow!(e)) +}