feat: OpenTelemetry Protocol support for metrics (#1473)

fixes: #1458

---------
Co-authored-by: aawsome <37850842+aawsome@users.noreply.github.com>
This commit is contained in:
Dubzer 2025-06-29 23:47:13 +03:00 committed by GitHub
parent 48b51a417c
commit 3a3b5b187e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 774 additions and 455 deletions

1
.gitignore vendored
View File

@ -5,6 +5,7 @@ mutants.out
cargo-test*
coverage/*lcov
.testscompletions-*
.DS_Store
# Ignore generated test files
/tests/generated/*.toml

356
Cargo.lock generated
View File

@ -103,7 +103,7 @@ dependencies = [
"getrandom 0.3.3",
"once_cell",
"version_check",
"zerocopy 0.8.25",
"zerocopy 0.8.26",
]
[[package]]
@ -248,14 +248,14 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[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 = "awaitable"
@ -402,9 +402,9 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.18.1"
version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "bytemuck"
@ -474,7 +474,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -486,7 +486,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -629,7 +629,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -648,7 +648,7 @@ dependencies = [
"eyre",
"indenter",
"once_cell",
"owo-colors 4.2.1",
"owo-colors 4.2.2",
]
[[package]]
@ -721,7 +721,7 @@ dependencies = [
"proc-macro-error2",
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -908,9 +908,9 @@ dependencies = [
[[package]]
name = "crunchy"
version = "0.2.3"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "crypto-common"
@ -965,7 +965,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -989,7 +989,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -1000,7 +1000,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -1095,7 +1095,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -1106,7 +1106,7 @@ checksum = "64b697ac90ff296f0fc031ee5a61c7ac31fb9fff50e3fb32873b09223613fc0c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -1126,7 +1126,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
"unicode-xid",
]
@ -1139,7 +1139,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -1241,7 +1241,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -1340,7 +1340,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -1362,7 +1362,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -1383,12 +1383,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.12"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
dependencies = [
"libc",
"windows-sys 0.59.0",
"windows-sys 0.60.2",
]
[[package]]
@ -1584,7 +1584,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -1775,7 +1775,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http 0.2.12",
"indexmap 2.9.0",
"indexmap 2.10.0",
"slab",
"tokio",
"tokio-util",
@ -2064,13 +2064,13 @@ dependencies = [
"http 1.3.1",
"hyper 1.6.0",
"hyper-util",
"rustls 0.23.27",
"rustls 0.23.28",
"rustls-native-certs",
"rustls-pki-types",
"tokio",
"tokio-rustls 0.26.2",
"tower-service",
"webpki-roots 1.0.0",
"webpki-roots 1.0.1",
]
[[package]]
@ -2269,9 +2269,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.9.0"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
dependencies = [
"equivalent",
"hashbrown 0.15.4",
@ -2330,7 +2330,7 @@ dependencies = [
"indoc",
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -2435,7 +2435,7 @@ checksum = "848f085cdaa2c0508c87e021392ec5fe3fd7da90d6d548f3c1790e1d499f3080"
dependencies = [
"foldhash",
"hifijson",
"indexmap 2.9.0",
"indexmap 2.10.0",
"jaq-core",
"jaq-std",
"serde_json",
@ -2534,9 +2534,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.172"
version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "libm"
@ -2546,9 +2546,9 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
[[package]]
name = "libmimalloc-sys"
version = "0.1.42"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec9d6fac27761dabcd4ee73571cdb06b7022dc99089acbe5435691edffaac0f4"
checksum = "bf88cd67e9de251c1781dbe2f641a1a3ad66eaae831b8a2c38fbdc5ddae16d4d"
dependencies = [
"cc",
"libc",
@ -2556,9 +2556,9 @@ dependencies = [
[[package]]
name = "libredox"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638"
dependencies = [
"bitflags 2.9.1",
"libc",
@ -2654,9 +2654,9 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "mimalloc"
version = "0.1.46"
version = "0.1.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "995942f432bbb4822a7e9c3faa87a695185b0d09273ba85f097b54f4e458f2af"
checksum = "b1791cbe101e95af5764f06f20f6760521f7158f69dbf9d6baf941ee1bf6bc40"
dependencies = [
"libmimalloc-sys",
]
@ -2834,7 +2834,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -3068,6 +3068,78 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "opentelemetry"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaf416e4cb72756655126f7dd7bb0af49c674f4c1b9903e80c009e0c37e552e6"
dependencies = [
"futures-core",
"futures-sink",
"js-sys",
"pin-project-lite",
"thiserror 2.0.12",
"tracing",
]
[[package]]
name = "opentelemetry-http"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f6639e842a97dbea8886e3439710ae463120091e2e064518ba8e716e6ac36d"
dependencies = [
"async-trait",
"bytes",
"http 1.3.1",
"opentelemetry",
"reqwest 0.12.20",
]
[[package]]
name = "opentelemetry-otlp"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbee664a43e07615731afc539ca60c6d9f1a9425e25ca09c57bc36c87c55852b"
dependencies = [
"http 1.3.1",
"opentelemetry",
"opentelemetry-http",
"opentelemetry-proto",
"opentelemetry_sdk",
"prost",
"reqwest 0.12.20",
"thiserror 2.0.12",
"tracing",
]
[[package]]
name = "opentelemetry-proto"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e046fd7660710fe5a05e8748e70d9058dc15c94ba914e7c4faa7c728f0e8ddc"
dependencies = [
"opentelemetry",
"opentelemetry_sdk",
"prost",
"tonic",
]
[[package]]
name = "opentelemetry_sdk"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11f644aa9e5e31d11896e024305d7e3c98a88884d9f8919dbf37a9991bc47a4b"
dependencies = [
"futures-channel",
"futures-executor",
"futures-util",
"opentelemetry",
"percent-encoding",
"rand 0.9.1",
"serde_json",
"thiserror 2.0.12",
]
[[package]]
name = "option-ext"
version = "0.2.0"
@ -3119,9 +3191,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
name = "owo-colors"
version = "4.2.1"
version = "4.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26995317201fa17f3656c36716aed4a7c81743a9634ac4c99c0eeda495db0cec"
checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e"
[[package]]
name = "page_size"
@ -3257,7 +3329,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -3323,7 +3395,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d77244ce2d584cd84f6a15f86195b8c9b2a0dfbfd817c09e0464244091a58ed"
dependencies = [
"base64 0.22.1",
"indexmap 2.9.0",
"indexmap 2.10.0",
"quick-xml 0.37.5",
"serde",
"time",
@ -3367,7 +3439,7 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy 0.8.25",
"zerocopy 0.8.26",
]
[[package]]
@ -3438,7 +3510,7 @@ dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -3485,7 +3557,7 @@ dependencies = [
"itertools 0.13.0",
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -3573,7 +3645,7 @@ checksum = "f71ee38b42f8459a88d3362be6f9b841ad2d5421844f61eb1c59c11bff3ac14a"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -3588,7 +3660,7 @@ dependencies = [
"quinn-proto",
"quinn-udp",
"rustc-hash",
"rustls 0.23.27",
"rustls 0.23.28",
"socket2",
"thiserror 2.0.12",
"tokio",
@ -3608,7 +3680,7 @@ dependencies = [
"rand 0.9.1",
"ring",
"rustc-hash",
"rustls 0.23.27",
"rustls 0.23.28",
"rustls-pki-types",
"slab",
"thiserror 2.0.12",
@ -3619,9 +3691,9 @@ dependencies = [
[[package]]
name = "quinn-udp"
version = "0.5.12"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842"
checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970"
dependencies = [
"cfg_aliases",
"libc",
@ -3642,9 +3714,9 @@ dependencies = [
[[package]]
name = "r-efi"
version = "5.2.0"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
@ -3786,6 +3858,26 @@ dependencies = [
"thiserror 2.0.12",
]
[[package]]
name = "ref-cast"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf"
dependencies = [
"ref-cast-impl",
]
[[package]]
name = "ref-cast-impl"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "regex"
version = "1.11.1"
@ -3844,9 +3936,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
[[package]]
name = "reqsign"
version = "0.16.3"
version = "0.16.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9323c0afb30e54f793f4705b10c890395bccc87c6e6ea62c4e7e82d09a380dc6"
checksum = "0e81996ab38ccfb2e295ecd3250c0fb7717e29ab8814d4bc0fd6097338d87518"
dependencies = [
"anyhow",
"async-trait",
@ -3871,6 +3963,7 @@ dependencies = [
"serde_json",
"sha1",
"sha2",
"tokio",
]
[[package]]
@ -3936,7 +4029,7 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls 0.23.27",
"rustls 0.23.28",
"rustls-native-certs",
"rustls-pki-types",
"serde",
@ -3954,7 +4047,7 @@ dependencies = [
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
"webpki-roots 1.0.0",
"webpki-roots 1.0.1",
]
[[package]]
@ -3984,7 +4077,7 @@ checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -4059,7 +4152,7 @@ dependencies = [
"regex",
"relative-path",
"rustc_version",
"syn 2.0.103",
"syn 2.0.104",
"unicode-ident",
]
@ -4150,6 +4243,9 @@ dependencies = [
"log",
"mimalloc",
"open",
"opentelemetry",
"opentelemetry-otlp",
"opentelemetry_sdk",
"predicates",
"pretty_assertions",
"prometheus",
@ -4326,9 +4422,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.27"
version = "0.23.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321"
checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643"
dependencies = [
"once_cell",
"ring",
@ -4429,6 +4525,18 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "schemars"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f"
dependencies = [
"dyn-clone",
"ref-cast",
"serde",
"serde_json",
]
[[package]]
name = "scoped-tls"
version = "1.0.1"
@ -4578,7 +4686,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -4616,15 +4724,16 @@ dependencies = [
[[package]]
name = "serde_with"
version = "3.12.0"
version = "3.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa"
checksum = "bf65a400f8f66fb7b0552869ad70157166676db75ed8181f8104ea91cf9d0b42"
dependencies = [
"base64 0.22.1",
"chrono",
"hex",
"indexmap 1.9.3",
"indexmap 2.9.0",
"indexmap 2.10.0",
"schemars",
"serde",
"serde_derive",
"serde_json",
@ -4634,14 +4743,14 @@ dependencies = [
[[package]]
name = "serde_with_macros"
version = "3.12.0"
version = "3.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e"
checksum = "81679d9ed988d5e9a5e6531dc3f2c28efbd639cbd1dfb628df08edea6004da77"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -4780,12 +4889,9 @@ dependencies = [
[[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"
@ -4915,7 +5021,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -4928,7 +5034,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -4950,9 +5056,9 @@ dependencies = [
[[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",
@ -4994,7 +5100,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -5108,7 +5214,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -5119,7 +5225,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -5243,7 +5349,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -5262,7 +5368,18 @@ version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
dependencies = [
"rustls 0.23.27",
"rustls 0.23.28",
"tokio",
]
[[package]]
name = "tokio-stream"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
]
@ -5318,7 +5435,7 @@ version = "0.22.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [
"indexmap 2.9.0",
"indexmap 2.10.0",
"serde",
"serde_spanned",
"toml_datetime",
@ -5332,6 +5449,27 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
[[package]]
name = "tonic"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9"
dependencies = [
"async-trait",
"base64 0.22.1",
"bytes",
"http 1.3.1",
"http-body 1.0.1",
"http-body-util",
"percent-encoding",
"pin-project",
"prost",
"tokio-stream",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower"
version = "0.5.2"
@ -5391,13 +5529,13 @@ dependencies = [
[[package]]
name = "tracing-attributes"
version = "0.1.29"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662"
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -5736,7 +5874,7 @@ dependencies = [
"log",
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
"wasm-bindgen-shared",
]
@ -5771,7 +5909,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -5826,9 +5964,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]]
name = "webpki-roots"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb"
checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502"
dependencies = [
"rustls-pki-types",
]
@ -5918,7 +6056,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -5929,7 +6067,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -6232,9 +6370,9 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
[[package]]
name = "xattr"
version = "1.5.0"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e"
checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909"
dependencies = [
"libc",
"rustix 1.0.7",
@ -6281,7 +6419,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
"synstructure 0.13.2",
]
@ -6297,11 +6435,11 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.25"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
dependencies = [
"zerocopy-derive 0.8.25",
"zerocopy-derive 0.8.26",
]
[[package]]
@ -6312,18 +6450,18 @@ checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.25"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -6343,7 +6481,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
"synstructure 0.13.2",
]
@ -6383,7 +6521,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"syn 2.0.104",
]
[[package]]
@ -6398,7 +6536,7 @@ dependencies = [
"crossbeam-utils",
"displaydoc",
"flate2",
"indexmap 2.9.0",
"indexmap 2.10.0",
"memchr",
"thiserror 2.0.12",
"zopfli",

View File

@ -18,7 +18,7 @@ rustic - fast, encrypted, deduplicated backups powered by Rust
"""
[features]
default = ["jq", "prometheus", "rhai", "tui", "webdav"]
default = ["jq", "prometheus", "opentelemetry", "rhai", "tui", "webdav"]
release = ["default", "self-update"]
# Allocators
@ -28,6 +28,11 @@ jemallocator = ["dep:jemallocator-global"]
# Commands
mount = ["dep:fuse_mt"]
prometheus = ["dep:prometheus", "dep:base64"]
opentelemetry = [
"dep:opentelemetry",
"dep:opentelemetry-otlp",
"dep:opentelemetry_sdk",
]
self-update = ["dep:self_update", "dep:semver"]
tui = ["dep:ratatui", "dep:crossterm", "dep:tui-textarea"]
webdav = [
@ -130,6 +135,9 @@ zip = { version = "2.3.0", default-features = false, features = ["deflate", "chr
jaq-core = { version = "2", optional = true }
jaq-json = { version = "1", features = ["serde_json"], optional = true }
jaq-std = { version = "2", optional = true }
opentelemetry = { version = "0.30.0", default-features = false, features = ["metrics"], optional = true }
opentelemetry-otlp = { version = "0.30.0", features = ["metrics"], optional = true }
opentelemetry_sdk = { version = "0.30.0", default-features = false, features = ["metrics"], optional = true }
rhai = { version = "1", features = ["sync", "serde", "no_optimize", "no_module", "no_custom_syntax", "only_i64"], optional = true }
[dev-dependencies]

View File

@ -75,18 +75,19 @@ If you want to contribute your own configuration, please
### Global Options `[global]`
| Attribute | Description | Default Value | Example Value | Environment Variable | CLI Option |
| ----------------- | --------------------------------------------------------------------------------- | ------------- | ----------------- | ------------------------ | ------------------- |
| check-index | If true, check the index and read pack headers if index information is missing. | false | | RUSTIC_CHECK_INDEX | --check-index |
| dry-run | If true, performs a dry run without making any changes. | false | | RUSTIC_DRY_RUN | --dry-run, -n |
| log-level | Logging level. Possible values: "off", "error", "warn", "info", "debug", "trace". | "info" | | RUSTIC_LOG_LEVEL | --log-level |
| log-file | Path to the log file. | No log file | "/log/rustic.log" | RUSTIC_LOG_FILE | --log-file |
| no-progress | If true, disables progress indicators. | false | | RUSTIC_NO_PROGRESS | --no-progress |
| progress-interval | The interval at which progress indicators are shown. | "100ms" | "1m" | RUSTIC_PROGRESS_INTERVAL | --progress-interval |
| use-profiles | Array of profiles to use. Allows to recursively use other profiles. | Empty array | ["2nd", "3rd"] | RUSTIC_USE_PROFILE | --use-profile, -P |
| prometheus | URL of a Prometheus Pushgateway to push metrics to. | Not set | "http://gateway/" | RUSTIC_PROMETHEUS | --prometheus |
| prometheus-user | username to authenticate to the Prometheus Pushgateway | Not set | "myuser" | RUSTIC_PROMETHEUS_USER | --prometheus-user |
| prometheus-pass | password to authenticate to the Prometheus Pushgateway | Not set | "secret" | RUSTIC_PROMETHEUS_PASS | --prometheus-pass |
| Attribute | Description | Default Value | Example Value | Environment Variable | CLI Option |
| ----------------- | --------------------------------------------------------------------------------- | ------------- | ------------------------ | ------------------------------------------------ | ------------------- |
| check-index | If true, check the index and read pack headers if index information is missing. | false | | RUSTIC_CHECK_INDEX | --check-index |
| dry-run | If true, performs a dry run without making any changes. | false | | RUSTIC_DRY_RUN | --dry-run, -n |
| log-level | Logging level. Possible values: "off", "error", "warn", "info", "debug", "trace". | "info" | | RUSTIC_LOG_LEVEL | --log-level |
| log-file | Path to the log file. | No log file | "/log/rustic.log" | RUSTIC_LOG_FILE | --log-file |
| no-progress | If true, disables progress indicators. | false | | RUSTIC_NO_PROGRESS | --no-progress |
| progress-interval | The interval at which progress indicators are shown. | "100ms" | "1m" | RUSTIC_PROGRESS_INTERVAL | --progress-interval |
| use-profiles | Array of profiles to use. Allows to recursively use other profiles. | Empty array | ["2nd", "3rd"] | RUSTIC_USE_PROFILE | --use-profile, -P |
| prometheus | URL of a Prometheus Pushgateway to push metrics to. | Not set | "http://gateway/" | RUSTIC_PROMETHEUS | --prometheus |
| prometheus-user | Username to authenticate to the Prometheus Pushgateway | Not set | "myuser" | RUSTIC_PROMETHEUS_USER | --prometheus-user |
| prometheus-pass | Password to authenticate to the Prometheus Pushgateway | Not set | "secret" | RUSTIC_PROMETHEUS_PASS | --prometheus-pass |
| opentelemetry | OpenTelemetry metrics endpoint (HTTP Protobuf) | Not set | "http://otel/v1/metrics" | RUSTIC_OTEL, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT | --opentelemetry |
### Global Hooks `[global.hooks]`
@ -112,9 +113,9 @@ rustic.
config profile as a possible source of errors if you encounter problems. They
could possibly shadow other values that you have already set.
### Global Prometheus labels `[global.prometheus-labels]`
### Global Metrics labels `[global.metrics-labels]`
All given labels are reported to the Promethus Pushgateway, if it is configured.
All given labels are included with the metrics, if it is configured.
### Repository Options `[repository]`
@ -189,41 +190,41 @@ See [Global Hooks](#global-hooks-globalhooks).
**Note**: If set here, the backup options apply for all sources, although they
can be overwritten in the source-specific configuration, see below.
| Attribute | Description | Default Value | Example Value | CLI Option |
| ------------------ | -------------------------------------------------------------------------------------------- | --------------------- | ------------- | -------------------- |
| as-path | Specifies the path for the backup when the source contains a single path. | Not set | | --as-path |
| command | Set the command saved in the snapshot. | The full command used | | --command |
| custom-ignorefiles | Array of names of custom ignorefiles which will be used to exclude files. | [] | | --custom-ignorefile |
| description | Description for the snapshot. | Not set | | --description |
| description-from | Path to a file containing the description for the snapshot. | Not set | | --description-from |
| delete-never | If true, never delete the snapshot. | false | | --delete-never |
| delete-after | Time duration after which the snapshot be deleted. | Not set | | --delete-after |
| exclude-if-present | Array of filenames to exclude from the backup if they are present. | [] | | --exclude-if-present |
| force | If true, forces the backup even if no changes are detected. | false | | --force |
| git-ignore | If true, use .gitignore rules to exclude files from the backup in the source directory. | false | | --git-ignore |
| globs | Array of globs specifying what to include/exclude in the backup. | [] | | --glob |
| glob-files | Array or string of glob files specifying what to include/exclude in the backup. | [] | | --glob-file |
| group-by | Grouping strategy to find parent snapshot. | "host,label,paths" | | --group-by |
| host | Host name used in the snapshot. | local hostname | | --host |
| iglobs | Like glob, but apply case-insensitive | [] | | --iglob |
| iglob-files | Like glob-file, but apply case-insensitive | [] | | --iglob-file |
| ignore-devid | If true, don't save device ID. | false | | --ignore-devid |
| ignore-ctime | If true, ignore file change time (ctime). | false | | --ignore-ctime |
| ignore-inode | If true, ignore file inode for the backup. | false | | --ignore-inode |
| init | If true, initialize repository if it doesn't exist, yet. | false | | --init |
| json | If true, returns output of the command as json. | false | | --json |
| label | Set label for the snapshot. | Not set | | --label |
| no-require-git | (with git-ignore:) Apply .git-ignore files even if they are not in a git repository. | false | | --no-require-git |
| no-scan | Don't scan the backup source for its size (disables ETA). | false | | --no-scan |
| one-file-system | If true, only backs up files from the same filesystem as the source. | false | | --one-file-system |
| parent | Parent snapshot ID for the backup. | Not set | | --parent |
| quiet | Don't output backup summary. | false | | --quiet |
| skip-if-unchanged | Skip saving of the snapshot if it is identical to the parent. | false | | --skip-if-unchanged |
| stdin-filename | File name to be used when reading from stdin. | Not set | | --stdin-filename |
| tags | Array of tags for the backup. | [] | | --tag |
| time | Set the time saved in the snapshot. | current time | | --time |
| with-atime | If true, includes file access time (atime) in the backup. | false | | --with-atime |
| prometheus-job | jobname used when pushing to the Prometheus Pushgateway (if global prometheus option is set) | "rustic-backup" | "myjob" | --prometheus-job |
| Attribute | Description | Default Value | Example Value | CLI Option |
| ------------------ | --------------------------------------------------------------------------------------- | --------------------- | ------------- | ----------------------- |
| as-path | Specifies the path for the backup when the source contains a single path. | Not set | | --as-path |
| command | Set the command saved in the snapshot. | The full command used | | --command |
| custom-ignorefiles | Array of names of custom ignorefiles which will be used to exclude files. | [] | | --custom-ignorefile |
| description | Description for the snapshot. | Not set | | --description |
| description-from | Path to a file containing the description for the snapshot. | Not set | | --description-from |
| delete-never | If true, never delete the snapshot. | false | | --delete-never |
| delete-after | Time duration after which the snapshot be deleted. | Not set | | --delete-after |
| exclude-if-present | Array of filenames to exclude from the backup if they are present. | [] | | --exclude-if-present |
| force | If true, forces the backup even if no changes are detected. | false | | --force |
| git-ignore | If true, use .gitignore rules to exclude files from the backup in the source directory. | false | | --git-ignore |
| globs | Array of globs specifying what to include/exclude in the backup. | [] | | --glob |
| glob-files | Array or string of glob files specifying what to include/exclude in the backup. | [] | | --glob-file |
| group-by | Grouping strategy to find parent snapshot. | "host,label,paths" | | --group-by |
| host | Host name used in the snapshot. | local hostname | | --host |
| iglobs | Like glob, but apply case-insensitive | [] | | --iglob |
| iglob-files | Like glob-file, but apply case-insensitive | [] | | --iglob-file |
| ignore-devid | If true, don't save device ID. | false | | --ignore-devid |
| ignore-ctime | If true, ignore file change time (ctime). | false | | --ignore-ctime |
| ignore-inode | If true, ignore file inode for the backup. | false | | --ignore-inode |
| init | If true, initialize repository if it doesn't exist, yet. | false | | --init |
| json | If true, returns output of the command as json. | false | | --json |
| label | Set label for the snapshot. | Not set | | --label |
| no-require-git | (with git-ignore:) Apply .git-ignore files even if they are not in a git repository. | false | | --no-require-git |
| no-scan | Don't scan the backup source for its size (disables ETA). | false | | --no-scan |
| one-file-system | If true, only backs up files from the same filesystem as the source. | false | | --one-file-system |
| parent | Parent snapshot ID for the backup. | Not set | | --parent |
| quiet | Don't output backup summary. | false | | --quiet |
| skip-if-unchanged | Skip saving of the snapshot if it is identical to the parent. | false | | --skip-identical-parent |
| stdin-filename | File name to be used when reading from stdin. | Not set | | --stdin-filename |
| tags | Array of tags for the backup. | [] | | --tag |
| time | Set the time saved in the snapshot. | current time | | --time |
| with-atime | If true, includes file access time (atime) in the backup. | false | | --with-atime |
| metrics-job | jobname used when pushing metrics (if global prometheus or opentelemetry option is set) | "rustic-backup" | "myjob" | --metrics-job |
### Backup Hooks `[backup.hooks]`
@ -233,10 +234,9 @@ These external commands are run before and after each backup, respectively.
See [Global Hooks](#global-hooks-globalhooks).
### Backup Prometheus lables `[backup.prometheus-labels]`
### Backup Metrics lables `[backup.metrics-labels]`
See
[Global Prometheus labels](#global-prometheus-labels-globalprometheus_labels).
See [Global Metrics labels](#global-metrics-labels-globalmetrics-labels).
### Backup Snapshots `[[backup.snapshots]]`

View File

@ -18,6 +18,9 @@ check-index = false
prometheus = "http://push-gateway/"
prometheus-user = "user"
prometheus-pass = "secret"
# Sending metrics directly to the Prometheus in this example
# See https://prometheus.io/docs/guides/opentelemetry/
opentelemetry = "http://prometheus/api/v1/otlp/v1/metrics"
# Global hooks: The given commands are called for every command
[global.hooks]
@ -35,7 +38,7 @@ run-finally = ["echo finally"] # Always run after, default: []
# This is only an example how to set an rclone env variable. Default: No variables are defined.
RCLONE_XXX = "true"
[global.prometheus-labels]
[global.metrics-labels]
label-a = "xxx"
# Repository options: These options define which backend to use and which password to use.
@ -138,7 +141,7 @@ init = false
no-scan = false
quiet = false
skip-if-unchanged = false
prometheus-job = "my-backup-jobs" # Only used if global prometheus option is set; default: not set
metrics-job = "my-backup-jobs" # Only used if global prometheus or opentelemetry option is set; default: not set
# Backup hooks: The given commands are called for the `backup` command
[backup.hooks]
@ -147,7 +150,7 @@ run-after = ["echo after"] # Run after if successful, default: []
run-failed = ["echo failed"] # Default: []
run-finally = ["echo finally"] # Always run after, default: []
[backup.prometheus-labels]
[backup.metrics-labels]
label-a = "xxx"
# Backup options for specific sources - all above options are also available here and replace them for the given source
@ -163,7 +166,7 @@ run-after = ["echo after"] # Run after if successful, default: []
run-failed = ["echo failed"] # Default: []
run-finally = ["echo finally"] # Always run after, default: []
[backup.snapshots.prometheus-labels]
[backup.snapshots.metrics-labels]
label-a = "xxx"
[[backup.snapshots]]

View File

@ -66,6 +66,7 @@ use clap::builder::{
use convert_case::{Case, Casing};
use human_panic::setup_panic;
use log::{Level, info, log};
use reqwest::Url;
use simplelog::{CombinedLogger, LevelFilter, TermLogger, TerminalMode, WriteLogger};
use self::find::FindCmd;
@ -220,7 +221,9 @@ impl Configurable<RusticConfig> for EntryPoint {
// That's why it says `_config`, because it's not read at all and therefore not needed.
let mut config = self.config.clone();
// collect "RUSTIC_REPO_OPT*" and "OPENDAL*" env variables
// collect "RUSTIC_REPO_OPT*" and "OPENDAL*" env variables.
// also add the standardized OTEL variables manually
// since clap does not support multiple variables for a single arg
for (var, value) in std::env::vars() {
if let Some(var) = var.strip_prefix("RUSTIC_REPO_OPT_") {
let var = var.from_case(Case::UpperSnake).to_case(Case::Kebab);
@ -240,6 +243,13 @@ impl Configurable<RusticConfig> for EntryPoint {
} else if let Some(var) = var.strip_prefix("OPENDALCOLD_") {
let var = var.from_case(Case::UpperSnake).to_case(Case::Snake);
_ = config.repository.be.options_cold.insert(var, value);
} else if var == "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT" {
#[cfg(feature = "opentelemetry")]
if let Ok(url) = Url::parse(&value) {
_ = config.global.opentelemetry.insert(url);
}
} else if var == "OTEL_SERVICE_NAME" && cfg!(feature = "opentelemetry") {
_ = config.backup.metrics_job.insert(value);
}
}

View File

@ -131,15 +131,15 @@ pub struct BackupCmd {
#[merge(skip)]
sources: Vec<String>,
/// Job name for the Prometheus Pushgateway push. Default: rustic-backup
#[clap(long, value_name = "JOB_NAME", env = "RUSTIC_PROMETHEUS_JOB")]
/// Job name for the metrics. Default: rustic-backup
#[clap(long, value_name = "JOB_NAME", env = "RUSTIC_METRICS_JOB")]
#[merge(strategy=conflate::option::overwrite_none)]
prometheus_job: Option<String>,
pub metrics_job: Option<String>,
/// Additional labels to set to generated Prometheus metrics
/// Additional labels to set to generated metrics
#[clap(long, value_name = "NAME=VALUE", value_parser = parse_labels, default_value = "")]
#[merge(strategy=conflate::btreemap::append_or_ignore)]
prometheus_labels: BTreeMap<String, String>,
metrics_labels: BTreeMap<String, String>,
}
/// Merge backup snapshots to generate
@ -342,26 +342,14 @@ impl BackupCmd {
info!("snapshot {} successfully saved.", snap.id);
}
#[cfg(feature = "prometheus")]
if config.global.is_prometheus_configured() {
#[cfg(feature = "prometheus")]
{
// Merge global prometheus labels
conflate::btreemap::append_or_ignore(
&mut self.prometheus_labels,
config.global.prometheus_labels.clone(),
);
if let Err(err) =
publish_metrics(&snap, self.prometheus_job, self.prometheus_labels)
{
warn!("error pushing prometheus metrics: {err}");
}
}
#[cfg(not(feature = "prometheus"))]
{
warn!(
"not pushing prometheus metrics - this rustic version is compiled without prometheus support"
);
if config.global.is_metrics_configured() {
// Merge global metrics labels
conflate::btreemap::append_or_ignore(
&mut self.metrics_labels,
config.global.metrics_labels.clone(),
);
if let Err(err) = publish_metrics(&snap, self.metrics_job, self.metrics_labels) {
warn!("error pushing metrics: {err}");
}
}
@ -370,150 +358,142 @@ impl BackupCmd {
}
}
#[cfg(feature = "prometheus")]
#[cfg(not(any(feature = "prometheus", feature = "opentelemetry")))]
fn publish_metrics(
snap: &SnapshotFile,
job_name: Option<String>,
mut labels: BTreeMap<String, String>,
) -> Result<()> {
use prometheus::register_gauge;
Err(anyhow!("metrics support is not compiled-in!"))
}
#[cfg(any(feature = "prometheus", feature = "opentelemetry"))]
fn publish_metrics(
snap: &SnapshotFile,
job_name: Option<String>,
mut labels: BTreeMap<String, String>,
) -> Result<()> {
use crate::metrics::MetricValue::*;
use crate::metrics::{Metric, MetricsExporter};
let summary = snap.summary.as_ref().expect("Reaching the 'push to prometheus' point should only happen for successful backups, which must have a summary set.");
let metric_time = register_gauge!("rustic_backup_time", "Timestamp of this snapshot",)
.context("registering prometheus gauge")?;
metric_time.set(snap.time.timestamp_millis() as f64 / 1000.);
let metric_files_new = register_gauge!(
"rustic_backup_files_new",
"New files compared to the last (i.e. parent) snapshot",
)
.context("registering prometheus gauge")?;
metric_files_new.set(summary.files_new as f64);
let metric_files_changed = register_gauge!(
"rustic_backup_files_changed",
"Changed files compared to the last (i.e. parent) snapshot",
)
.context("registering prometheus gauge")?;
metric_files_changed.set(summary.files_changed as f64);
let metric_files_unmodified = register_gauge!(
"rustic_backup_files_unmodified",
"Unchanged files compared to the last (i.e. parent) snapshot",
)
.context("registering prometheus gauge")?;
metric_files_unmodified.set(summary.files_unmodified as f64);
let metric_total_files_processed = register_gauge!(
"rustic_backup_total_files_processed",
"Total processed files",
)
.context("registering prometheus gauge")?;
metric_total_files_processed.set(summary.total_files_processed as f64);
let metric_total_bytes_processed = register_gauge!(
"rustic_backup_total_bytes_processed",
"Total size of all processed files",
)
.context("registering prometheus gauge")?;
metric_total_bytes_processed.set(summary.total_bytes_processed as f64);
let metric_dirs_new = register_gauge!(
"rustic_backup_dirs_new",
"New directories compared to the last (i.e. parent) snapshot",
)
.context("registering prometheus gauge")?;
metric_dirs_new.set(summary.dirs_new as f64);
let metric_dirs_changed = register_gauge!(
"rustic_backup_dirs_changed",
"Changed directories compared to the last (i.e. parent) snapshot",
)
.context("registering prometheus gauge")?;
metric_dirs_changed.set(summary.dirs_changed as f64);
let metric_dirs_unmodified = register_gauge!(
"rustic_backup_dirs_unmodified",
"Unchanged directories compared to the last (i.e. parent) snapshot",
)
.context("registering prometheus gauge")?;
metric_dirs_unmodified.set(summary.dirs_unmodified as f64);
let metric_total_dirs_processed = register_gauge!(
"rustic_backup_total_dirs_processed",
"Total processed directories",
)
.context("registering prometheus gauge")?;
metric_total_dirs_processed.set(summary.total_dirs_processed as f64);
let metric_total_dirsize_processed = register_gauge!(
"rustic_backup_total_dirsize_processed",
"Total number of data blobs added by this snapshot",
)
.context("registering prometheus gauge")?;
metric_total_dirsize_processed.set(summary.total_dirsize_processed as f64);
let metric_data_blobs = register_gauge!(
"rustic_backup_data_blobs",
"Total size of all processed dirs",
)
.context("registering prometheus gauge")?;
metric_data_blobs.set(summary.data_blobs as f64);
let metric_tree_blobs = register_gauge!(
"rustic_backup_tree_blobs",
"Total number of tree blobs added by this snapshot",
)
.context("registering prometheus gauge")?;
metric_tree_blobs.set(summary.tree_blobs as f64);
let metric_data_added = register_gauge!(
"rustic_backup_data_added",
"Total uncompressed bytes added by this snapshot",
)
.context("registering prometheus gauge")?;
metric_data_added.set(summary.data_added as f64);
let metric_data_added_packed = register_gauge!(
"rustic_backup_data_added_packed",
"Total bytes added to the repository by this snapshot",
)
.context("registering prometheus gauge")?;
metric_data_added_packed.set(summary.data_added_packed as f64);
let metric_data_added_files = register_gauge!(
"rustic_backup_data_added_files",
"Total uncompressed bytes (new/changed files) added by this snapshot",
)
.context("registering prometheus gauge")?;
metric_data_added_files.set(summary.data_added_files as f64);
let metric_data_added_files_packed = register_gauge!(
"rustic_backup_data_added_files_packed",
"Total bytes for new/changed files added to the repository by this snapshot",
)
.context("registering prometheus gauge")?;
metric_data_added_files_packed.set(summary.data_added_files_packed as f64);
let metric_data_added_trees = register_gauge!(
"rustic_backup_data_added_trees",
"Total uncompressed bytes (new/changed directories) added by this snapshot",
)
.context("registering prometheus gauge")?;
metric_data_added_trees.set(summary.data_added_trees as f64);
let metric_data_added_trees_packed = register_gauge!(
"rustic_backup_data_added_trees_packed",
"Total bytes (new/changed directories) added to the repository by this snapshot",
)
.context("registering prometheus gauge")?;
metric_data_added_trees_packed.set(summary.data_added_trees_packed as f64);
let metric_backup_start = register_gauge!(
"rustic_backup_backup_start",
"Start time of the backup. This may differ from the snapshot `time`.",
)
.context("registering prometheus gauge")?;
metric_backup_start.set(summary.backup_start.timestamp_millis() as f64 / 1000.);
let metric_backup_end = register_gauge!(
"rustic_backup_backup_end",
"The time that the backup has been finished.",
)
.context("registering prometheus gauge")?;
metric_backup_end.set(summary.backup_end.timestamp_millis() as f64 / 1000.);
let metric_backup_duration = register_gauge!(
"rustic_backup_backup_duration",
"Total duration of the backup in seconds, i.e. the time between `backup_start` and `backup_end`",
).context("registering prometheus gauge")?;
metric_backup_duration.set(summary.backup_duration);
let metric_total_duration = register_gauge!(
"rustic_backup_total_duration",
"Total duration that the rustic command ran in seconds",
)
.context("registering prometheus gauge")?;
metric_total_duration.set(summary.total_duration);
let metrics = [
Metric {
name: "rustic_backup_time",
description: "Timestamp of this snapshot",
value: Float(snap.time.timestamp_millis() as f64 / 1000.),
},
Metric {
name: "rustic_backup_files_new",
description: "New files compared to the last (i.e. parent) snapshot",
value: Int(summary.files_new),
},
Metric {
name: "rustic_backup_files_changed",
description: "Changed files compared to the last (i.e. parent) snapshot",
value: Int(summary.files_changed),
},
Metric {
name: "rustic_backup_files_unmodified",
description: "Unchanged files compared to the last (i.e. parent) snapshot",
value: Int(summary.files_unmodified),
},
Metric {
name: "rustic_backup_total_files_processed",
description: "Total processed files",
value: Int(summary.total_files_processed),
},
Metric {
name: "rustic_backup_total_bytes_processed",
description: "Total size of all processed files",
value: Int(summary.total_bytes_processed),
},
Metric {
name: "rustic_backup_dirs_new",
description: "New directories compared to the last (i.e. parent) snapshot",
value: Int(summary.dirs_new),
},
Metric {
name: "rustic_backup_dirs_changed",
description: "Changed directories compared to the last (i.e. parent) snapshot",
value: Int(summary.dirs_changed),
},
Metric {
name: "rustic_backup_dirs_unmodified",
description: "Unchanged directories compared to the last (i.e. parent) snapshot",
value: Int(summary.dirs_unmodified),
},
Metric {
name: "rustic_backup_total_dirs_processed",
description: "Total processed directories",
value: Int(summary.total_dirs_processed),
},
Metric {
name: "rustic_backup_total_dirsize_processed",
description: "Total size of all processed dirs",
value: Int(summary.total_dirsize_processed),
},
Metric {
name: "rustic_backup_data_blobs",
description: "Total number of data blobs added by this snapshot",
value: Int(summary.data_blobs),
},
Metric {
name: "rustic_backup_tree_blobs",
description: "Total number of tree blobs added by this snapshot",
value: Int(summary.tree_blobs),
},
Metric {
name: "rustic_backup_data_added",
description: "Total uncompressed bytes added by this snapshot",
value: Int(summary.data_added),
},
Metric {
name: "rustic_backup_data_added_packed",
description: "Total bytes added to the repository by this snapshot",
value: Int(summary.data_added_packed),
},
Metric {
name: "rustic_backup_data_added_files",
description: "Total uncompressed bytes (new/changed files) added by this snapshot",
value: Int(summary.data_added_files),
},
Metric {
name: "rustic_backup_data_added_files_packed",
description: "Total bytes for new/changed files added to the repository by this snapshot",
value: Int(summary.data_added_files_packed),
},
Metric {
name: "rustic_backup_data_added_trees",
description: "Total uncompressed bytes (new/changed directories) added by this snapshot",
value: Int(summary.data_added_trees),
},
Metric {
name: "rustic_backup_data_added_trees_packed",
description: "Total bytes (new/changed directories) added to the repository by this snapshot",
value: Int(summary.data_added_trees_packed),
},
Metric {
name: "rustic_backup_backup_start",
description: "Start time of the backup. This may differ from the snapshot `time`.",
value: Float(summary.backup_start.timestamp_millis() as f64 / 1000.),
},
Metric {
name: "rustic_backup_backup_end",
description: "The time that the backup has been finished.",
value: Float(summary.backup_end.timestamp_millis() as f64 / 1000.),
},
Metric {
name: "rustic_backup_backup_duration",
description: "Total duration of the backup in seconds, i.e. the time between `backup_start` and `backup_end`",
value: Float(summary.backup_duration),
},
Metric {
name: "rustic_backup_total_duration",
description: "Total duration that the rustic command ran in seconds",
value: Float(summary.total_duration),
},
];
_ = labels
.entry("paths".to_string())
@ -529,5 +509,49 @@ fn publish_metrics(
.or_insert_with(|| format!("{}", snap.tags));
let job_name = job_name.as_deref().unwrap_or("rustic_backup");
RUSTIC_APP.config().global.push_metrics(job_name, labels)
let global_config = &RUSTIC_APP.config().global;
#[cfg(feature = "prometheus")]
if let Some(prometheus_endpoint) = &global_config.prometheus {
use crate::metrics::prometheus::PrometheusExporter;
let metrics_exporter = PrometheusExporter {
endpoint: prometheus_endpoint.clone(),
job_name: job_name.to_string(),
grouping: labels.clone(),
prometheus_user: global_config.prometheus_user.clone(),
prometheus_pass: global_config.prometheus_pass.clone(),
};
metrics_exporter
.push_metrics(metrics.as_slice())
.context("pushing prometheus metrics")?;
}
#[cfg(not(feature = "prometheus"))]
if global_config.prometheus.is_some() {
bail!("prometheus metrics support is not compiled-in!");
}
#[cfg(feature = "opentelemetry")]
if let Some(otlp_endpoint) = &global_config.opentelemetry {
use crate::metrics::opentelemetry::OpentelemetryExporter;
let metrics_exporter = OpentelemetryExporter {
endpoint: otlp_endpoint.clone(),
service_name: job_name.to_string(),
labels: global_config.metrics_labels.clone(),
};
metrics_exporter
.push_metrics(metrics.as_slice())
.context("pushing opentelemetry metrics")?;
}
#[cfg(not(feature = "opentelemetry"))]
if global_config.opentelemetry.is_some() {
bail!("opentelemetry metrics support is not compiled-in!");
}
Ok(())
}

View File

@ -14,12 +14,12 @@ use std::{
};
use abscissa_core::{FrameworkError, config::Config, path::AbsPathBuf};
use anyhow::{Result, anyhow, bail};
use anyhow::{Result, anyhow};
use clap::{Parser, ValueHint};
use conflate::Merge;
use directories::ProjectDirs;
use itertools::Itertools;
use log::{Level, debug};
use log::Level;
use reqwest::Url;
use serde::{Deserialize, Serialize};
use serde_with::{DisplayFromStr, serde_as};
@ -200,22 +200,28 @@ pub struct GlobalOptions {
#[serde_as(as = "Option<DisplayFromStr>")]
#[clap(long, global = true, env = "RUSTIC_PROMETHEUS", value_name = "PUSHGATEWAY_URL", value_hint = ValueHint::Url)]
#[merge(strategy=conflate::option::overwrite_none)]
prometheus: Option<Url>,
pub prometheus: Option<Url>,
/// Authenticate to Prometheus Pushgateway using this user
#[clap(long, value_name = "USER", env = "RUSTIC_PROMETHEUS_USER")]
#[merge(strategy=conflate::option::overwrite_none)]
prometheus_user: Option<String>,
pub prometheus_user: Option<String>,
/// Authenticate to Prometheus Pushgateway using this password
#[clap(long, value_name = "PASSWORD", env = "RUSTIC_PROMETHEUS_PASS")]
#[merge(strategy=conflate::option::overwrite_none)]
prometheus_pass: Option<String>,
pub prometheus_pass: Option<String>,
/// Additional labels to set to generated Prometheus metrics
/// Additional labels to set to generated metrics
#[clap(skip)]
#[merge(strategy=conflate::btreemap::append_or_ignore)]
pub prometheus_labels: BTreeMap<String, String>,
pub metrics_labels: BTreeMap<String, String>,
/// OpenTelemetry metrics endpoint (HTTP Protobuf)
#[serde_as(as = "Option<DisplayFromStr>")]
#[clap(long, global = true, env = "RUSTIC_OTEL", value_name = "ENDPOINT_URL", value_hint = ValueHint::Url)]
#[merge(strategy=conflate::option::overwrite_none)]
pub opentelemetry: Option<Url>,
}
pub fn parse_labels(s: &str) -> Result<BTreeMap<String, String>> {
@ -233,88 +239,10 @@ pub fn parse_labels(s: &str) -> Result<BTreeMap<String, String>> {
.try_collect()
}
#[cfg(feature = "prometheus")]
impl GlobalOptions {
pub fn is_prometheus_configured(&self) -> bool {
self.prometheus.is_some()
pub fn is_metrics_configured(&self) -> bool {
self.prometheus.is_some() || self.opentelemetry.is_some()
}
pub fn push_metrics(&self, job_name: &str, labels: BTreeMap<String, String>) -> Result<()> {
use prometheus::{Encoder, ProtobufEncoder};
use reqwest::{StatusCode, blocking::Client, header::CONTENT_TYPE};
// only move on if a prometheus push url is given
let Some(url) = &self.prometheus else {
return Ok(());
};
let (full_url, encoded_metrics) = make_url_and_encoded_metrics(url, job_name, labels)?;
debug!("using url: {full_url}");
let mut builder = Client::new()
.post(full_url)
.header(CONTENT_TYPE, ProtobufEncoder::new().format_type())
.body(encoded_metrics);
if let Some(username) = &self.prometheus_user {
debug!(
"using auth {} {}",
username,
self.prometheus_pass.as_deref().unwrap_or("[NOT SET]")
);
builder = builder.basic_auth(username, self.prometheus_pass.as_ref());
}
let response = builder.send()?;
match response.status() {
StatusCode::ACCEPTED | StatusCode::OK => Ok(()),
_ => bail!(
"unexpected status code {} while pushing to {}",
response.status(),
url
),
}
}
}
#[cfg(feature = "prometheus")]
// TODO: This should be actually part of the prometheus crate, see https://github.com/tikv/rust-prometheus/issues/536
fn make_url_and_encoded_metrics(
url: &Url,
job: &str,
grouping: BTreeMap<String, String>,
) -> Result<(Url, Vec<u8>)> {
use base64::prelude::*;
use prometheus::{Encoder, ProtobufEncoder};
let mut url_components = vec![
"metrics".to_string(),
"job@base64".to_string(),
BASE64_URL_SAFE_NO_PAD.encode(job),
];
for (ln, lv) in &grouping {
// See https://github.com/tikv/rust-prometheus/issues/535
if !lv.is_empty() {
// TODO: check label name
let name = ln.to_string() + "@base64";
url_components.push(name);
url_components.push(BASE64_URL_SAFE_NO_PAD.encode(lv));
}
}
let url = url.join(&url_components.join("/"))?;
let encoder = ProtobufEncoder::new();
let mut buf = Vec::new();
for mf in prometheus::gather() {
// Note: We don't check here for pre-existing grouping labels, as we don't set them
// Ignore error, `no metrics` and `no name`.
let _ = encoder.encode(&[mf], &mut buf);
}
Ok((url, buf))
}
/// Get the paths to the config file
@ -382,7 +310,6 @@ fn get_global_config_path() -> Option<PathBuf> {
#[cfg(test)]
mod tests {
use super::*;
use anyhow::Result;
use insta::{assert_debug_snapshot, assert_snapshot};
#[test]
@ -422,27 +349,4 @@ mod tests {
// Check Debug
assert_debug_snapshot!(deserialized);
}
#[cfg(feature = "prometheus")]
#[test]
fn test_make_url_and_encoded_metrics() -> Result<()> {
use std::str::FromStr;
let grouping = [
("abc", "xyz"),
("path", "/my/path"),
("tags", "a,b,cde"),
("nogroup", ""),
]
.into_iter()
.map(|(a, b)| (a.to_string(), b.to_string()))
.collect();
let (url, _) =
make_url_and_encoded_metrics(&Url::from_str("http://host")?, "test_job", grouping)?;
assert_eq!(
url.to_string(),
"http://host/metrics/job@base64/dGVzdF9qb2I/abc@base64/eHl6/path@base64/L215L3BhdGg/tags@base64/YSxiLGNkZQ"
);
Ok(())
}
}

View File

@ -65,6 +65,8 @@ pub(crate) mod config;
pub(crate) mod error;
pub(crate) mod filtering;
pub(crate) mod helpers;
#[cfg(any(feature = "prometheus", feature = "opentelemetry"))]
pub(crate) mod metrics;
pub(crate) mod repository;
// rustic_cli Public API

22
src/metrics.rs Normal file
View File

@ -0,0 +1,22 @@
use anyhow::Result;
pub enum MetricValue {
Int(u64),
Float(f64),
}
pub struct Metric {
pub name: &'static str,
pub description: &'static str,
pub value: MetricValue,
}
pub trait MetricsExporter {
fn push_metrics(&self, metrics: &[Metric]) -> Result<()>;
}
#[cfg(feature = "prometheus")]
pub mod prometheus;
#[cfg(feature = "opentelemetry")]
pub mod opentelemetry;

View File

@ -0,0 +1,75 @@
use std::{collections::BTreeMap, time::Duration};
use opentelemetry_otlp::{MetricExporter, Protocol, WithExportConfig};
use opentelemetry_sdk::{
Resource,
metrics::{PeriodicReader, SdkMeterProvider},
};
use anyhow::Result;
use opentelemetry::{KeyValue, metrics::MeterProvider};
use reqwest::Url;
use super::{Metric, MetricValue, MetricsExporter};
pub struct OpentelemetryExporter {
pub endpoint: Url,
pub service_name: String,
pub labels: BTreeMap<String, String>,
}
impl MetricsExporter for OpentelemetryExporter {
fn push_metrics(&self, metrics: &[Metric]) -> Result<()> {
let exporter = MetricExporter::builder()
.with_http()
.with_protocol(Protocol::HttpBinary)
.with_endpoint(self.endpoint.to_string())
.build()?;
// ManualReader is not stable yet, so we use PeriodicReader
let reader = PeriodicReader::builder(exporter)
.with_interval(Duration::from_secs(u64::MAX))
.build();
let attributes = self
.labels
.iter()
.map(|(k, v)| KeyValue::new(k.clone(), v.clone()));
let resource = Resource::builder()
.with_service_name(self.service_name.clone())
.with_attributes(attributes)
.build();
let meter_provider = SdkMeterProvider::builder()
.with_reader(reader)
.with_resource(resource)
.build();
let meter = meter_provider.meter("rustic");
for metric in metrics {
match metric.value {
MetricValue::Int(value) => {
let gauge = &meter
.u64_gauge(metric.name)
.with_description(metric.description)
.build();
gauge.record(value, &[]);
}
MetricValue::Float(value) => {
let gauge = &meter
.f64_gauge(metric.name)
.with_description(metric.description)
.build();
gauge.record(value, &[]);
}
};
}
meter_provider.shutdown()?;
Ok(())
}
}

130
src/metrics/prometheus.rs Normal file
View File

@ -0,0 +1,130 @@
use anyhow::{Context, Result, bail};
use log::debug;
use prometheus::register_gauge;
use reqwest::Url;
use std::collections::BTreeMap;
use crate::metrics::MetricValue::*;
use super::{Metric, MetricsExporter};
pub struct PrometheusExporter {
pub endpoint: Url,
pub job_name: String,
pub grouping: BTreeMap<String, String>,
pub prometheus_user: Option<String>,
pub prometheus_pass: Option<String>,
}
impl MetricsExporter for PrometheusExporter {
fn push_metrics(&self, metrics: &[Metric]) -> Result<()> {
use prometheus::{Encoder, ProtobufEncoder};
use reqwest::{StatusCode, blocking::Client, header::CONTENT_TYPE};
for metric in metrics {
let gauge = register_gauge!(metric.name, metric.description,)
.context("registering prometheus gauge")?;
gauge.set(match metric.value {
Int(i) => i as f64,
Float(f) => f,
});
}
let (full_url, encoded_metrics) = self.make_url_and_encoded_metrics()?;
debug!("using url: {full_url}");
let mut builder = Client::new()
.post(full_url)
.header(CONTENT_TYPE, ProtobufEncoder::new().format_type())
.body(encoded_metrics);
if let Some(username) = &self.prometheus_user {
debug!(
"using auth {} {}",
username,
self.prometheus_pass.as_deref().unwrap_or("[NOT SET]")
);
builder = builder.basic_auth(username, self.prometheus_pass.as_ref());
}
let response = builder.send()?;
match response.status() {
StatusCode::ACCEPTED | StatusCode::OK => Ok(()),
_ => bail!(
"unexpected status code {} while pushing to {}",
response.status(),
self.endpoint
),
}
}
}
impl PrometheusExporter {
// TODO: This should be actually part of the prometheus crate, see https://github.com/tikv/rust-prometheus/issues/536
fn make_url_and_encoded_metrics(&self) -> Result<(Url, Vec<u8>)> {
use base64::prelude::*;
use prometheus::{Encoder, ProtobufEncoder};
let mut url_components = vec![
"metrics".to_string(),
"job@base64".to_string(),
BASE64_URL_SAFE_NO_PAD.encode(&self.job_name),
];
for (ln, lv) in &self.grouping {
// See https://github.com/tikv/rust-prometheus/issues/535
if !lv.is_empty() {
// TODO: check label name
let name = ln.to_string() + "@base64";
url_components.push(name);
url_components.push(BASE64_URL_SAFE_NO_PAD.encode(lv));
}
}
let url = self.endpoint.join(&url_components.join("/"))?;
let encoder = ProtobufEncoder::new();
let mut buf = Vec::new();
for mf in prometheus::gather() {
// Note: We don't check here for pre-existing grouping labels, as we don't set them
// Ignore error, `no metrics` and `no name`.
let _ = encoder.encode(&[mf], &mut buf);
}
Ok((url, buf))
}
}
#[cfg(feature = "prometheus")]
#[test]
fn test_make_url_and_encoded_metrics() -> Result<()> {
use std::str::FromStr;
let grouping = [
("abc", "xyz"),
("path", "/my/path"),
("tags", "a,b,cde"),
("nogroup", ""),
]
.into_iter()
.map(|(a, b)| (a.to_string(), b.to_string()))
.collect();
let exporter = PrometheusExporter {
endpoint: Url::from_str("http://host")?,
job_name: "test_job".to_string(),
grouping,
prometheus_user: None,
prometheus_pass: None,
};
let (url, _) = exporter.make_url_and_encoded_metrics()?;
assert_eq!(
url.to_string(),
"http://host/metrics/job@base64/dGVzdF9qb2I/abc@base64/eHl6/path@base64/L215L3BhdGg/tags@base64/YSxiLGNkZQ"
);
Ok(())
}

View File

@ -16,7 +16,7 @@ run-finally = []
[global.env]
[global.prometheus-labels]
[global.metrics-labels]
[repository]
no-cache = false
@ -75,7 +75,7 @@ run-after = []
run-failed = []
run-finally = []
[backup.prometheus-labels]
[backup.metrics-labels]
[copy]
targets = []

View File

@ -24,7 +24,8 @@ RusticConfig {
prometheus: None,
prometheus_user: None,
prometheus_pass: None,
prometheus_labels: {},
metrics_labels: {},
opentelemetry: None,
},
repository: AllRepositoryOptions {
be: BackendOptions {
@ -140,8 +141,8 @@ RusticConfig {
},
snapshots: [],
sources: [],
prometheus_job: None,
prometheus_labels: {},
metrics_job: None,
metrics_labels: {},
},
copy: CopyCmd {
ids: [],

View File

@ -26,7 +26,7 @@ KEY7 = "VALUE7"
KEY8 = "VALUE8"
KEY9 = "VALUE9"
[global.prometheus-labels]
[global.metrics-labels]
[repository]
no-cache = false
@ -85,7 +85,7 @@ run-after = []
run-failed = []
run-finally = []
[backup.prometheus-labels]
[backup.metrics-labels]
[copy]
targets = []

View File

@ -35,7 +35,8 @@ RusticConfig {
prometheus: None,
prometheus_user: None,
prometheus_pass: None,
prometheus_labels: {},
metrics_labels: {},
opentelemetry: None,
},
repository: AllRepositoryOptions {
be: BackendOptions {
@ -151,8 +152,8 @@ RusticConfig {
},
snapshots: [],
sources: [],
prometheus_job: None,
prometheus_labels: {},
metrics_job: None,
metrics_labels: {},
},
copy: CopyCmd {
ids: [],

View File

@ -26,7 +26,7 @@ KEY7 = "VALUE7"
KEY8 = "VALUE8"
KEY9 = "VALUE9"
[global.prometheus-labels]
[global.metrics-labels]
[repository]
no-cache = false
@ -85,7 +85,7 @@ run-after = []
run-failed = []
run-finally = []
[backup.prometheus-labels]
[backup.metrics-labels]
[copy]
targets = []

View File

@ -16,7 +16,7 @@ run-finally = []
[global.env]
[global.prometheus-labels]
[global.metrics-labels]
[repository]
no-cache = false
@ -75,7 +75,7 @@ run-after = []
run-failed = []
run-finally = []
[backup.prometheus-labels]
[backup.metrics-labels]
[copy]
targets = []