diff --git a/Cargo.lock b/Cargo.lock index 175fd9e7..6282425e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -233,6 +233,21 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anes" version = "0.1.6" @@ -588,6 +603,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets 0.52.0", +] + [[package]] name = "ciborium" version = "0.2.1" @@ -1297,6 +1324,29 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "idna" version = "0.4.0" @@ -1547,19 +1597,24 @@ dependencies = [ "log", "notify", "openssl", + "opentelemetry", + "opentelemetry-otlp", + "opentelemetry-stdout", + "opentelemetry_sdk", "paperclip", - "prost", + "prost 0.12.1", "prost-types", "serde", "serde_yaml", "sysinfo", "thiserror", "tokio", - "tonic", + "tonic 0.10.2", "tonic-build", "tonic-reflection", "tracing", "tracing-log", + "tracing-opentelemetry", "tracing-subscriber", "url", ] @@ -1889,6 +1944,108 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "opentelemetry" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" +dependencies = [ + "futures-core", + "futures-sink", + "indexmap 2.0.2", + "js-sys", + "once_cell", + "pin-project-lite", + "thiserror", + "urlencoding", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f24cda83b20ed2433c68241f918d0f6fdec8b1d43b7a9590ab4420c5095ca930" +dependencies = [ + "async-trait", + "futures-core", + "http", + "opentelemetry", + "opentelemetry-proto", + "opentelemetry-semantic-conventions", + "opentelemetry_sdk", + "prost 0.11.9", + "thiserror", + "tokio", + "tonic 0.9.2", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2e155ce5cc812ea3d1dffbd1539aed653de4bf4882d60e6e04dcf0901d674e1" +dependencies = [ + "opentelemetry", + "opentelemetry_sdk", + "prost 0.11.9", + "tonic 0.9.2", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5774f1ef1f982ef2a447f6ee04ec383981a3ab99c8e77a1a7b30182e65bbc84" +dependencies = [ + "opentelemetry", +] + +[[package]] +name = "opentelemetry-stdout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13b2df4cd59c176099ac82806725ba340c8fa7b1a7004c0912daad30470f63e" +dependencies = [ + "chrono", + "futures-util", + "opentelemetry", + "opentelemetry_sdk", + "ordered-float", + "serde", + "serde_json", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f16aec8a98a457a52664d69e0091bac3a0abd18ead9b641cb00202ba4e0efe4" +dependencies = [ + "async-trait", + "crossbeam-channel", + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "once_cell", + "opentelemetry", + "ordered-float", + "percent-encoding", + "rand", + "thiserror", + "tokio", + "tokio-stream", +] + +[[package]] +name = "ordered-float" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e" +dependencies = [ + "num-traits", +] + [[package]] name = "overload" version = "0.1.1" @@ -1988,7 +2145,7 @@ dependencies = [ "libc", "redox_syscall 0.4.1", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -2167,6 +2324,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive 0.11.9", +] + [[package]] name = "prost" version = "0.12.1" @@ -2174,7 +2341,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4fdd22f3b9c31b53c060df4a0613a1c7f062d4115a2b984dd15b1858f7e340d" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.12.1", ] [[package]] @@ -2191,7 +2358,7 @@ dependencies = [ "once_cell", "petgraph", "prettyplease", - "prost", + "prost 0.12.1", "prost-types", "regex", "syn 2.0.38", @@ -2199,6 +2366,19 @@ dependencies = [ "which", ] +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "prost-derive" version = "0.12.1" @@ -2218,7 +2398,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e081b29f63d83a4bc75cfc9f3fe424f9156cf92d8a4f0c9407cce9a1b67327cf" dependencies = [ - "prost", + "prost 0.12.1", ] [[package]] @@ -3067,6 +3247,34 @@ dependencies = [ "tracing", ] +[[package]] +name = "tonic" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" +dependencies = [ + "async-trait", + "axum", + "base64 0.21.4", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost 0.11.9", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tonic" version = "0.10.2" @@ -3085,7 +3293,7 @@ dependencies = [ "hyper-timeout", "percent-encoding", "pin-project", - "prost", + "prost 0.12.1", "tokio", "tokio-stream", "tower", @@ -3113,11 +3321,11 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fa37c513df1339d197f4ba21d28c918b9ef1ac1768265f11ecb6b7f1cba1b76" dependencies = [ - "prost", + "prost 0.12.1", "prost-types", "tokio", "tokio-stream", - "tonic", + "tonic 0.10.2", ] [[package]] @@ -3196,6 +3404,24 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-opentelemetry" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c67ac25c5407e7b961fafc6f7e9aa5958fd297aada2d20fa2ae1737357e55596" +dependencies = [ + "js-sys", + "once_cell", + "opentelemetry", + "opentelemetry_sdk", + "smallvec", + "tracing", + "tracing-core", + "tracing-log", + "tracing-subscriber", + "web-time", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" @@ -3434,6 +3660,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "which" version = "4.4.2" @@ -3477,13 +3713,22 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -3492,13 +3737,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -3507,42 +3767,84 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winreg" version = "0.50.0" diff --git a/doc/server/configuration.md b/doc/server/configuration.md index 2cfb8d4f..b3db958c 100644 --- a/doc/server/configuration.md +++ b/doc/server/configuration.md @@ -31,6 +31,8 @@ Options: Include the Limit Name in prometheus label -v... Sets the level of verbosity + --tracing-endpoint + The endpoint for the tracing service --validate Validates the LIMITS_FILE and exits -H, --rate-limit-headers @@ -326,6 +328,14 @@ docs](https://prometheus.io/docs/practices/naming/#labels) - Optional. Disabled by default. - Format: `bool`, set to `"1"` to enable. + +#### `TRACING_ENDPOINT` + +- The endpoint of the OTLP tracing collector (scheme://host:port). +- Optional. Default to `""` (tracing disabled) +- Format: `string` + + #### `REDIS_LOCAL_CACHE_ENABLED` - Enables a storage implementation that uses Redis, but also caches some data in diff --git a/limitador-server/Cargo.toml b/limitador-server/Cargo.toml index 8c97e5ad..07907a04 100644 --- a/limitador-server/Cargo.toml +++ b/limitador-server/Cargo.toml @@ -30,6 +30,11 @@ log = "0.4" tracing = "0.1.40" tracing-log = "0.2.0" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +tracing-opentelemetry = "0.22" +opentelemetry = "0.21" +opentelemetry_sdk = { version = "0.21", features = ["rt-tokio"] } +opentelemetry-stdout = { version = "0.2.0", features = ["trace"] } +opentelemetry-otlp = "0.14.0" url = "2" actix-web = "4.1" actix-rt = "2" diff --git a/limitador-server/README.md b/limitador-server/README.md index 0fda9add..33fe150b 100644 --- a/limitador-server/README.md +++ b/limitador-server/README.md @@ -37,6 +37,8 @@ Options: Include the Limit Name in prometheus label -v... Sets the level of verbosity + --tracing-endpoint + The endpoint for the tracing service --validate Validates the LIMITS_FILE and exits -H, --rate-limit-headers diff --git a/limitador-server/src/config.rs b/limitador-server/src/config.rs index e6a1481d..53037cf2 100644 --- a/limitador-server/src/config.rs +++ b/limitador-server/src/config.rs @@ -31,6 +31,7 @@ pub struct Configuration { http_host: String, http_port: u16, pub limit_name_in_labels: bool, + pub tracing_endpoint: String, pub log_level: Option, pub rate_limit_headers: RateLimitHeaders, pub grpc_reflection_service: bool, @@ -45,6 +46,7 @@ pub mod env { pub static ref ENVOY_RLS_PORT: Option<&'static str> = value_for("ENVOY_RLS_PORT"); pub static ref HTTP_API_HOST: Option<&'static str> = value_for("HTTP_API_HOST"); pub static ref HTTP_API_PORT: Option<&'static str> = value_for("HTTP_API_PORT"); + pub static ref TRACING_ENDPOINT: Option<&'static str> = value_for("TRACING_ENDPOINT"); pub static ref DISK_PATH: Option<&'static str> = value_for("DISK_PATH"); pub static ref DISK_OPTIMIZE: Option<&'static str> = value_for("DISK_OPTIMIZE"); pub static ref REDIS_URL: Option<&'static str> = value_for("REDIS_URL"); @@ -83,6 +85,7 @@ impl Configuration { http_host: String, http_port: u16, limit_name_in_labels: bool, + tracing_endpoint: String, rate_limit_headers: RateLimitHeaders, grpc_reflection_service: bool, ) -> Self { @@ -94,6 +97,7 @@ impl Configuration { http_host, http_port, limit_name_in_labels, + tracing_endpoint, log_level: None, rate_limit_headers, grpc_reflection_service, @@ -122,6 +126,7 @@ impl Default for Configuration { http_host: "".to_string(), http_port: 0, limit_name_in_labels: false, + tracing_endpoint: "".to_string(), log_level: None, rate_limit_headers: RateLimitHeaders::None, grpc_reflection_service: false, diff --git a/limitador-server/src/main.rs b/limitador-server/src/main.rs index 17205a73..68f06f31 100644 --- a/limitador-server/src/main.rs +++ b/limitador-server/src/main.rs @@ -31,6 +31,9 @@ use limitador::{ }; use notify::event::{ModifyKind, RenameMode}; use notify::{Error, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; +use opentelemetry::KeyValue; +use opentelemetry_otlp::WithExportConfig; +use opentelemetry_sdk::{trace, Resource}; use std::env::VarError; use std::fs; use std::path::Path; @@ -42,6 +45,8 @@ use thiserror::Error; use tokio::runtime::Handle; use tracing::level_filters::LevelFilter; use tracing_subscriber::fmt::format::FmtSpan; +use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::util::SubscriberInitExt; mod envoy_rls; mod http_api; @@ -290,12 +295,36 @@ async fn main() -> Result<(), Box> { .max_level_hint() .unwrap_or(LevelFilter::ERROR) }); - let builder = if level >= LevelFilter::DEBUG { - tracing_subscriber::fmt().with_span_events(FmtSpan::CLOSE) + let fmt_layer = if level >= LevelFilter::DEBUG { + tracing_subscriber::fmt::layer().with_span_events(FmtSpan::CLOSE) } else { - tracing_subscriber::fmt() + tracing_subscriber::fmt::layer() + }; + + if !config.tracing_endpoint.is_empty() { + let tracer = opentelemetry_otlp::new_pipeline() + .tracing() + .with_exporter( + opentelemetry_otlp::new_exporter() + .tonic() + .with_endpoint(config.tracing_endpoint.clone()), + ) + .with_trace_config(trace::config().with_resource(Resource::new(vec![ + KeyValue::new("service.name", "limitador-server"), + ]))) + .install_batch(opentelemetry_sdk::runtime::Tokio)?; + let telemetry_layer = tracing_opentelemetry::layer().with_tracer(tracer); + tracing_subscriber::registry() + .with(level) + .with(fmt_layer) + .with(telemetry_layer) + .init(); + } else { + tracing_subscriber::registry() + .with(level) + .with(fmt_layer) + .init(); }; - builder.with_max_level(level).init(); info!("Version: {}", version); info!("Using config: {:?}", config); @@ -344,7 +373,7 @@ async fn main() -> Result<(), Box> { // Sometimes this event happens in k8s envs when // content source is a configmap and it is replaced - // As the move event always occurrs, + // As the move event always occurs, // skip reloading limit file in this event. // the parent dir is being watched @@ -494,26 +523,33 @@ fn create_config() -> (Configuration, &'static str) { .display_order(5) .help("Include the Limit Name in prometheus label"), ) + .arg( + Arg::new("tracing_endpoint") + .long("tracing-endpoint") + .default_value(config::env::TRACING_ENDPOINT.unwrap_or("")) + .display_order(6) + .help("The host for the tracing service"), + ) .arg( Arg::new("v") .short('v') .action(ArgAction::Count) .value_parser(value_parser!(u8).range(..5)) - .display_order(6) + .display_order(7) .help("Sets the level of verbosity"), ) .arg( Arg::new("validate") .long("validate") .action(ArgAction::SetTrue) - .display_order(7) + .display_order(8) .help("Validates the LIMITS_FILE and exits"), ) .arg( Arg::new("rate_limit_headers") .long("rate-limit-headers") .short('H') - .display_order(8) + .display_order(9) .default_value(config::env::RATE_LIMIT_HEADERS.unwrap_or("NONE")) .value_parser(clap::builder::PossibleValuesParser::new([ "NONE", @@ -525,7 +561,7 @@ fn create_config() -> (Configuration, &'static str) { Arg::new("grpc_reflection_service") .long("grpc-reflection-service") .action(ArgAction::SetTrue) - .display_order(9) + .display_order(10) .help("Enables gRPC server reflection service"), ) .subcommand( @@ -757,6 +793,10 @@ fn create_config() -> (Configuration, &'static str) { *matches.get_one::("http_port").unwrap(), matches.get_flag("limit_name_in_labels") || env_option_is_enabled("LIMIT_NAME_IN_PROMETHEUS_LABELS"), + matches + .get_one::("tracing_endpoint") + .unwrap() + .into(), rate_limit_headers, matches.get_flag("grpc_reflection_service"), );