difftreelog
feat extract otel autoconfig from pusher
in: trunk
6 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -673,6 +673,15 @@
]
[[package]]
+name = "crc32fast"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
name = "crossterm"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1000,6 +1009,16 @@
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
+name = "flate2"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
name = "fleet"
version = "0.2.0"
dependencies = [
@@ -1025,6 +1044,8 @@
"nixlike",
"nom 8.0.0",
"openssh",
+ "opentelemetry",
+ "opentelemetry_sdk",
"owo-colors",
"peg",
"regex",
@@ -1038,6 +1059,7 @@
"tokio-util",
"tracing",
"tracing-indicatif",
+ "tracing-opentelemetry",
"tracing-subscriber",
]
@@ -1168,6 +1190,15 @@
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
+name = "form_urlencoded"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
name = "futures"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1492,6 +1523,7 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
dependencies = [
+ "base64 0.22.1",
"bytes",
"futures-channel",
"futures-core",
@@ -1499,7 +1531,9 @@
"http",
"http-body",
"hyper",
+ "ipnet",
"libc",
+ "percent-encoding",
"pin-project-lite",
"socket2 0.6.0",
"tokio",
@@ -1598,6 +1632,113 @@
]
[[package]]
+name = "icu_collections"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
+
+[[package]]
+name = "icu_properties"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "potential_utf",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
+
+[[package]]
+name = "icu_provider"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "stable_deref_trait",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "idna"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1684,6 +1825,22 @@
checksum = "4b3f7cef34251886990511df1c61443aa928499d598a9473929ab5a90a527304"
[[package]]
+name = "ipnet"
+version = "2.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
+
+[[package]]
+name = "iri-string"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
name = "is-terminal"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1799,6 +1956,12 @@
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
+name = "litemap"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
+
+[[package]]
name = "litrs"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2050,6 +2213,93 @@
]
[[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.16",
+ "tracing",
+]
+
+[[package]]
+name = "opentelemetry-exporter-env"
+version = "0.1.0"
+dependencies = [
+ "clap",
+ "opentelemetry-otlp",
+ "thiserror 2.0.16",
+]
+
+[[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",
+ "opentelemetry",
+ "reqwest",
+]
+
+[[package]]
+name = "opentelemetry-otlp"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbee664a43e07615731afc539ca60c6d9f1a9425e25ca09c57bc36c87c55852b"
+dependencies = [
+ "http",
+ "opentelemetry",
+ "opentelemetry-http",
+ "opentelemetry-proto",
+ "opentelemetry_sdk",
+ "prost",
+ "reqwest",
+ "serde_json",
+ "thiserror 2.0.16",
+ "tokio",
+ "tonic 0.13.1",
+ "tracing",
+]
+
+[[package]]
+name = "opentelemetry-proto"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e046fd7660710fe5a05e8748e70d9058dc15c94ba914e7c4faa7c728f0e8ddc"
+dependencies = [
+ "base64 0.22.1",
+ "hex",
+ "opentelemetry",
+ "opentelemetry_sdk",
+ "prost",
+ "serde",
+ "tonic 0.13.1",
+]
+
+[[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.2",
+ "serde_json",
+ "thiserror 2.0.16",
+]
+
+[[package]]
name = "owo-colors"
version = "4.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2251,6 +2501,15 @@
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
[[package]]
+name = "potential_utf"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2484,6 +2743,40 @@
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
[[package]]
+name = "reqwest"
+version = "0.12.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb"
+dependencies = [
+ "base64 0.22.1",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "js-sys",
+ "log",
+ "percent-encoding",
+ "pin-project-lite",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tower 0.5.2",
+ "tower-http 0.6.6",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2817,6 +3110,18 @@
]
[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2937,6 +3242,12 @@
]
[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2983,8 +3294,22 @@
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+dependencies = [
+ "futures-core",
+]
[[package]]
+name = "synstructure"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "tabled"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3103,9 +3428,9 @@
"tokio",
"tokio-stream",
"tokio-util",
- "tonic",
+ "tonic 0.12.3",
"tonic-build",
- "tower-http",
+ "tower-http 0.5.2",
"tracing",
"tracing-subscriber",
]
@@ -3306,6 +3631,33 @@
]
[[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",
+ "flate2",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-timeout",
+ "hyper-util",
+ "percent-encoding",
+ "pin-project",
+ "prost",
+ "tokio",
+ "tokio-stream",
+ "tower 0.5.2",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
name = "tonic-build"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3347,10 +3699,15 @@
dependencies = [
"futures-core",
"futures-util",
+ "indexmap 2.11.0",
"pin-project-lite",
+ "slab",
"sync_wrapper",
+ "tokio",
+ "tokio-util",
"tower-layer",
"tower-service",
+ "tracing",
]
[[package]]
@@ -3371,6 +3728,24 @@
]
[[package]]
+name = "tower-http"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
+dependencies = [
+ "bitflags",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "iri-string",
+ "pin-project-lite",
+ "tower 0.5.2",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
name = "tower-layer"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3438,6 +3813,24 @@
]
[[package]]
+name = "tracing-opentelemetry"
+version = "0.31.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddcf5959f39507d0d04d6413119c04f33b623f4f951ebcbdddddfad2d0623a9c"
+dependencies = [
+ "js-sys",
+ "once_cell",
+ "opentelemetry",
+ "opentelemetry_sdk",
+ "smallvec",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+ "tracing-subscriber",
+ "web-time",
+]
+
+[[package]]
name = "tracing-serde"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3567,6 +3960,24 @@
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
+name = "url"
+version = "2.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3691,6 +4102,19 @@
]
[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "once_cell",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
name = "wasm-bindgen-macro"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3723,6 +4147,16 @@
]
[[package]]
+name = "web-sys"
+version = "0.3.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
name = "web-time"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3997,6 +4431,12 @@
checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814"
[[package]]
+name = "writeable"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
+
+[[package]]
name = "wsl"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4024,6 +4464,30 @@
]
[[package]]
+name = "yoke"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
name = "z85"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4054,8 +4518,23 @@
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
+dependencies = [
+ "zerofrom-derive",
+]
[[package]]
+name = "zerofrom-derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4076,10 +4555,34 @@
]
[[package]]
+name = "zerotrie"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
name = "zerovec"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
dependencies = [
+ "yoke",
"zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
]
cmds/fleet/Cargo.tomldiffbeforeafterboth--- a/cmds/fleet/Cargo.toml
+++ b/cmds/fleet/Cargo.toml
@@ -46,6 +46,9 @@
indicatif = { version = "0.18", optional = true }
nom = "8.0.0"
tracing-indicatif = { version = "0.3", optional = true }
+tracing-opentelemetry = "0.31.0"
+opentelemetry = "0.30.0"
+opentelemetry_sdk = "0.30.0"
[features]
default = []
cmds/fleet/src/main.rsdiffbeforeafterboth1#![recursion_limit = "512"]23pub(crate) mod cmds;4// pub(crate) mod command;5pub(crate) mod extra_args;67use std::{ffi::OsString, process::ExitCode};89use anyhow::{Result, bail};10use clap::{CommandFactory, Parser};11use cmds::{12 build_systems::{BuildSystems, Deploy},13 complete::Complete,14 info::Info,15 rollback::RollbackSingle,16 secrets::Secret,17 tf::Tf,18};19use fleet_base::{host::Config, opts::FleetOpts};20use futures::{TryStreamExt, future::LocalBoxFuture, stream::FuturesUnordered};21// use host::Config;22#[cfg(feature = "indicatif")]23use human_repr::HumanCount;24#[cfg(feature = "indicatif")]25use indicatif::{ProgressState, ProgressStyle};26use nix_eval::{gc_register_my_thread, gc_unregister_my_thread, init_libraries};27use tracing::{Instrument, error, info, info_span};28#[cfg(feature = "indicatif")]29use tracing_indicatif::IndicatifLayer;30use tracing_subscriber::{fmt::format::Format, prelude::*, EnvFilter};3132#[derive(Parser)]33struct Prefetch {}34impl Prefetch {35 async fn run(&self, config: &Config) -> Result<()> {36 let mut prefetch_dir = config.directory.to_path_buf();37 prefetch_dir.push("prefetch");38 if !prefetch_dir.is_dir() {39 info!("nothing to prefetch: no prefetch directory");40 return Ok(());41 }42 let tasks = <FuturesUnordered<LocalBoxFuture<Result<()>>>>::new();43 for entry in std::fs::read_dir(&prefetch_dir)? {44 tasks.push(Box::pin(async {45 let entry = entry?;46 if !entry.metadata()?.is_file() {47 bail!("only files should exist in prefetch directory");48 }49 let span = info_span!(50 "prefetching",51 name = entry.file_name().to_string_lossy().as_ref()52 );53 let mut path = OsString::new();54 path.push("file://");55 path.push(entry.path());5657 let mut status = config.local_host().cmd("nix").await?;58 status.args(&config.nix_args);59 status.arg("store").arg("prefetch-file").arg(path);60 status.run_nix_string().instrument(span).await?;61 Ok(())62 }));63 }64 tasks.try_collect::<Vec<()>>().await?;65 Ok(())66 }67}6869#[derive(Parser)]70enum Opts {71 /// Build system closures72 BuildSystems(BuildSystems),73 /// Upload and switch system closures74 Deploy(Deploy),75 /// Rollback remote machine by redeploying old generation as the new one76 RollbackSingle(RollbackSingle),77 /// Secret management78 #[clap(subcommand)]79 Secret(Secret),80 /// Upload prefetch directory to the nix store81 Prefetch(Prefetch),82 /// Config parsing83 Info(Info),84 /// Command completions85 #[clap(hide(true))]86 Complete(Complete),87 /// Compile and evaluate terranix configuration88 Tf(Tf),89}9091#[derive(Parser)]92#[clap(version, author)]93struct RootOpts {94 #[clap(flatten)]95 fleet_opts: FleetOpts,96 #[clap(subcommand)]97 command: Opts,98}99100async fn run_command(config: &Config, opts: FleetOpts, command: Opts) -> Result<()> {101 match command {102 Opts::BuildSystems(c) => c.run(config, &opts).await?,103 Opts::Deploy(d) => d.run(config, &opts).await?,104 Opts::RollbackSingle(r) => r.run(config, &opts).await?,105 Opts::Secret(s) => s.run(config, &opts).await?,106 Opts::Info(i) => i.run(config).await?,107 Opts::Prefetch(p) => p.run(config).await?,108 Opts::Tf(t) => t.run(config).await?,109 // TODO: actually parse commands before starting the async runtime110 Opts::Complete(c) => {111 tokio::task::spawn_blocking(move || c.run(RootOpts::command())).await?112 }113 };114 Ok(())115}116117fn setup_logging() {118 #[cfg(feature = "indicatif")]119 let indicatif_layer = {120 use std::time::Duration;121122 IndicatifLayer::new().with_progress_style(123 ProgressStyle::with_template(124 "{color_start}{span_child_prefix} {span_name}{{{span_fields}}}{color_end} {wide_msg} {color_start}{download_progress} {elapsed}{color_end}",125 )126 .unwrap()127 .with_key("download_progress", |state: &ProgressState, writer: &mut dyn std::fmt::Write| {128 let Some(len) = state.len() else {129 return;130 };131 let pos = state.pos();132 if pos > len {133 let _ = write!(writer, "{}", pos.human_count_bare());134 } else {135 let _ = write!(writer, "{} / {}", pos.human_count_bare(), len.human_count_bare());136 }137 })138 .with_key(139 "color_start",140 |state: &ProgressState, writer: &mut dyn std::fmt::Write| {141 let elapsed = state.elapsed();142143 if elapsed > Duration::from_secs(60) {144 // Red145 let _ = write!(writer, "\x1b[{}m", 1 + 30);146 } else if elapsed > Duration::from_secs(30) {147 // Yellow148 let _ = write!(writer, "\x1b[{}m", 3 + 30);149 }150 },151 )152 .with_key(153 "color_end",154 |state: &ProgressState, writer: &mut dyn std::fmt::Write| {155 if state.elapsed() > Duration::from_secs(30) {156 let _ = write!(writer, "\x1b[0m");157 }158 },159 ),160 )161 };162163 let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));164165 let reg = tracing_subscriber::registry().with({166 let sub = tracing_subscriber::fmt::layer()167 .without_time()168 .with_target(false);169 #[cfg(feature = "indicatif")]170 let sub = sub.with_writer(indicatif_layer.get_stderr_writer());171 sub.with_filter(filter) // .without,172 });173 // #[cfg(feature = "indicatif")]174 #[cfg(feature = "indicatif")]175 let reg = reg.with(indicatif_layer);176 reg.init();177}178179fn main() -> ExitCode {180 let opts = RootOpts::parse();181 if let Opts::Complete(c) = &opts.command {182 c.run(RootOpts::command());183 return ExitCode::SUCCESS;184 }185186 setup_logging();187188 init_libraries();189190 tokio::runtime::Builder::new_multi_thread()191 .enable_all()192 .on_thread_start(|| {193 gc_register_my_thread();194 })195 .on_thread_stop(|| {196 gc_unregister_my_thread();197 })198 .build()199 .expect("failed to build runtime")200 .block_on(async {201 if let Err(e) = main_real(opts).await {202 error!("{e:#}");203 ExitCode::FAILURE204 } else {205 ExitCode::SUCCESS206 }207 })208 // async_main(opts)209}210211async fn main_real(opts: RootOpts) -> Result<()> {212 let nix_args = std::env::var_os("NIX_ARGS")213 .map(|a| extra_args::parse_os(&a))214 .transpose()?215 .unwrap_or_default();216 let config = opts217 .fleet_opts218 .build(219 nix_args,220 matches!(opts.command, Opts::Deploy(_) | Opts::BuildSystems(_)),221 )222 .await?;223224 match run_command(&config, opts.fleet_opts, opts.command).await {225 Ok(()) => {226 config.save()?;227 Ok(())228 }229 Err(e) => {230 let _ = config.save();231 Err(e)232 }233 }234}235236#[cfg(test)]237mod tests {238 use super::*;239240 #[test]241 fn verify_command() {242 use clap::CommandFactory;243 RootOpts::command().debug_assert();244 }245}1#![recursion_limit = "512"]23pub(crate) mod cmds;4// pub(crate) mod command;5pub(crate) mod extra_args;67use std::{env, ffi::OsString, process::ExitCode};89use anyhow::{Result, bail};10use clap::{CommandFactory, Parser};11use cmds::{12 build_systems::{BuildSystems, Deploy},13 complete::Complete,14 info::Info,15 rollback::RollbackSingle,16 secrets::Secret,17 tf::Tf,18};19use fleet_base::{host::Config, opts::FleetOpts};20use futures::{TryStreamExt, future::LocalBoxFuture, stream::FuturesUnordered};21// use host::Config;22#[cfg(feature = "indicatif")]23use human_repr::HumanCount;24#[cfg(feature = "indicatif")]25use indicatif::{ProgressState, ProgressStyle};26use nix_eval::{gc_register_my_thread, gc_unregister_my_thread, init_libraries};27use tracing::{Instrument, error, info, info_span};28#[cfg(feature = "indicatif")]29use tracing_indicatif::IndicatifLayer;30use tracing_subscriber::{EnvFilter, fmt::format::Format, prelude::*};3132#[derive(Parser)]33struct Prefetch {}34impl Prefetch {35 async fn run(&self, config: &Config) -> Result<()> {36 let mut prefetch_dir = config.directory.to_path_buf();37 prefetch_dir.push("prefetch");38 if !prefetch_dir.is_dir() {39 info!("nothing to prefetch: no prefetch directory");40 return Ok(());41 }42 let tasks = <FuturesUnordered<LocalBoxFuture<Result<()>>>>::new();43 for entry in std::fs::read_dir(&prefetch_dir)? {44 tasks.push(Box::pin(async {45 let entry = entry?;46 if !entry.metadata()?.is_file() {47 bail!("only files should exist in prefetch directory");48 }49 let span = info_span!(50 "prefetching",51 name = entry.file_name().to_string_lossy().as_ref()52 );53 let mut path = OsString::new();54 path.push("file://");55 path.push(entry.path());5657 let mut status = config.local_host().cmd("nix").await?;58 status.args(&config.nix_args);59 status.arg("store").arg("prefetch-file").arg(path);60 status.run_nix_string().instrument(span).await?;61 Ok(())62 }));63 }64 tasks.try_collect::<Vec<()>>().await?;65 Ok(())66 }67}6869#[derive(Parser)]70enum Opts {71 /// Build system closures72 BuildSystems(BuildSystems),73 /// Upload and switch system closures74 Deploy(Deploy),75 /// Rollback remote machine by redeploying old generation as the new one76 RollbackSingle(RollbackSingle),77 /// Secret management78 #[clap(subcommand)]79 Secret(Secret),80 /// Upload prefetch directory to the nix store81 Prefetch(Prefetch),82 /// Config parsing83 Info(Info),84 /// Command completions85 #[clap(hide(true))]86 Complete(Complete),87 /// Compile and evaluate terranix configuration88 Tf(Tf),89}9091#[derive(Parser)]92#[clap(version, author)]93struct RootOpts {94 #[clap(flatten)]95 fleet_opts: FleetOpts,96 #[clap(subcommand)]97 command: Opts,98}99100async fn run_command(config: &Config, opts: FleetOpts, command: Opts) -> Result<()> {101 match command {102 Opts::BuildSystems(c) => c.run(config, &opts).await?,103 Opts::Deploy(d) => d.run(config, &opts).await?,104 Opts::RollbackSingle(r) => r.run(config, &opts).await?,105 Opts::Secret(s) => s.run(config, &opts).await?,106 Opts::Info(i) => i.run(config).await?,107 Opts::Prefetch(p) => p.run(config).await?,108 Opts::Tf(t) => t.run(config).await?,109 // TODO: actually parse commands before starting the async runtime110 Opts::Complete(c) => {111 tokio::task::spawn_blocking(move || c.run(RootOpts::command())).await?112 }113 };114 Ok(())115}116117fn setup_logging() {118 #[cfg(feature = "indicatif")]119 let indicatif_layer = {120 use std::time::Duration;121122 IndicatifLayer::new().with_progress_style(123 ProgressStyle::with_template(124 "{color_start}{span_child_prefix} {span_name}{{{span_fields}}}{color_end} {wide_msg} {color_start}{download_progress} {elapsed}{color_end}",125 )126 .unwrap()127 .with_key("download_progress", |state: &ProgressState, writer: &mut dyn std::fmt::Write| {128 let Some(len) = state.len() else {129 return;130 };131 let pos = state.pos();132 if pos > len {133 let _ = write!(writer, "{}", pos.human_count_bare());134 } else {135 let _ = write!(writer, "{} / {}", pos.human_count_bare(), len.human_count_bare());136 }137 })138 .with_key(139 "color_start",140 |state: &ProgressState, writer: &mut dyn std::fmt::Write| {141 let elapsed = state.elapsed();142143 if elapsed > Duration::from_secs(60) {144 // Red145 let _ = write!(writer, "\x1b[{}m", 1 + 30);146 } else if elapsed > Duration::from_secs(30) {147 // Yellow148 let _ = write!(writer, "\x1b[{}m", 3 + 30);149 }150 },151 )152 .with_key(153 "color_end",154 |state: &ProgressState, writer: &mut dyn std::fmt::Write| {155 if state.elapsed() > Duration::from_secs(30) {156 let _ = write!(writer, "\x1b[0m");157 }158 },159 ),160 )161 };162163 let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));164165 let reg = tracing_subscriber::registry().with({166 let sub = tracing_subscriber::fmt::layer()167 .without_time()168 .with_target(false);169 #[cfg(feature = "indicatif")]170 let sub = sub.with_writer(indicatif_layer.get_stderr_writer());171 sub.with_filter(filter) // .without,172 });173174 if env::var_os("FLEET_OTEL").is_some() {}175176 // #[cfg(feature = "indicatif")]177 #[cfg(feature = "indicatif")]178 let reg = reg.with(indicatif_layer);179 reg.init();180}181182fn main() -> ExitCode {183 let opts = RootOpts::parse();184 if let Opts::Complete(c) = &opts.command {185 c.run(RootOpts::command());186 return ExitCode::SUCCESS;187 }188189 setup_logging();190191 init_libraries();192193 tokio::runtime::Builder::new_multi_thread()194 .enable_all()195 .on_thread_start(|| {196 gc_register_my_thread();197 })198 .on_thread_stop(|| {199 gc_unregister_my_thread();200 })201 .build()202 .expect("failed to build runtime")203 .block_on(async {204 if let Err(e) = main_real(opts).await {205 error!("{e:#}");206 ExitCode::FAILURE207 } else {208 ExitCode::SUCCESS209 }210 })211 // async_main(opts)212}213214async fn main_real(opts: RootOpts) -> Result<()> {215 let nix_args = std::env::var_os("NIX_ARGS")216 .map(|a| extra_args::parse_os(&a))217 .transpose()?218 .unwrap_or_default();219 let config = opts220 .fleet_opts221 .build(222 nix_args,223 matches!(opts.command, Opts::Deploy(_) | Opts::BuildSystems(_)),224 )225 .await?;226227 match run_command(&config, opts.fleet_opts, opts.command).await {228 Ok(()) => {229 config.save()?;230 Ok(())231 }232 Err(e) => {233 let _ = config.save();234 Err(e)235 }236 }237}238239#[cfg(test)]240mod tests {241 use super::*;242243 #[test]244 fn verify_command() {245 use clap::CommandFactory;246 RootOpts::command().debug_assert();247 }248}crates/opentelemetry-exporter-env/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/crates/opentelemetry-exporter-env/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "opentelemetry-exporter-env"
+version.workspace = true
+edition.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+clap = { workspace = true, optional = true }
+opentelemetry-otlp = { version = "0.30.0", features = ["grpc-tonic", "gzip-tonic", "http-json"], optional = true }
+thiserror.workspace = true
+
+[features]
+default = ["clap", "otlp"]
+clap = ["dep:clap"]
+otlp = ["dep:opentelemetry-otlp"]
crates/opentelemetry-exporter-env/src/lib.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/opentelemetry-exporter-env/src/lib.rs
@@ -0,0 +1,264 @@
+use std::collections::HashMap;
+use std::convert::Infallible;
+use std::env::{self, VarError};
+use std::ffi::OsString;
+use std::num::ParseIntError;
+use std::str::FromStr;
+use std::time::Duration;
+
+use clap::Parser;
+#[cfg(feature = "otlp")]
+use opentelemetry_otlp::tonic_types::metadata::MetadataMap;
+#[cfg(feature = "otlp")]
+use opentelemetry_otlp::{
+ ExporterBuildError, LogExporter, MetricExporter, SpanExporter, WithExportConfig,
+ WithHttpConfig, WithTonicConfig,
+};
+
+#[cfg(feature = "otlp")]
+mod otlp;
+
+pub enum Error {
+ InvalidUtf8 {
+ env: &'static str,
+ value: OsString,
+ },
+ EnvParseError {
+ env: &'static str,
+ value: String,
+ error: &'static str,
+ },
+ EnvParseIntError {
+ env: &'static str,
+ value: String,
+ error: ParseIntError,
+ },
+}
+impl From<(&'static str, &'static str, String)> for Error {
+ fn from((env, error, value): (&'static str, &'static str, String)) -> Self {
+ Self::EnvParseError { env, value, error }
+ }
+}
+impl From<(&'static str, ParseIntError, String)> for Error {
+ fn from((env, error, value): (&'static str, ParseIntError, String)) -> Self {
+ Self::EnvParseIntError { env, value, error }
+ }
+}
+impl From<(&'static str, Infallible, String)> for Error {
+ fn from(_v: (&'static str, Infallible, String)) -> Self {
+ unreachable!()
+ }
+}
+
+fn load_env<T>(env: &'static str) -> Result<Option<T>, Error>
+where
+ T: FromStr,
+ Error: From<(&'static str, <T as FromStr>::Err, String)>,
+{
+ match env::var(env) {
+ Ok(v) => Ok(Some(T::from_str(&v).map_err(|err| (env, err, v))?)),
+ Err(VarError::NotPresent) => Ok(None),
+ Err(VarError::NotUnicode(value)) => Err(Error::InvalidUtf8 { env, value }),
+ }
+}
+
+macro_rules! impl_settings {
+ (
+ #[name($env_prefix:literal, $long_prefix:literal)]
+ struct $id:ident {
+ $(
+ $(#[doc = $doc:literal])*
+ #[name($env:literal, $long:literal)]
+ $(#[arg($($tt:tt)*)])?
+ $name:ident: $ty:ty,
+ )*
+ }) => {
+ #[derive(Parser)]
+ pub struct $id {
+ $(
+ $(#[doc = $doc])*
+ #[arg(long = concat!("otel-exporter-otlp-", $long_prefix, $long), env = concat!("OTEL_EXPORTER_OTLP_", $env_prefix, $env), $($($tt)*)?)]
+ pub $name: Option<$ty>,
+ )*
+ }
+ impl $id {
+ pub fn from_env() -> Result<Self, Error> {
+ Ok(Self {
+ $(
+ $name: load_env(concat!("OTEL_EXPORTER_OTLP_", $env_prefix, $env))?,
+ )*
+ })
+ }
+ }
+ }
+}
+macro_rules! impl_enum {
+ (enum $id:ident {
+ $(
+ #[name = $value:literal]
+ $var:ident,
+ )*
+ }) => {
+ #[derive(Clone, Copy)]
+ #[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
+ pub enum $id {
+ $(
+ #[cfg_attr(feature = "clap", value(name = $value))]
+ $var,
+ )*
+ }
+ impl FromStr for $id {
+ type Err = &'static str;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Ok(match s {
+ $(
+ $value => Self::$var,
+ )*
+ _ => return Err("unsupported value, supported are")
+ })
+ }
+ }
+ };
+}
+
+impl_enum! {
+ enum Compression {
+ #[name = "gzip"]
+ Gzip,
+ #[name = "zstd"]
+ Zstd,
+ }
+}
+#[cfg(feature = "otlp")]
+impl From<Compression> for opentelemetry_otlp::Compression {
+ fn from(value: Compression) -> Self {
+ match value {
+ Compression::Gzip => opentelemetry_otlp::Compression::Gzip,
+ Compression::Zstd => opentelemetry_otlp::Compression::Zstd,
+ }
+ }
+}
+
+impl_enum! {
+ enum OtlpProtocol {
+ #[name = "grpc"]
+ Grpc,
+ #[name = "http/protobuf"]
+ HttpProtobuf,
+ #[name = "http/json"]
+ HttpJson,
+ }
+}
+#[cfg(feature = "otlp")]
+impl From<OtlpProtocol> for opentelemetry_otlp::Protocol {
+ fn from(value: OtlpProtocol) -> Self {
+ match value {
+ OtlpProtocol::Grpc => opentelemetry_otlp::Protocol::Grpc,
+ OtlpProtocol::HttpProtobuf => opentelemetry_otlp::Protocol::HttpBinary,
+ OtlpProtocol::HttpJson => opentelemetry_otlp::Protocol::HttpJson,
+ }
+ }
+}
+
+impl_settings! {
+ #[name("", "")]
+ struct OtlpBaseSettings {
+ /// Specifies the OTLP transport compression to be used for all telemetry data.
+ #[name("COMPRESSION", "compression")]
+ #[arg(value_enum)]
+ compression: Compression,
+ /// A base endpoint URL for any signal type, with an optionally-specified port number. Helpful for when you’re sending more than one signal to the same endpoint and want one environment variable to control the endpoint.
+ #[name("ENDPOINT", "endpoint")]
+ endpoint: String,
+ /// A list of headers to apply to all outgoing data (traces, metrics, and logs).
+ #[name("HEADERS", "headers")]
+ headers: String,
+ /// Specifies the OTLP transport protocol to be used for all telemetry data.
+ #[name("PROTOCOL", "protocol")]
+ #[arg(value_enum)]
+ protocol: OtlpProtocol,
+ /// The timeout value for all outgoing data (traces, metrics, and logs) in milliseconds.
+ #[name("TIMEOUT", "timeout")]
+ timeout: u64,
+ }
+}
+impl_settings! {
+ #[name("LOGS_", "logs-")]
+ struct OtlpLogsSettings {
+ /// Specifies the OTLP transport compression to be used for log data.
+ #[name("COMPRESSION", "compression")]
+ #[arg(value_enum)]
+ compression: Compression,
+ /// Endpoint URL for log data only, with an optionally-specified port number. Typically ends with `v1/logs` when using OTLP/HTTP.
+ #[name("ENDPOINT", "endpoint")]
+ endpoint: String,
+ /// A list of headers to apply to all outgoing logs.
+ #[name("HEADERS", "headers")]
+ headers: String,
+ /// Specifies the OTLP transport protocol to be used for log data.
+ #[name("PROTOCOL", "protocol")]
+ #[arg(value_enum)]
+ protocol: OtlpProtocol,
+ /// The timeout value for all outgoing logs in milliseconds.
+ #[name("TIMEOUT", "timeout")]
+ timeout: u64,
+ }
+}
+impl_settings! {
+ #[name("METRICS_", "metrics-")]
+ struct OtlpMetricsSettings {
+ /// Specifies the OTLP transport compression to be used for metrics data.
+ #[name("COMPRESSION", "compression")]
+ #[arg(value_enum)]
+ compression: Compression,
+ /// Endpoint URL for metric data only, with an optionally-specified port number. Typically ends with `v1/metrics` when using OTLP/HTTP.
+ #[name("ENDPOINT", "endpoint")]
+ endpoint: String,
+ /// A list of headers to apply to all outgoing metrics.
+ #[name("HEADERS", "headers")]
+ headers: String,
+ /// Specifies the OTLP transport protocol to be used for metrics data.
+ #[name("PROTOCOL", "protocol")]
+ #[arg(value_enum)]
+ protocol: OtlpProtocol,
+ /// The timeout value for all outgoing metrics in milliseconds.
+ #[name("TIMEOUT", "timeout")]
+ timeout: u64,
+ }
+}
+
+impl_settings! {
+ #[name("TRACES_", "traces-")]
+ struct OtlpTracesSettings {
+ /// Specifies the OTLP transport compression to be used for trace data.
+ #[name("COMPRESSION", "compression")]
+ #[arg(value_enum)]
+ compression: Compression,
+ /// Endpoint URL for trace data only, with an optionally-specified port number. Typically ends with `v1/traces` when using OTLP/HTTP.
+ #[name("ENDPOINT", "endpoint")]
+ endpoint: String,
+ /// A list of headers to apply to all outgoing traces.
+ #[name("HEADERS", "headers")]
+ headers: String,
+ /// Specifies the OTLP transport protocol to be used for trace data.
+ #[name("PROTOCOL", "protocol")]
+ #[arg(value_enum)]
+ protocol: OtlpProtocol,
+ /// The timeout value for all outgoing traces in milliseconds.
+ #[name("TIMEOUT", "timeout")]
+ timeout: u64,
+ }
+}
+
+#[derive(thiserror::Error, Debug)]
+enum ProviderError {
+ #[error("protocol is not set")]
+ UnsetProtocol,
+ #[error("endpoint is not set")]
+ EndpointUnset,
+ #[cfg(feature = "otlp")]
+ #[error("failed to build exporter: {0}")]
+ Exporter(#[from] ExporterBuildError),
+}
+type ProviderResult<T, E = ProviderError> = Result<T, E>;
crates/opentelemetry-exporter-env/src/otlp.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/opentelemetry-exporter-env/src/otlp.rs
@@ -0,0 +1,175 @@
+use std::collections::HashMap;
+use std::time::Duration;
+
+use opentelemetry_otlp::tonic_types::metadata::MetadataMap;
+use opentelemetry_otlp::{
+ LogExporter, MetricExporter, SpanExporter, WithExportConfig as _, WithHttpConfig as _,
+ WithTonicConfig as _,
+};
+
+use crate::{
+ OtlpBaseSettings, OtlpLogsSettings, OtlpMetricsSettings, OtlpProtocol, ProviderError,
+ ProviderResult,
+};
+
+fn parse_headers<'a>(
+ headers: &'a str,
+) -> std::iter::Map<std::str::Split<'a, char>, impl FnMut(&'a str) -> (&'a str, &'a str)> {
+ headers.split(',').map(|header| {
+ let mut parts = header.splitn(2, '=');
+ let key = parts.next().unwrap();
+ let value = parts.next().unwrap_or("");
+ (key, value)
+ })
+}
+
+fn parse_headers_metadata_map(headers: Option<&str>) -> MetadataMap {
+ headers
+ .map(|headers| {
+ MetadataMap::from_headers(
+ parse_headers(headers)
+ .map(|(key, value)| (key.parse().unwrap(), value.parse().unwrap()))
+ .collect(),
+ )
+ })
+ .unwrap_or_default()
+}
+fn parse_headers_hashmap(headers: Option<&str>) -> HashMap<String, String> {
+ headers
+ .map(|headers| {
+ parse_headers(headers)
+ .map(|(key, value)| (key.into(), value.into()))
+ .collect()
+ })
+ .unwrap_or_default()
+}
+
+fn logger_exporter(base: &OtlpBaseSettings, log: &OtlpLogsSettings) -> ProviderResult<LogExporter> {
+ let endpoint = log
+ .endpoint
+ .clone()
+ .or_else(|| Some(format!("{}/v1/logs", base.endpoint.as_ref()?)))
+ .ok_or(ProviderError::EndpointUnset)?;
+ let headers = log.headers.as_deref().or(base.headers.as_deref());
+ let timeout = Duration::from_millis(log.timeout.or(base.timeout).unwrap_or(10000));
+
+ let protocol = log
+ .protocol
+ .or(base.protocol)
+ .ok_or(ProviderError::UnsetProtocol)?;
+
+ match protocol {
+ OtlpProtocol::Grpc => {
+ let mut builder = LogExporter::builder()
+ .with_tonic()
+ .with_endpoint(endpoint)
+ .with_metadata(parse_headers_metadata_map(headers))
+ .with_protocol(protocol.into())
+ .with_timeout(timeout);
+ let compression = log.compression.or(base.compression);
+ if let Some(compression) = compression {
+ builder = builder.with_compression(compression.into());
+ }
+
+ Ok(builder.build()?)
+ }
+ OtlpProtocol::HttpProtobuf | OtlpProtocol::HttpJson => {
+ let builder = LogExporter::builder()
+ .with_http()
+ .with_endpoint(endpoint)
+ .with_headers(parse_headers_hashmap(headers))
+ .with_protocol(protocol.into())
+ .with_timeout(timeout);
+
+ Ok(builder.build()?)
+ }
+ }
+}
+fn metric_exporter(
+ base: &OtlpBaseSettings,
+ metric: &OtlpMetricsSettings,
+) -> ProviderResult<MetricExporter> {
+ let endpoint = metric
+ .endpoint
+ .clone()
+ .or_else(|| Some(format!("{}/v1/metrics", base.endpoint.as_ref()?)))
+ .ok_or(ProviderError::EndpointUnset)?;
+ let headers = metric.headers.as_deref().or(base.headers.as_deref());
+ let timeout = Duration::from_millis(metric.timeout.or(base.timeout).unwrap_or(10000));
+
+ let protocol = metric
+ .protocol
+ .or(base.protocol)
+ .ok_or(ProviderError::UnsetProtocol)?;
+
+ match protocol {
+ OtlpProtocol::Grpc => {
+ let mut builder = MetricExporter::builder()
+ .with_tonic()
+ .with_endpoint(endpoint)
+ .with_metadata(parse_headers_metadata_map(headers))
+ .with_protocol(protocol.into())
+ .with_timeout(timeout);
+ let compression = metric.compression.or(base.compression);
+ if let Some(compression) = compression {
+ builder = builder.with_compression(compression.into());
+ }
+
+ Ok(builder.build()?)
+ }
+ OtlpProtocol::HttpProtobuf | OtlpProtocol::HttpJson => {
+ let builder = MetricExporter::builder()
+ .with_http()
+ .with_endpoint(endpoint)
+ .with_headers(parse_headers_hashmap(headers))
+ .with_protocol(protocol.into())
+ .with_timeout(timeout);
+
+ Ok(builder.build()?)
+ }
+ }
+}
+fn span_exporter(
+ base: &OtlpBaseSettings,
+ trace: &OtlpMetricsSettings,
+) -> ProviderResult<SpanExporter> {
+ let endpoint = trace
+ .endpoint
+ .clone()
+ .or_else(|| Some(format!("{}/v1/traces", base.endpoint.as_ref()?)))
+ .ok_or(ProviderError::EndpointUnset)?;
+ let headers = trace.headers.as_deref().or(base.headers.as_deref());
+ let timeout = Duration::from_millis(trace.timeout.or(base.timeout).unwrap_or(10000));
+
+ let protocol = trace
+ .protocol
+ .or(base.protocol)
+ .ok_or(ProviderError::UnsetProtocol)?;
+
+ match protocol {
+ OtlpProtocol::Grpc => {
+ let mut builder = SpanExporter::builder()
+ .with_tonic()
+ .with_endpoint(endpoint)
+ .with_metadata(parse_headers_metadata_map(headers))
+ .with_protocol(protocol.into())
+ .with_timeout(timeout);
+ let compression = trace.compression.or(base.compression);
+ if let Some(compression) = compression {
+ builder = builder.with_compression(compression.into());
+ }
+
+ Ok(builder.build()?)
+ }
+ OtlpProtocol::HttpProtobuf | OtlpProtocol::HttpJson => {
+ let builder = SpanExporter::builder()
+ .with_http()
+ .with_endpoint(endpoint)
+ .with_headers(parse_headers_hashmap(headers))
+ .with_protocol(protocol.into())
+ .with_timeout(timeout);
+
+ Ok(builder.build()?)
+ }
+ }
+}