Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
This commit is contained in:
parent
766ca99fd5
commit
45bed71c36
6 changed files with 37 additions and 66 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
13
src/lib.rs
13
src/lib.rs
|
@ -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())
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue