From a0bd703a8afaa7a5fdbe1ed52c398f1fe2ae719e Mon Sep 17 00:00:00 2001 From: Adam Cattermole Date: Mon, 5 Feb 2024 13:12:59 +0000 Subject: [PATCH 1/4] Support pushing traces to otel collector --- Cargo.lock | 336 +++++++++++++++++++++++++++++++-- limitador-server/Cargo.toml | 5 + limitador-server/src/config.rs | 15 ++ limitador-server/src/main.rs | 67 ++++++- 4 files changed, 397 insertions(+), 26 deletions(-) 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/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/src/config.rs b/limitador-server/src/config.rs index e6a1481d..eba352ed 100644 --- a/limitador-server/src/config.rs +++ b/limitador-server/src/config.rs @@ -31,6 +31,8 @@ pub struct Configuration { http_host: String, http_port: u16, pub limit_name_in_labels: bool, + pub tracing_host: String, + tracing_port: u16, pub log_level: Option, pub rate_limit_headers: RateLimitHeaders, pub grpc_reflection_service: bool, @@ -45,6 +47,8 @@ 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_HOST: Option<&'static str> = value_for("TRACING_HOST"); + pub static ref TRACING_PORT: Option<&'static str> = value_for("TRACING_PORT"); 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"); @@ -72,6 +76,7 @@ pub mod env { impl Configuration { pub const DEFAULT_RLS_PORT: &'static str = "8081"; pub const DEFAULT_HTTP_PORT: &'static str = "8080"; + pub const DEFAULT_TRACING_PORT: &'static str = "4317"; pub const DEFAULT_IP_BIND: &'static str = "0.0.0.0"; #[allow(clippy::too_many_arguments)] @@ -83,6 +88,8 @@ impl Configuration { http_host: String, http_port: u16, limit_name_in_labels: bool, + tracing_host: String, + tracing_port: u16, rate_limit_headers: RateLimitHeaders, grpc_reflection_service: bool, ) -> Self { @@ -94,6 +101,8 @@ impl Configuration { http_host, http_port, limit_name_in_labels, + tracing_host, + tracing_port, log_level: None, rate_limit_headers, grpc_reflection_service, @@ -107,6 +116,10 @@ impl Configuration { pub fn http_address(&self) -> String { format!("{}:{}", self.http_host, self.http_port) } + + pub fn tracing_address(&self) -> String { + format!("{}:{}", self.tracing_host, self.tracing_port) + } } #[cfg(test)] @@ -122,6 +135,8 @@ impl Default for Configuration { http_host: "".to_string(), http_port: 0, limit_name_in_labels: false, + tracing_host: "".to_string(), + tracing_port: 0, 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..5f2fa5e7 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::{Protocol, 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,37 @@ 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_host.is_empty() { + let tracer = opentelemetry_otlp::new_pipeline() + .tracing() + .with_exporter( + opentelemetry_otlp::new_exporter() + .tonic() + .with_endpoint(format!("rpc://{}", config.tracing_address())) + .with_protocol(Protocol::Grpc), + ) + .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 +374,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 +524,43 @@ fn create_config() -> (Configuration, &'static str) { .display_order(5) .help("Include the Limit Name in prometheus label"), ) + .arg( + Arg::new("tracing_host") + .long("tracing-host") + .default_value(config::env::TRACING_HOST.unwrap_or("")) + .display_order(6) + .help("The host for the tracing service"), + ) + .arg( + Arg::new("tracing_port") + .long("tracing-port") + .default_value( + config::env::TRACING_PORT.unwrap_or(Configuration::DEFAULT_TRACING_PORT), + ) + .value_parser(value_parser!(u16)) + .display_order(7) + .help("The port 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(8) .help("Sets the level of verbosity"), ) .arg( Arg::new("validate") .long("validate") .action(ArgAction::SetTrue) - .display_order(7) + .display_order(9) .help("Validates the LIMITS_FILE and exits"), ) .arg( Arg::new("rate_limit_headers") .long("rate-limit-headers") .short('H') - .display_order(8) + .display_order(10) .default_value(config::env::RATE_LIMIT_HEADERS.unwrap_or("NONE")) .value_parser(clap::builder::PossibleValuesParser::new([ "NONE", @@ -525,7 +572,7 @@ fn create_config() -> (Configuration, &'static str) { Arg::new("grpc_reflection_service") .long("grpc-reflection-service") .action(ArgAction::SetTrue) - .display_order(9) + .display_order(11) .help("Enables gRPC server reflection service"), ) .subcommand( @@ -757,6 +804,8 @@ 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_host").unwrap().into(), + *matches.get_one::("tracing_port").unwrap(), rate_limit_headers, matches.get_flag("grpc_reflection_service"), ); From 43afa413a348d9550b438e8b20e55dec84e831d2 Mon Sep 17 00:00:00 2001 From: Adam Cattermole Date: Wed, 21 Feb 2024 11:23:31 +0000 Subject: [PATCH 2/4] Update docs with tracing options --- doc/server/configuration.md | 16 ++++++++++++++++ limitador-server/README.md | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/doc/server/configuration.md b/doc/server/configuration.md index 2cfb8d4f..040597be 100644 --- a/doc/server/configuration.md +++ b/doc/server/configuration.md @@ -31,6 +31,10 @@ Options: Include the Limit Name in prometheus label -v... Sets the level of verbosity + --tracing-host + The host for the tracing service + --tracing-port + The port for the tracing service [default: 4317] --validate Validates the LIMITS_FILE and exits -H, --rate-limit-headers @@ -326,6 +330,18 @@ docs](https://prometheus.io/docs/practices/naming/#labels) - Optional. Disabled by default. - Format: `bool`, set to `"1"` to enable. +#### `TRACING_HOST` + +- Host of the OTLP tracing collector. +- Optional. Default to `""` (tracing disabled) +- Format: `string` + +#### `TRACING_PORT` + +- Port of the OTLP tracing collector. +- Optional. Defaults to `4317`. +- Format: `integer`. + #### `REDIS_LOCAL_CACHE_ENABLED` - Enables a storage implementation that uses Redis, but also caches some data in diff --git a/limitador-server/README.md b/limitador-server/README.md index 0fda9add..166c27ae 100644 --- a/limitador-server/README.md +++ b/limitador-server/README.md @@ -37,6 +37,10 @@ Options: Include the Limit Name in prometheus label -v... Sets the level of verbosity + --tracing-host + The host for the tracing service + --tracing-port + The port for the tracing service [default: 4317] --validate Validates the LIMITS_FILE and exits -H, --rate-limit-headers From 0e3f68a4b9d7ad920f8df872c6abb0015cc29f22 Mon Sep 17 00:00:00 2001 From: Adam Cattermole Date: Mon, 18 Mar 2024 12:58:03 +0000 Subject: [PATCH 3/4] Move to single tracing-endpoint option --- doc/server/configuration.md | 139 ++++++++++++++------------------- limitador-server/src/config.rs | 20 ++--- limitador-server/src/main.rs | 37 ++++----- 3 files changed, 79 insertions(+), 117 deletions(-) diff --git a/doc/server/configuration.md b/doc/server/configuration.md index 040597be..d7719410 100644 --- a/doc/server/configuration.md +++ b/doc/server/configuration.md @@ -31,10 +31,8 @@ Options: Include the Limit Name in prometheus label -v... Sets the level of verbosity - --tracing-host - The host for the tracing service - --tracing-port - The port for the tracing service [default: 4317] + --tracing-endpoint + The endpoint for the tracing service --validate Validates the LIMITS_FILE and exits -H, --rate-limit-headers @@ -47,7 +45,8 @@ Options: Print version ``` -The values used are authoritative over any [environment variables](#configuration-using-environment-variables) independently set. +The values used are authoritative over any [environment variables](#configuration-using-environment-variables) +independently set. ### Limit definitions @@ -101,13 +100,13 @@ variables: - user_id ``` - - `namespace` namespaces the limit, will generally be the domain, [see here](../how-it-works.md) - - `seconds` is the duration for which the limit applies, in seconds: e.g. `60` is a span of time of one minute - - `max_value` is the actual limit, e.g. `100` would limit to 100 requests - - `name` lets the user _optionally_ name the limit - - `variables` is an array of variables, which once resolved, will be used to qualify counters for the limit, - e.g. `api_key` to limit per api keys - - `conditions` is an array of conditions, which once evaluated will decide whether to apply the limit or not +- `namespace` namespaces the limit, will generally be the domain, [see here](../how-it-works.md) +- `seconds` is the duration for which the limit applies, in seconds: e.g. `60` is a span of time of one minute +- `max_value` is the actual limit, e.g. `100` would limit to 100 requests +- `name` lets the user _optionally_ name the limit +- `variables` is an array of variables, which once resolved, will be used to qualify counters for the limit, + e.g. `api_key` to limit per api keys +- `conditions` is an array of conditions, which once evaluated will decide whether to apply the limit or not #### `condition` syntax @@ -116,9 +115,9 @@ Each `condition` is an expression producing a boolean value (`true` or `false`). Expressions follow the following syntax: `$IDENTIFIER $OP $STRING_LITERAL`, where: - - `$IDENTIFIER` will be used to resolve the value at evaluation time, e.g. `role` - - `$OP` is an operator, either `==` or `!=` - - `$STRING_LITERAL` is a literal string value, `"` or `'` demarcated, e.g. `"admin"` +- `$IDENTIFIER` will be used to resolve the value at evaluation time, e.g. `role` +- `$OP` is an operator, either `==` or `!=` +- `$STRING_LITERAL` is a literal string value, `"` or `'` demarcated, e.g. `"admin"` So that `role != "admin"` would apply the limit on request from all users, but `admin`'s. @@ -281,7 +280,8 @@ For an in-depth coverage of the different topologies supported and how they affe ## Configuration using environment variables The Limitador server has some options that can be configured with environment variables. These will override the -_default_ values the server uses. [Any argument](#command-line-configuration) used when starting the server will prevail over the +_default_ values the server uses. [Any argument](#command-line-configuration) used when starting the server will prevail +over the environment variables. #### `ENVOY_RLS_HOST` @@ -290,137 +290,120 @@ environment variables. - Optional. Defaults to `"0.0.0.0"`. - Format: `string`. - #### `ENVOY_RLS_PORT` - Port where the Envoy RLS server listens. - Optional. Defaults to `8081`. - Format: `integer`. - #### `HTTP_API_HOST` - Host where the HTTP server listens. - Optional. Defaults to `"0.0.0.0"`. - Format: `string`. - #### `HTTP_API_PORT` - Port where the HTTP API listens. - Optional. Defaults to `8080`. - Format: `integer`. - #### `LIMITS_FILE` - YAML file that contains the limits to create when Limitador boots. If the -limits specified already have counters associated, Limitador will not delete them. -Changes to the file will be picked up by the running server. + limits specified already have counters associated, Limitador will not delete them. + Changes to the file will be picked up by the running server. - *Required*. No default - Format: `string`, file path. - #### `LIMIT_NAME_IN_PROMETHEUS_LABELS` - Enables using limit names as labels in Prometheus metrics. This is disabled by -default because for a few limits it should be fine, but it could become a -problem when defining lots of limits. See the caution note in the [Prometheus -docs](https://prometheus.io/docs/practices/naming/#labels) + default because for a few limits it should be fine, but it could become a + problem when defining lots of limits. See the caution note in the [Prometheus + docs](https://prometheus.io/docs/practices/naming/#labels) - Optional. Disabled by default. - Format: `bool`, set to `"1"` to enable. -#### `TRACING_HOST` +#### `TRACING_ENDPOINT` -- Host of the OTLP tracing collector. +- The endpoint of the OTLP tracing collector (scheme://host:port). - Optional. Default to `""` (tracing disabled) - Format: `string` -#### `TRACING_PORT` - -- Port of the OTLP tracing collector. -- Optional. Defaults to `4317`. -- Format: `integer`. - #### `REDIS_LOCAL_CACHE_ENABLED` - Enables a storage implementation that uses Redis, but also caches some data in -memory. The idea is to improve throughput and latencies by caching the counters -in memory to reduce the number of accesses to Redis. To achieve that, this mode -sacrifices some rate-limit accuracy. This mode does two things: + memory. The idea is to improve throughput and latencies by caching the counters + in memory to reduce the number of accesses to Redis. To achieve that, this mode + sacrifices some rate-limit accuracy. This mode does two things: - Batches counter updates. Instead of updating the counters on every - request, it updates them in memory and commits them to Redis in batches. The - flushing interval can be configured with the - [`REDIS_LOCAL_CACHE_FLUSHING_PERIOD_MS`](#redis_local_cache_flushing_period_ms) - env. The trade-off is that when running several instances of Limitador, - other instances will not become aware of the counter updates until they're - committed to Redis. + request, it updates them in memory and commits them to Redis in batches. The + flushing interval can be configured with the + [`REDIS_LOCAL_CACHE_FLUSHING_PERIOD_MS`](#redis_local_cache_flushing_period_ms) + env. The trade-off is that when running several instances of Limitador, + other instances will not become aware of the counter updates until they're + committed to Redis. - Caches counters. Instead of fetching the value of a counter every time - it's needed, the value is cached for a configurable period. The trade-off is - that when running several instances of Limitador, an instance will not - become aware of the counter updates other instances do while the value is - cached. When a counter is already at 0 (limit exceeded), it's cached until - it expires in Redis. In this case, no matter what other instances do, we - know that the quota will not be reestablished until the key expires in - Redis, so in this case, rate-limit accuracy is not affected. When a counter - has still some quota remaining the situation is different, that's why we can - tune for how long it will be cached. The formula is as follows: - MIN(ttl_in_redis/[`REDIS_LOCAL_CACHE_TTL_RATIO_CACHED_COUNTERS`](#redis_local_cache_ttl_ratio_cached_counters), - [`REDIS_LOCAL_CACHE_MAX_TTL_CACHED_COUNTERS_MS`](#redis_local_cache_max_ttl_cached_counters_ms)). - For example, let's image that the current TTL (time remaining until the - limit resets) in Redis for a counter is 10 seconds, and we set the ratio to - 2, and the max time for 30s. In this case, the counter will be cached for 5s - (min(10/2, 30)). During those 5s, Limitador will not fetch the value of that - counter from Redis, so it will answer faster, but it will also miss the - updates done by other instances, so it can go over the limits in that 5s - interval. + it's needed, the value is cached for a configurable period. The trade-off is + that when running several instances of Limitador, an instance will not + become aware of the counter updates other instances do while the value is + cached. When a counter is already at 0 (limit exceeded), it's cached until + it expires in Redis. In this case, no matter what other instances do, we + know that the quota will not be reestablished until the key expires in + Redis, so in this case, rate-limit accuracy is not affected. When a counter + has still some quota remaining the situation is different, that's why we can + tune for how long it will be cached. The formula is as follows: + MIN(ttl_in_redis/[`REDIS_LOCAL_CACHE_TTL_RATIO_CACHED_COUNTERS`](#redis_local_cache_ttl_ratio_cached_counters), + [`REDIS_LOCAL_CACHE_MAX_TTL_CACHED_COUNTERS_MS`](#redis_local_cache_max_ttl_cached_counters_ms)). + For example, let's image that the current TTL (time remaining until the + limit resets) in Redis for a counter is 10 seconds, and we set the ratio to + 2, and the max time for 30s. In this case, the counter will be cached for 5s + (min(10/2, 30)). During those 5s, Limitador will not fetch the value of that + counter from Redis, so it will answer faster, but it will also miss the + updates done by other instances, so it can go over the limits in that 5s + interval. - Optional. Disabled by default. - Format: set to "1" to enable. - Note: "REDIS_URL" needs to be set. - #### `REDIS_LOCAL_CACHE_FLUSHING_PERIOD_MS` - Used to configure the local cache when using Redis. See -[`REDIS_LOCAL_CACHE_ENABLED`](#redis_local_cache_enabled). This env only applies -when `"REDIS_LOCAL_CACHE_ENABLED" == 1`. + [`REDIS_LOCAL_CACHE_ENABLED`](#redis_local_cache_enabled). This env only applies + when `"REDIS_LOCAL_CACHE_ENABLED" == 1`. - Optional. Defaults to `1000`. - Format: `integer`. Duration in milliseconds. - #### `REDIS_LOCAL_CACHE_MAX_TTL_CACHED_COUNTERS_MS` - Used to configure the local cache when using Redis. See -[`REDIS_LOCAL_CACHE_ENABLED`](#redis_local_cache_enabled). This env only applies -when `"REDIS_LOCAL_CACHE_ENABLED" == 1`. + [`REDIS_LOCAL_CACHE_ENABLED`](#redis_local_cache_enabled). This env only applies + when `"REDIS_LOCAL_CACHE_ENABLED" == 1`. - Optional. Defaults to `5000`. - Format: `integer`. Duration in milliseconds. - #### `REDIS_LOCAL_CACHE_TTL_RATIO_CACHED_COUNTERS` - Used to configure the local cache when using Redis. See -[`REDIS_LOCAL_CACHE_ENABLED`](#redis_local_cache_enabled). This env only applies -when `"REDIS_LOCAL_CACHE_ENABLED" == 1`. + [`REDIS_LOCAL_CACHE_ENABLED`](#redis_local_cache_enabled). This env only applies + when `"REDIS_LOCAL_CACHE_ENABLED" == 1`. - Optional. Defaults to `10`. - Format: `integer`. - #### `REDIS_URL` - Redis URL. Required only when you want to use Redis to store the limits. - Optional. By default, Limitador stores the limits in memory and does not -require Redis. + require Redis. - Format: `string`, URL in the format of `"redis://127.0.0.1:6379"`. - #### `RUST_LOG` - Defines the log level. - Optional. Defaults to `"error"`. - Format: `enum`: `"debug"`, `"error"`, `"info"`, `"warn"`, or `"trace"`. - ### When built with the `infinispan` feature - _experimental_ #### `INFINISPAN_CACHE_NAME` @@ -431,7 +414,6 @@ require Redis. - Optional. By default, Limitador will use a cache called `"limitador"`. - Format: `string`. - #### `INFINISPAN_COUNTERS_CONSISTENCY` - Defines the consistency mode for the Infinispan counters created by Limitador. @@ -439,7 +421,6 @@ require Redis. - Optional. Defaults to `"strong"`. - Format: `enum`: `"Strong"` or `"Weak"`. - #### `INFINISPAN_URL` - Infinispan URL. Required only when you want to use Infinispan to store the @@ -448,12 +429,12 @@ require Redis. require Infinispan. - Format: `URL`, in the format of `http://username:password@127.0.0.1:11222`. - #### `RATE_LIMIT_HEADERS` - Enables rate limit response headers. Only supported by the RLS server. - Optional. Defaults to `"NONE"`. - Must be one of: - - `"NONE"` - Does not add any additional headers to the http response. - - `"DRAFT_VERSION_03"`. Adds response headers per https://datatracker.ietf.org/doc/id/draft-polli-ratelimit-headers-03.html + - `"NONE"` - Does not add any additional headers to the http response. + - `"DRAFT_VERSION_03"`. Adds response headers + per https://datatracker.ietf.org/doc/id/draft-polli-ratelimit-headers-03.html diff --git a/limitador-server/src/config.rs b/limitador-server/src/config.rs index eba352ed..53037cf2 100644 --- a/limitador-server/src/config.rs +++ b/limitador-server/src/config.rs @@ -31,8 +31,7 @@ pub struct Configuration { http_host: String, http_port: u16, pub limit_name_in_labels: bool, - pub tracing_host: String, - tracing_port: u16, + pub tracing_endpoint: String, pub log_level: Option, pub rate_limit_headers: RateLimitHeaders, pub grpc_reflection_service: bool, @@ -47,8 +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_HOST: Option<&'static str> = value_for("TRACING_HOST"); - pub static ref TRACING_PORT: Option<&'static str> = value_for("TRACING_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"); @@ -76,7 +74,6 @@ pub mod env { impl Configuration { pub const DEFAULT_RLS_PORT: &'static str = "8081"; pub const DEFAULT_HTTP_PORT: &'static str = "8080"; - pub const DEFAULT_TRACING_PORT: &'static str = "4317"; pub const DEFAULT_IP_BIND: &'static str = "0.0.0.0"; #[allow(clippy::too_many_arguments)] @@ -88,8 +85,7 @@ impl Configuration { http_host: String, http_port: u16, limit_name_in_labels: bool, - tracing_host: String, - tracing_port: u16, + tracing_endpoint: String, rate_limit_headers: RateLimitHeaders, grpc_reflection_service: bool, ) -> Self { @@ -101,8 +97,7 @@ impl Configuration { http_host, http_port, limit_name_in_labels, - tracing_host, - tracing_port, + tracing_endpoint, log_level: None, rate_limit_headers, grpc_reflection_service, @@ -116,10 +111,6 @@ impl Configuration { pub fn http_address(&self) -> String { format!("{}:{}", self.http_host, self.http_port) } - - pub fn tracing_address(&self) -> String { - format!("{}:{}", self.tracing_host, self.tracing_port) - } } #[cfg(test)] @@ -135,8 +126,7 @@ impl Default for Configuration { http_host: "".to_string(), http_port: 0, limit_name_in_labels: false, - tracing_host: "".to_string(), - tracing_port: 0, + 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 5f2fa5e7..68f06f31 100644 --- a/limitador-server/src/main.rs +++ b/limitador-server/src/main.rs @@ -32,7 +32,7 @@ use limitador::{ use notify::event::{ModifyKind, RenameMode}; use notify::{Error, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; use opentelemetry::KeyValue; -use opentelemetry_otlp::{Protocol, WithExportConfig}; +use opentelemetry_otlp::WithExportConfig; use opentelemetry_sdk::{trace, Resource}; use std::env::VarError; use std::fs; @@ -301,14 +301,13 @@ async fn main() -> Result<(), Box> { tracing_subscriber::fmt::layer() }; - if !config.tracing_host.is_empty() { + if !config.tracing_endpoint.is_empty() { let tracer = opentelemetry_otlp::new_pipeline() .tracing() .with_exporter( opentelemetry_otlp::new_exporter() .tonic() - .with_endpoint(format!("rpc://{}", config.tracing_address())) - .with_protocol(Protocol::Grpc), + .with_endpoint(config.tracing_endpoint.clone()), ) .with_trace_config(trace::config().with_resource(Resource::new(vec![ KeyValue::new("service.name", "limitador-server"), @@ -525,42 +524,32 @@ fn create_config() -> (Configuration, &'static str) { .help("Include the Limit Name in prometheus label"), ) .arg( - Arg::new("tracing_host") - .long("tracing-host") - .default_value(config::env::TRACING_HOST.unwrap_or("")) + 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("tracing_port") - .long("tracing-port") - .default_value( - config::env::TRACING_PORT.unwrap_or(Configuration::DEFAULT_TRACING_PORT), - ) - .value_parser(value_parser!(u16)) - .display_order(7) - .help("The port for the tracing service"), - ) .arg( Arg::new("v") .short('v') .action(ArgAction::Count) .value_parser(value_parser!(u8).range(..5)) - .display_order(8) + .display_order(7) .help("Sets the level of verbosity"), ) .arg( Arg::new("validate") .long("validate") .action(ArgAction::SetTrue) - .display_order(9) + .display_order(8) .help("Validates the LIMITS_FILE and exits"), ) .arg( Arg::new("rate_limit_headers") .long("rate-limit-headers") .short('H') - .display_order(10) + .display_order(9) .default_value(config::env::RATE_LIMIT_HEADERS.unwrap_or("NONE")) .value_parser(clap::builder::PossibleValuesParser::new([ "NONE", @@ -572,7 +561,7 @@ fn create_config() -> (Configuration, &'static str) { Arg::new("grpc_reflection_service") .long("grpc-reflection-service") .action(ArgAction::SetTrue) - .display_order(11) + .display_order(10) .help("Enables gRPC server reflection service"), ) .subcommand( @@ -804,8 +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_host").unwrap().into(), - *matches.get_one::("tracing_port").unwrap(), + matches + .get_one::("tracing_endpoint") + .unwrap() + .into(), rate_limit_headers, matches.get_flag("grpc_reflection_service"), ); From 2575b26aaa5a58a462b9034cbee89b595f5d96bd Mon Sep 17 00:00:00 2001 From: Adam Cattermole Date: Tue, 19 Mar 2024 14:22:56 +0000 Subject: [PATCH 4/4] Reset unnecessary docs changes --- doc/server/configuration.md | 125 ++++++++++++++++++++---------------- limitador-server/README.md | 6 +- 2 files changed, 71 insertions(+), 60 deletions(-) diff --git a/doc/server/configuration.md b/doc/server/configuration.md index d7719410..b3db958c 100644 --- a/doc/server/configuration.md +++ b/doc/server/configuration.md @@ -45,8 +45,7 @@ Options: Print version ``` -The values used are authoritative over any [environment variables](#configuration-using-environment-variables) -independently set. +The values used are authoritative over any [environment variables](#configuration-using-environment-variables) independently set. ### Limit definitions @@ -100,13 +99,13 @@ variables: - user_id ``` -- `namespace` namespaces the limit, will generally be the domain, [see here](../how-it-works.md) -- `seconds` is the duration for which the limit applies, in seconds: e.g. `60` is a span of time of one minute -- `max_value` is the actual limit, e.g. `100` would limit to 100 requests -- `name` lets the user _optionally_ name the limit -- `variables` is an array of variables, which once resolved, will be used to qualify counters for the limit, - e.g. `api_key` to limit per api keys -- `conditions` is an array of conditions, which once evaluated will decide whether to apply the limit or not + - `namespace` namespaces the limit, will generally be the domain, [see here](../how-it-works.md) + - `seconds` is the duration for which the limit applies, in seconds: e.g. `60` is a span of time of one minute + - `max_value` is the actual limit, e.g. `100` would limit to 100 requests + - `name` lets the user _optionally_ name the limit + - `variables` is an array of variables, which once resolved, will be used to qualify counters for the limit, + e.g. `api_key` to limit per api keys + - `conditions` is an array of conditions, which once evaluated will decide whether to apply the limit or not #### `condition` syntax @@ -115,9 +114,9 @@ Each `condition` is an expression producing a boolean value (`true` or `false`). Expressions follow the following syntax: `$IDENTIFIER $OP $STRING_LITERAL`, where: -- `$IDENTIFIER` will be used to resolve the value at evaluation time, e.g. `role` -- `$OP` is an operator, either `==` or `!=` -- `$STRING_LITERAL` is a literal string value, `"` or `'` demarcated, e.g. `"admin"` + - `$IDENTIFIER` will be used to resolve the value at evaluation time, e.g. `role` + - `$OP` is an operator, either `==` or `!=` + - `$STRING_LITERAL` is a literal string value, `"` or `'` demarcated, e.g. `"admin"` So that `role != "admin"` would apply the limit on request from all users, but `admin`'s. @@ -280,8 +279,7 @@ For an in-depth coverage of the different topologies supported and how they affe ## Configuration using environment variables The Limitador server has some options that can be configured with environment variables. These will override the -_default_ values the server uses. [Any argument](#command-line-configuration) used when starting the server will prevail -over the +_default_ values the server uses. [Any argument](#command-line-configuration) used when starting the server will prevail over the environment variables. #### `ENVOY_RLS_HOST` @@ -290,120 +288,133 @@ environment variables. - Optional. Defaults to `"0.0.0.0"`. - Format: `string`. + #### `ENVOY_RLS_PORT` - Port where the Envoy RLS server listens. - Optional. Defaults to `8081`. - Format: `integer`. + #### `HTTP_API_HOST` - Host where the HTTP server listens. - Optional. Defaults to `"0.0.0.0"`. - Format: `string`. + #### `HTTP_API_PORT` - Port where the HTTP API listens. - Optional. Defaults to `8080`. - Format: `integer`. + #### `LIMITS_FILE` - YAML file that contains the limits to create when Limitador boots. If the - limits specified already have counters associated, Limitador will not delete them. - Changes to the file will be picked up by the running server. +limits specified already have counters associated, Limitador will not delete them. +Changes to the file will be picked up by the running server. - *Required*. No default - Format: `string`, file path. + #### `LIMIT_NAME_IN_PROMETHEUS_LABELS` - Enables using limit names as labels in Prometheus metrics. This is disabled by - default because for a few limits it should be fine, but it could become a - problem when defining lots of limits. See the caution note in the [Prometheus - docs](https://prometheus.io/docs/practices/naming/#labels) +default because for a few limits it should be fine, but it could become a +problem when defining lots of limits. See the caution note in the [Prometheus +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 - memory. The idea is to improve throughput and latencies by caching the counters - in memory to reduce the number of accesses to Redis. To achieve that, this mode - sacrifices some rate-limit accuracy. This mode does two things: +memory. The idea is to improve throughput and latencies by caching the counters +in memory to reduce the number of accesses to Redis. To achieve that, this mode +sacrifices some rate-limit accuracy. This mode does two things: - Batches counter updates. Instead of updating the counters on every - request, it updates them in memory and commits them to Redis in batches. The - flushing interval can be configured with the - [`REDIS_LOCAL_CACHE_FLUSHING_PERIOD_MS`](#redis_local_cache_flushing_period_ms) - env. The trade-off is that when running several instances of Limitador, - other instances will not become aware of the counter updates until they're - committed to Redis. + request, it updates them in memory and commits them to Redis in batches. The + flushing interval can be configured with the + [`REDIS_LOCAL_CACHE_FLUSHING_PERIOD_MS`](#redis_local_cache_flushing_period_ms) + env. The trade-off is that when running several instances of Limitador, + other instances will not become aware of the counter updates until they're + committed to Redis. - Caches counters. Instead of fetching the value of a counter every time - it's needed, the value is cached for a configurable period. The trade-off is - that when running several instances of Limitador, an instance will not - become aware of the counter updates other instances do while the value is - cached. When a counter is already at 0 (limit exceeded), it's cached until - it expires in Redis. In this case, no matter what other instances do, we - know that the quota will not be reestablished until the key expires in - Redis, so in this case, rate-limit accuracy is not affected. When a counter - has still some quota remaining the situation is different, that's why we can - tune for how long it will be cached. The formula is as follows: - MIN(ttl_in_redis/[`REDIS_LOCAL_CACHE_TTL_RATIO_CACHED_COUNTERS`](#redis_local_cache_ttl_ratio_cached_counters), - [`REDIS_LOCAL_CACHE_MAX_TTL_CACHED_COUNTERS_MS`](#redis_local_cache_max_ttl_cached_counters_ms)). - For example, let's image that the current TTL (time remaining until the - limit resets) in Redis for a counter is 10 seconds, and we set the ratio to - 2, and the max time for 30s. In this case, the counter will be cached for 5s - (min(10/2, 30)). During those 5s, Limitador will not fetch the value of that - counter from Redis, so it will answer faster, but it will also miss the - updates done by other instances, so it can go over the limits in that 5s - interval. + it's needed, the value is cached for a configurable period. The trade-off is + that when running several instances of Limitador, an instance will not + become aware of the counter updates other instances do while the value is + cached. When a counter is already at 0 (limit exceeded), it's cached until + it expires in Redis. In this case, no matter what other instances do, we + know that the quota will not be reestablished until the key expires in + Redis, so in this case, rate-limit accuracy is not affected. When a counter + has still some quota remaining the situation is different, that's why we can + tune for how long it will be cached. The formula is as follows: + MIN(ttl_in_redis/[`REDIS_LOCAL_CACHE_TTL_RATIO_CACHED_COUNTERS`](#redis_local_cache_ttl_ratio_cached_counters), + [`REDIS_LOCAL_CACHE_MAX_TTL_CACHED_COUNTERS_MS`](#redis_local_cache_max_ttl_cached_counters_ms)). + For example, let's image that the current TTL (time remaining until the + limit resets) in Redis for a counter is 10 seconds, and we set the ratio to + 2, and the max time for 30s. In this case, the counter will be cached for 5s + (min(10/2, 30)). During those 5s, Limitador will not fetch the value of that + counter from Redis, so it will answer faster, but it will also miss the + updates done by other instances, so it can go over the limits in that 5s + interval. - Optional. Disabled by default. - Format: set to "1" to enable. - Note: "REDIS_URL" needs to be set. + #### `REDIS_LOCAL_CACHE_FLUSHING_PERIOD_MS` - Used to configure the local cache when using Redis. See - [`REDIS_LOCAL_CACHE_ENABLED`](#redis_local_cache_enabled). This env only applies - when `"REDIS_LOCAL_CACHE_ENABLED" == 1`. +[`REDIS_LOCAL_CACHE_ENABLED`](#redis_local_cache_enabled). This env only applies +when `"REDIS_LOCAL_CACHE_ENABLED" == 1`. - Optional. Defaults to `1000`. - Format: `integer`. Duration in milliseconds. + #### `REDIS_LOCAL_CACHE_MAX_TTL_CACHED_COUNTERS_MS` - Used to configure the local cache when using Redis. See - [`REDIS_LOCAL_CACHE_ENABLED`](#redis_local_cache_enabled). This env only applies - when `"REDIS_LOCAL_CACHE_ENABLED" == 1`. +[`REDIS_LOCAL_CACHE_ENABLED`](#redis_local_cache_enabled). This env only applies +when `"REDIS_LOCAL_CACHE_ENABLED" == 1`. - Optional. Defaults to `5000`. - Format: `integer`. Duration in milliseconds. + #### `REDIS_LOCAL_CACHE_TTL_RATIO_CACHED_COUNTERS` - Used to configure the local cache when using Redis. See - [`REDIS_LOCAL_CACHE_ENABLED`](#redis_local_cache_enabled). This env only applies - when `"REDIS_LOCAL_CACHE_ENABLED" == 1`. +[`REDIS_LOCAL_CACHE_ENABLED`](#redis_local_cache_enabled). This env only applies +when `"REDIS_LOCAL_CACHE_ENABLED" == 1`. - Optional. Defaults to `10`. - Format: `integer`. + #### `REDIS_URL` - Redis URL. Required only when you want to use Redis to store the limits. - Optional. By default, Limitador stores the limits in memory and does not - require Redis. +require Redis. - Format: `string`, URL in the format of `"redis://127.0.0.1:6379"`. + #### `RUST_LOG` - Defines the log level. - Optional. Defaults to `"error"`. - Format: `enum`: `"debug"`, `"error"`, `"info"`, `"warn"`, or `"trace"`. + ### When built with the `infinispan` feature - _experimental_ #### `INFINISPAN_CACHE_NAME` @@ -414,6 +425,7 @@ environment variables. - Optional. By default, Limitador will use a cache called `"limitador"`. - Format: `string`. + #### `INFINISPAN_COUNTERS_CONSISTENCY` - Defines the consistency mode for the Infinispan counters created by Limitador. @@ -421,6 +433,7 @@ environment variables. - Optional. Defaults to `"strong"`. - Format: `enum`: `"Strong"` or `"Weak"`. + #### `INFINISPAN_URL` - Infinispan URL. Required only when you want to use Infinispan to store the @@ -429,12 +442,12 @@ environment variables. require Infinispan. - Format: `URL`, in the format of `http://username:password@127.0.0.1:11222`. + #### `RATE_LIMIT_HEADERS` - Enables rate limit response headers. Only supported by the RLS server. - Optional. Defaults to `"NONE"`. - Must be one of: - - `"NONE"` - Does not add any additional headers to the http response. - - `"DRAFT_VERSION_03"`. Adds response headers - per https://datatracker.ietf.org/doc/id/draft-polli-ratelimit-headers-03.html + - `"NONE"` - Does not add any additional headers to the http response. + - `"DRAFT_VERSION_03"`. Adds response headers per https://datatracker.ietf.org/doc/id/draft-polli-ratelimit-headers-03.html diff --git a/limitador-server/README.md b/limitador-server/README.md index 166c27ae..33fe150b 100644 --- a/limitador-server/README.md +++ b/limitador-server/README.md @@ -37,10 +37,8 @@ Options: Include the Limit Name in prometheus label -v... Sets the level of verbosity - --tracing-host - The host for the tracing service - --tracing-port - The port for the tracing service [default: 4317] + --tracing-endpoint + The endpoint for the tracing service --validate Validates the LIMITS_FILE and exits -H, --rate-limit-headers