From 9e3b3567b26e3db11a92b50448bd417ed771f48b Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Wed, 28 Sep 2022 10:52:41 +0200 Subject: [PATCH] Gate the prometheus exporter behind a feature Also fixes the Docker image building and the CI builds --- .github/workflows/ci.yaml | 9 +++++ Dockerfile | 3 +- crates/cli/Cargo.toml | 10 +++-- crates/cli/src/telemetry.rs | 80 +++++++++++++++++++++++++------------ 4 files changed, 72 insertions(+), 30 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 60a33c11..828ff3a0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -170,6 +170,9 @@ jobs: profile: minimal override: true + - name: Install Protoc + uses: arduino/setup-protoc@v1 + - name: Setup OPA uses: open-policy-agent/setup-opa@v1 with: @@ -248,6 +251,9 @@ jobs: profile: minimal override: true + - name: Install Protoc + uses: arduino/setup-protoc@v1 + - name: Setup OPA uses: open-policy-agent/setup-opa@v1 with: @@ -329,6 +335,9 @@ jobs: override: true components: llvm-tools-preview + - name: Install Protoc + uses: arduino/setup-protoc@v1 + - name: Setup OPA uses: open-policy-agent/setup-opa@v1 with: diff --git a/Dockerfile b/Dockerfile index 5a272718..cbfc50dd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -66,7 +66,8 @@ RUN \ g++-aarch64-linux-gnu \ g++-x86-64-linux-gnu \ libc6-dev-arm64-cross \ - libc6-dev-amd64-cross + libc6-dev-amd64-cross \ + protobuf-compiler WORKDIR /app RUN \ diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index e1720f84..475b060a 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -31,8 +31,8 @@ opentelemetry-jaeger = { version = "0.17.0", features = ["rt-tokio", "collector_ opentelemetry-otlp = { version = "0.11.0", features = ["trace", "metrics", "http-proto"], optional = true } opentelemetry-zipkin = { version = "0.16.0", features = ["opentelemetry-http"], default-features = false, optional = true } opentelemetry-http = { version = "0.7.0", features = ["tokio", "hyper"], optional = true } -opentelemetry-prometheus = "0.11.0" -prometheus = "0.13.2" +opentelemetry-prometheus = { version = "0.11.0", optional = true } +prometheus = { version = "0.13.2", optional = true } mas-config = { path = "../config" } mas-email = { path = "../email" } @@ -49,7 +49,7 @@ mas-templates = { path = "../templates" } indoc = "1.0.7" [features] -default = ["otlp", "jaeger", "zipkin", "native-roots"] +default = ["otlp", "jaeger", "zipkin", "prometheus", "native-roots"] # Use the native root certificates native-roots = ["mas-http/native-roots", "mas-handlers/native-roots"] @@ -59,9 +59,11 @@ webpki-roots = ["mas-http/webpki-roots", "mas-handlers/webpki-roots"] # Read the builtin static files and templates from the source directory dev = ["mas-templates/dev", "mas-static-files/dev"] -# Enable OpenTelemetry OTLP exporter. Requires "protoc" +# Enable OpenTelemetry OTLP exporter. otlp = ["dep:opentelemetry-otlp", "dep:opentelemetry-http"] # Enable OpenTelemetry Jaeger exporter and propagator. jaeger = ["dep:opentelemetry-jaeger"] # Enable OpenTelemetry Zipkin exporter and B3 propagator. zipkin = ["dep:opentelemetry-zipkin", "dep:opentelemetry-http"] +# Enable OpenTelemetry Prometheus exporter. Requires "protoc" +prometheus = ["dep:opentelemetry-prometheus", "dep:prometheus"] diff --git a/crates/cli/src/telemetry.rs b/crates/cli/src/telemetry.rs index 384e1507..aacc84a9 100644 --- a/crates/cli/src/telemetry.rs +++ b/crates/cli/src/telemetry.rs @@ -12,14 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::{ - convert::Infallible, - net::{SocketAddr, TcpListener}, - time::Duration, -}; +use std::time::Duration; use anyhow::{bail, Context as _}; -use hyper::{header::CONTENT_TYPE, service::make_service_fn, Body, Method, Request, Response}; use mas_config::{ JaegerExporterProtocolConfig, MetricsExporterConfig, Propagator, TelemetryConfig, TracingExporterConfig, @@ -41,10 +36,7 @@ use opentelemetry_jaeger::Propagator as JaegerPropagator; use opentelemetry_semantic_conventions as semcov; #[cfg(feature = "zipkin")] use opentelemetry_zipkin::{B3Encoding, Propagator as ZipkinPropagator}; -use prometheus::{Encoder, TextEncoder}; use tokio::sync::OnceCell; -use tower::service_fn; -use tracing::info; use url::Url; static METRICS_BASIC_CONTROLLER: OnceCell> = OnceCell::const_new(); @@ -60,8 +52,10 @@ pub async fn setup( mas_http::set_propagator(&propagator); global::set_text_map_propagator(propagator); - let tracer = tracer(&config.tracing.exporter).await?; - let meter = meter(&config.metrics.exporter)?; + let tracer = tracer(&config.tracing.exporter) + .await + .context("Failed to configure traces exporter")?; + let meter = meter(&config.metrics.exporter).context("Failed to configure metrics exporter")?; METRICS_BASIC_CONTROLLER.set(meter.clone())?; Ok((tracer, meter)) @@ -97,8 +91,7 @@ fn match_propagator( ))), p => bail!( - "The service was compiled without support for the {:?} propagator, but config uses it.", - p + "The service was compiled without support for the {p:?} propagator, but config uses it.", ), } } @@ -112,7 +105,9 @@ fn propagator(propagators: &[Propagator]) -> anyhow::Result anyhow::Result { - let client = mas_http::make_untraced_client().await?; + let client = mas_http::make_untraced_client() + .await + .context("Failed to build HTTP client used by telemetry exporter")?; let client = opentelemetry_http::hyper::HyperClient::new_with_timeout(client, Duration::from_secs(30)); Ok(client) @@ -138,18 +133,22 @@ fn otlp_tracer(endpoint: &Option) -> anyhow::Result { .tracing() .with_exporter(exporter) .with_trace_config(trace_config()) - .install_batch(opentelemetry::runtime::Tokio)?; + .install_batch(opentelemetry::runtime::Tokio) + .context("Failed to configure OTLP trace exporter")?; Ok(tracer) } #[cfg(not(feature = "otlp"))] -fn otlp_tracer(_endpoint: &Option) -> anyhow::Result { +fn otlp_tracer(endpoint: &Option) -> anyhow::Result { + let _ = endpoint; anyhow::bail!("The service was compiled without OTLP exporter support, but config exports traces via OTLP.") } #[cfg(not(feature = "jaeger"))] fn jaeger_agent_tracer(host: &str, port: u16) -> anyhow::Result { + let _ = host; + let _ = port; anyhow::bail!("The service was compiled without Jaeger exporter support, but config exports traces via Jaeger.") } @@ -160,7 +159,9 @@ fn jaeger_agent_tracer(host: &str, port: u16) -> anyhow::Result { .with_trace_config(trace_config()) .with_endpoint((host, port)); - let tracer = pipeline.install_batch(opentelemetry::runtime::Tokio)?; + let tracer = pipeline + .install_batch(opentelemetry::runtime::Tokio) + .context("Failed to configure Jaeger agent exporter")?; Ok(tracer) } @@ -171,6 +172,10 @@ async fn jaeger_collector_tracer( username: Option<&str>, password: Option<&str>, ) -> anyhow::Result { + let _ = endpoint; + let _ = username; + let _ = password; + futures_util::future::ready(()).await; // Silence the "unused async" lint anyhow::bail!("The service was compiled without Jaeger exporter support, but config exports traces via Jaeger.") } @@ -195,13 +200,17 @@ async fn jaeger_collector_tracer( pipeline = pipeline.with_password(password); } - let tracer = pipeline.install_batch(opentelemetry::runtime::Tokio)?; + let tracer = pipeline + .install_batch(opentelemetry::runtime::Tokio) + .context("Failed to configure Jaeger collector exporter")?; Ok(tracer) } #[cfg(not(feature = "zipkin"))] -async fn zipkin_tracer(_collector_endpoint: &Option) -> anyhow::Result { +async fn zipkin_tracer(collector_endpoint: &Option) -> anyhow::Result { + let _ = collector_endpoint; + futures_util::future::ready(()).await; // Silence the "unused async" lint anyhow::bail!("The service was compiled without Jaeger exporter support, but config exports traces via Jaeger.") } @@ -218,7 +227,9 @@ async fn zipkin_tracer(collector_endpoint: &Option) -> anyhow::Result) -> anyhow::Result { ) .with_resource(resource()) .with_exporter(exporter) - .build()?; + .build() + .context("Failed to configure OTLP metrics exporter")?; Ok(controller) } #[cfg(not(feature = "otlp"))] -fn otlp_meter(_endpoint: &Option) -> anyhow::Result { +fn otlp_meter(endpoint: &Option) -> anyhow::Result { + let _ = endpoint; anyhow::bail!("The service was compiled without OTLP exporter support, but config exports metrics via OTLP.") } @@ -289,7 +302,22 @@ fn stdout_meter() -> anyhow::Result { Ok(controller) } +#[cfg(not(feature = "prometheus"))] fn prometheus_meter(address: &str) -> anyhow::Result { + let _ = address; + anyhow::bail!("The service was compiled without Prometheus exporter support, but config exports metrics via Prometheus.") +} + +#[cfg(feature = "prometheus")] +fn prometheus_meter(address: &str) -> anyhow::Result { + use std::{ + convert::Infallible, + net::{SocketAddr, TcpListener}, + }; + + use hyper::{header::CONTENT_TYPE, service::make_service_fn, Body, Method, Request, Response}; + use prometheus::{Encoder, TextEncoder}; + let controller = sdk::metrics::controllers::basic( sdk::metrics::processors::factory( sdk::metrics::selectors::simple::histogram([ @@ -307,7 +335,7 @@ fn prometheus_meter(address: &str) -> anyhow::Result { let make_svc = make_service_fn(move |_conn| { let exporter = exporter.clone(); async move { - Ok::<_, Infallible>(service_fn(move |req: Request| { + Ok::<_, Infallible>(tower::service_fn(move |req: Request| { let exporter = exporter.clone(); async move { let response = match (req.method(), req.uri().path()) { @@ -340,12 +368,14 @@ fn prometheus_meter(address: &str) -> anyhow::Result { .context("could not parse listener address")?; let listener = TcpListener::bind(address).context("could not bind address")?; - info!( + tracing::info!( "Prometheus exporter listening on on http://{}/metrics", listener.local_addr().unwrap() ); - let server = hyper::server::Server::from_tcp(listener)?.serve(make_svc); + let server = hyper::server::Server::from_tcp(listener) + .context("Failed to start HTTP server for the Prometheus metrics exporter")? + .serve(make_svc); tokio::spawn(server); Ok(controller)