diff --git a/src/cli.rs b/src/cli.rs index 0251f79e..331dbef2 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -169,7 +169,12 @@ pub(crate) fn build_cli() -> Command { .long("continue-on-errors") .env("KUBEWARDEN_CONTINUE_ON_ERRORS") .action(ArgAction::SetTrue) - .hide(true) + .hide(true), + Arg::new("otlp-endpoint") + .long("otlp-endpoint") + .env("OTEL_EXPORTER_OTLP_ENDPOINT") + .default_value("http://localhost:4317") + .help("The OTLP gRPC endpoint for exporting traces and metrics.") ]; args.sort_by(|a, b| a.get_id().cmp(b.get_id())); diff --git a/src/config.rs b/src/config.rs index 3454da8d..49e5ffc0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -41,6 +41,7 @@ pub struct Config { pub log_level: String, pub log_fmt: String, pub log_no_color: bool, + pub otlp_endpoint: Option, pub daemon: bool, pub enable_pprof: bool, pub daemon_pid_file: String, @@ -125,6 +126,9 @@ impl Config { .get_one::("log-no-color") .expect("clap should have assigned a default value") .to_owned(); + + let otlp_endpoint = matches.get_one::("otlp-endpoint").cloned(); + let (cert_file, key_file) = tls_files(matches)?; let tls_config = if cert_file.is_empty() { None @@ -160,6 +164,7 @@ impl Config { log_level, log_fmt, log_no_color, + otlp_endpoint, daemon, daemon_pid_file, daemon_stdout_file, diff --git a/src/main.rs b/src/main.rs index 045985c2..0fe86cee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,10 +21,15 @@ async fn main() -> Result<()> { let matches = cli::build_cli().get_matches(); let config = policy_server::config::Config::from_args(&matches)?; - setup_tracing(&config.log_level, &config.log_fmt, config.log_no_color)?; + setup_tracing( + &config.log_level, + &config.log_fmt, + config.log_no_color, + config.otlp_endpoint.as_deref(), + )?; if config.metrics_enabled { - setup_metrics()?; + setup_metrics(config.otlp_endpoint.as_deref())?; }; if config.daemon { diff --git a/src/metrics.rs b/src/metrics.rs index d771af5d..3342d775 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -10,18 +10,20 @@ pub use policy_evaluations_latency::record_policy_latency; const METER_NAME: &str = "kubewarden"; -pub fn setup_metrics() -> Result<()> { - let meter_reader = opentelemetry_sdk::metrics::PeriodicReader::builder( - opentelemetry_otlp::MetricExporter::builder() - .with_tonic() - .with_export_config(ExportConfig::default()) - .build()?, - runtime::Tokio, - ) - .build(); +pub fn setup_metrics(otlp_endpoint: Option<&str>) -> Result<()> { + let mut metric_exporter_builder = opentelemetry_otlp::MetricExporter::builder() + .with_tonic() + .with_export_config(ExportConfig::default()); + if let Some(endpoint) = otlp_endpoint { + metric_exporter_builder = metric_exporter_builder.with_endpoint(endpoint); + } + let meter_reader = metric_exporter_builder.build()?; + let periodic_reader = + opentelemetry_sdk::metrics::PeriodicReader::builder(meter_reader, runtime::Tokio).build(); let meter_provider = opentelemetry_sdk::metrics::SdkMeterProvider::builder() - .with_reader(meter_reader) + .with_reader(periodic_reader) .build(); + global::set_meter_provider(meter_provider); Ok(()) } diff --git a/src/tracing.rs b/src/tracing.rs index fdaf06fb..8fc4e098 100644 --- a/src/tracing.rs +++ b/src/tracing.rs @@ -1,5 +1,6 @@ use anyhow::{anyhow, Result}; use opentelemetry::trace::TracerProvider; +use opentelemetry_otlp::WithExportConfig; use tracing_subscriber::prelude::*; use tracing_subscriber::{fmt, EnvFilter}; @@ -7,7 +8,12 @@ use crate::config; // Setup the tracing system. This MUST be done inside of a tokio Runtime // because some collectors rely on it and would panic otherwise. -pub fn setup_tracing(log_level: &str, log_fmt: &str, log_no_color: bool) -> Result<()> { +pub fn setup_tracing( + log_level: &str, + log_fmt: &str, + log_no_color: bool, + otlp_endpoint: Option<&str>, +) -> Result<()> { // setup logging let filter_layer = EnvFilter::new(log_level) // some of our dependencies generate trace events too, but we don't care about them -> @@ -37,11 +43,16 @@ pub fn setup_tracing(log_level: &str, log_fmt: &str, log_no_color: bool) -> Resu "otlp" => { // Create a new OpenTelemetry pipeline sending events to a // OpenTelemetry collector using the OTLP format. - // The collector must run on localhost (eg: use a sidecar inside of k8s) - // using GRPC - let otlp_exporter = opentelemetry_otlp::SpanExporter::builder() - .with_tonic() - .build()?; + // If no endpoint is provided, the default one is used. + // The default endpoint is "http://localhost:4317". + let mut otlp_exporter_builder = + opentelemetry_otlp::SpanExporter::builder().with_tonic(); + + if let Some(endpoint) = otlp_endpoint { + otlp_exporter_builder = otlp_exporter_builder.with_endpoint(endpoint); + } + + let otlp_exporter = otlp_exporter_builder.build()?; let tracer_provider = opentelemetry_sdk::trace::TracerProvider::builder() .with_resource(opentelemetry_sdk::Resource::new(vec![ diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 7b32c9d2..dd9c22a8 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -120,6 +120,7 @@ pub(crate) fn default_test_config() -> Config { log_level: "info".to_owned(), log_fmt: "json".to_owned(), log_no_color: false, + otlp_endpoint: None, daemon: false, daemon_pid_file: "policy_server.pid".to_owned(), daemon_stdout_file: None, diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 1ca34683..d9f69150 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -773,7 +773,7 @@ async fn test_otel() { traces_output_file_path.to_str().unwrap(), "/tmp/traces.json", )) - .with_mapped_port(4317, 4317.into()) + .with_mapped_port(1337, 4317.into()) .with_cmd(vec!["--config=/etc/otel-collector-config.yaml"]) .with_startup_timeout(Duration::from_secs(30)) .start() @@ -783,8 +783,15 @@ async fn test_otel() { let mut config = default_test_config(); config.metrics_enabled = true; config.log_fmt = "otlp".to_string(); - setup_metrics().unwrap(); - setup_tracing(&config.log_level, &config.log_fmt, config.log_no_color).unwrap(); + config.otlp_endpoint = Some("http://localhost:1337".to_string()); + setup_metrics(config.otlp_endpoint.as_deref()).unwrap(); + setup_tracing( + &config.log_level, + &config.log_fmt, + config.log_no_color, + config.otlp_endpoint.as_deref(), + ) + .unwrap(); let app = app(config).await;