Include prometheus in default build, remove build feature (fixes #3558) (#4071)

Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
This commit is contained in:
Nutomic 2023-10-25 12:54:58 +02:00 committed by GitHub
parent 766ca99fd5
commit 45bed71c36
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 37 additions and 66 deletions

5
Cargo.lock generated
View file

@ -10,8 +10,9 @@ checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
[[package]] [[package]]
name = "activitypub_federation" name = "activitypub_federation"
version = "0.5.0-beta.3" version = "0.5.0-beta.4"
source = "git+https://github.com/LemmyNet/activitypub-federation-rust.git?branch=webfinger-alphabets#071218396b2b1254e12ad061362befe0f17e76c9" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a122cf2c2adf45b164134946bc069659cd93083fab294839a3f1d794b707c17"
dependencies = [ dependencies = [
"activitystreams-kinds", "activitystreams-kinds",
"actix-web", "actix-web",

View file

@ -41,7 +41,6 @@ console = [
"reqwest-tracing/opentelemetry_0_16", "reqwest-tracing/opentelemetry_0_16",
] ]
json-log = ["tracing-subscriber/json"] json-log = ["tracing-subscriber/json"]
prometheus-metrics = ["prometheus", "actix-web-prom"]
default = [] default = []
[workspace] [workspace]
@ -70,7 +69,7 @@ lemmy_routes = { version = "=0.19.0-rc.3", path = "./crates/routes" }
lemmy_db_views = { version = "=0.19.0-rc.3", path = "./crates/db_views" } lemmy_db_views = { version = "=0.19.0-rc.3", path = "./crates/db_views" }
lemmy_db_views_actor = { version = "=0.19.0-rc.3", path = "./crates/db_views_actor" } lemmy_db_views_actor = { version = "=0.19.0-rc.3", path = "./crates/db_views_actor" }
lemmy_db_views_moderator = { version = "=0.19.0-rc.3", path = "./crates/db_views_moderator" } lemmy_db_views_moderator = { version = "=0.19.0-rc.3", path = "./crates/db_views_moderator" }
activitypub_federation = { git = "https://github.com/LemmyNet/activitypub-federation-rust.git", branch = "webfinger-alphabets", default-features = false, features = [ activitypub_federation = { version = "0.5.0-beta.4", default-features = false, features = [
"actix-web", "actix-web",
] } ] }
diesel = "2.1.3" diesel = "2.1.3"
@ -169,8 +168,8 @@ futures-util = { workspace = true }
tokio-postgres = { workspace = true } tokio-postgres = { workspace = true }
tokio-postgres-rustls = { workspace = true } tokio-postgres-rustls = { workspace = true }
chrono = { workspace = true } chrono = { workspace = true }
prometheus = { version = "0.13.3", features = ["process"], optional = true } prometheus = { version = "0.13.3", features = ["process"] }
actix-web-prom = { version = "0.6.0", optional = true } actix-web-prom = { version = "0.6.0" }
serial_test = { workspace = true } serial_test = { workspace = true }
clap = { version = "4.4.7", features = ["derive"] } clap = { version = "4.4.7", features = ["derive"] }
actix-web-httpauth = "0.8.1" actix-web-httpauth = "0.8.1"

View file

@ -74,7 +74,7 @@ impl<Kind: Id + DeserializeOwned + Send> IdOrNestedObject<Kind> {
pub(crate) async fn object(self, context: &Data<LemmyContext>) -> Result<Kind, LemmyError> { pub(crate) async fn object(self, context: &Data<LemmyContext>) -> Result<Kind, LemmyError> {
match self { match self {
// TODO: move IdOrNestedObject struct to library and make fetch_object_http private // TODO: move IdOrNestedObject struct to library and make fetch_object_http private
IdOrNestedObject::Id(i) => Ok(fetch_object_http(&i, context).await?), IdOrNestedObject::Id(i) => Ok(fetch_object_http(&i, context).await?.object),
IdOrNestedObject::NestedObject(o) => Ok(o), IdOrNestedObject::NestedObject(o) => Ok(o),
} }
} }

View file

@ -170,11 +170,11 @@ pub struct SetupConfig {
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct PrometheusConfig { pub struct PrometheusConfig {
// Address that the Prometheus metrics will be served on. // Address that the Prometheus metrics will be served on.
#[default(Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))))] #[default(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)))]
#[doku(example = "127.0.0.1")] #[doku(example = "127.0.0.1")]
pub bind: Option<IpAddr>, pub bind: IpAddr,
// Port that the Prometheus metrics will be served on. // Port that the Prometheus metrics will be served on.
#[default(Some(10002))] #[default(10002)]
#[doku(example = "10002")] #[doku(example = "10002")]
pub port: Option<i32>, pub port: i32,
} }

View file

@ -1,6 +1,5 @@
pub mod api_routes_http; pub mod api_routes_http;
pub mod code_migrations; pub mod code_migrations;
#[cfg(feature = "prometheus-metrics")]
pub mod prometheus_metrics; pub mod prometheus_metrics;
pub mod root_span_builder; pub mod root_span_builder;
pub mod scheduled_tasks; pub mod scheduled_tasks;
@ -52,6 +51,7 @@ use lemmy_utils::{
response::jsonify_plain_text_errors, response::jsonify_plain_text_errors,
settings::{structs::Settings, SETTINGS}, settings::{structs::Settings, SETTINGS},
}; };
use prometheus_metrics::serve_prometheus;
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use reqwest_tracing::TracingMiddleware; use reqwest_tracing::TracingMiddleware;
use serde_json::json; use serde_json::json;
@ -63,12 +63,6 @@ use tracing_error::ErrorLayer;
use tracing_log::LogTracer; use tracing_log::LogTracer;
use tracing_subscriber::{filter::Targets, layer::SubscriberExt, Layer, Registry}; use tracing_subscriber::{filter::Targets, layer::SubscriberExt, Layer, Registry};
use url::Url; use url::Url;
#[cfg(feature = "prometheus-metrics")]
use {
actix_web_prom::PrometheusMetricsBuilder,
prometheus::default_registry,
prometheus_metrics::serve_prometheus,
};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command( #[command(
@ -173,8 +167,9 @@ pub async fn start_lemmy_server(args: CmdArgs) -> Result<(), LemmyError> {
let _scheduled_tasks = tokio::task::spawn(scheduled_tasks::setup(context.clone())); let _scheduled_tasks = tokio::task::spawn(scheduled_tasks::setup(context.clone()));
} }
#[cfg(feature = "prometheus-metrics")] if let Some(prometheus) = SETTINGS.prometheus.clone() {
serve_prometheus(SETTINGS.prometheus.as_ref(), context.clone()); serve_prometheus(prometheus, context.clone())?;
}
let federation_config = FederationConfig::builder() let federation_config = FederationConfig::builder()
.domain(SETTINGS.hostname.clone()) .domain(SETTINGS.hostname.clone())

View file

@ -1,14 +1,9 @@
// TODO: should really not unwrap everywhere here.... use actix_web::{rt::System, web, App, HttpServer};
#![allow(clippy::unwrap_used)]
use actix_web::{rt::System, web, App, HttpResponse, HttpServer, Responder};
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_utils::settings::structs::PrometheusConfig; use lemmy_utils::{error::LemmyResult, settings::structs::PrometheusConfig};
use prometheus::{default_registry, Encoder, Gauge, Opts, TextEncoder}; use prometheus::{default_registry, Encoder, Gauge, Opts, TextEncoder};
use std::{ use std::{sync::Arc, thread};
net::{IpAddr, Ipv4Addr}, use tracing::error;
sync::Arc,
thread,
};
struct PromContext { struct PromContext {
lemmy: LemmyContext, lemmy: LemmyContext,
@ -21,23 +16,12 @@ struct DbPoolMetrics {
available: Gauge, available: Gauge,
} }
static DEFAULT_BIND: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); pub fn serve_prometheus(config: PrometheusConfig, lemmy_context: LemmyContext) -> LemmyResult<()> {
static DEFAULT_PORT: i32 = 10002;
pub fn serve_prometheus(config: Option<&PrometheusConfig>, lemmy_context: LemmyContext) {
let context = Arc::new(PromContext { let context = Arc::new(PromContext {
lemmy: lemmy_context, lemmy: lemmy_context,
db_pool_metrics: create_db_pool_metrics(), db_pool_metrics: create_db_pool_metrics()?,
}); });
let (bind, port) = match config {
Some(config) => (
config.bind.unwrap_or(DEFAULT_BIND),
config.port.unwrap_or(DEFAULT_PORT),
),
None => (DEFAULT_BIND, DEFAULT_PORT),
};
// spawn thread that blocks on handling requests // spawn thread that blocks on handling requests
// only mapping /metrics to a handler // only mapping /metrics to a handler
thread::spawn(move || { thread::spawn(move || {
@ -48,19 +32,20 @@ pub fn serve_prometheus(config: Option<&PrometheusConfig>, lemmy_context: LemmyC
.app_data(web::Data::new(Arc::clone(&context))) .app_data(web::Data::new(Arc::clone(&context)))
.route("/metrics", web::get().to(metrics)) .route("/metrics", web::get().to(metrics))
}) })
.bind((bind, port as u16)) .bind((config.bind, config.port as u16))
.unwrap_or_else(|_| panic!("Cannot bind to {}:{}", bind, port)) .unwrap_or_else(|e| panic!("Cannot bind to {}:{}: {e}", config.bind, config.port))
.run(); .run();
if let Err(err) = server.await { if let Err(err) = server.await {
eprintln!("Prometheus server error: {}", err); error!("Prometheus server error: {err}");
} }
}) })
}); });
Ok(())
} }
// handler for the /metrics path // handler for the /metrics path
async fn metrics(context: web::Data<Arc<PromContext>>) -> impl Responder { async fn metrics(context: web::Data<Arc<PromContext>>) -> LemmyResult<String> {
// collect metrics // collect metrics
collect_db_pool_metrics(&context).await; collect_db_pool_metrics(&context).await;
@ -69,43 +54,34 @@ async fn metrics(context: web::Data<Arc<PromContext>>) -> impl Responder {
// gather metrics from registry and encode in prometheus format // gather metrics from registry and encode in prometheus format
let metric_families = prometheus::gather(); let metric_families = prometheus::gather();
encoder.encode(&metric_families, &mut buffer).unwrap(); encoder.encode(&metric_families, &mut buffer)?;
let output = String::from_utf8(buffer).unwrap(); let output = String::from_utf8(buffer)?;
HttpResponse::Ok().body(output) Ok(output)
} }
// create lemmy_db_pool_* metrics and register them with the default registry // create lemmy_db_pool_* metrics and register them with the default registry
fn create_db_pool_metrics() -> DbPoolMetrics { fn create_db_pool_metrics() -> LemmyResult<DbPoolMetrics> {
let metrics = DbPoolMetrics { let metrics = DbPoolMetrics {
max_size: Gauge::with_opts(Opts::new( max_size: Gauge::with_opts(Opts::new(
"lemmy_db_pool_max_connections", "lemmy_db_pool_max_connections",
"Maximum number of connections in the pool", "Maximum number of connections in the pool",
)) ))?,
.unwrap(),
size: Gauge::with_opts(Opts::new( size: Gauge::with_opts(Opts::new(
"lemmy_db_pool_connections", "lemmy_db_pool_connections",
"Current number of connections in the pool", "Current number of connections in the pool",
)) ))?,
.unwrap(),
available: Gauge::with_opts(Opts::new( available: Gauge::with_opts(Opts::new(
"lemmy_db_pool_available_connections", "lemmy_db_pool_available_connections",
"Number of available connections in the pool", "Number of available connections in the pool",
)) ))?,
.unwrap(),
}; };
default_registry() default_registry().register(Box::new(metrics.max_size.clone()))?;
.register(Box::new(metrics.max_size.clone())) default_registry().register(Box::new(metrics.size.clone()))?;
.unwrap(); default_registry().register(Box::new(metrics.available.clone()))?;
default_registry()
.register(Box::new(metrics.size.clone()))
.unwrap();
default_registry()
.register(Box::new(metrics.available.clone()))
.unwrap();
metrics Ok(metrics)
} }
async fn collect_db_pool_metrics(context: &PromContext) { async fn collect_db_pool_metrics(context: &PromContext) {