diff --git a/Cargo.lock b/Cargo.lock index 27c9ec4bd..05341794c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -629,29 +629,6 @@ dependencies = [ "syn 2.0.38", ] -[[package]] -name = "axum-prometheus" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97def327c5481791abb57ac295bfc70f2e1a0727675b7dbf74bd1b27a72b6fd8" -dependencies = [ - "axum", - "axum-core", - "bytes", - "futures", - "futures-core", - "http", - "http-body", - "matchit", - "metrics", - "metrics-exporter-prometheus", - "once_cell", - "pin-project", - "tokio", - "tower", - "tower-http", -] - [[package]] name = "backtrace" version = "0.3.69" @@ -2621,12 +2598,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "ipnet" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" - [[package]] name = "iri-string" version = "0.7.0" @@ -2765,7 +2736,6 @@ dependencies = [ "axum", "axum-extra 0.8.0", "axum-flash", - "axum-prometheus", "bytes", "chrono", "clap", @@ -2783,6 +2753,7 @@ dependencies = [ "iso8601-timestamp", "kitsune-cache", "kitsune-captcha", + "kitsune-config", "kitsune-core", "kitsune-db", "kitsune-embed", @@ -2790,14 +2761,12 @@ dependencies = [ "kitsune-http-signatures", "kitsune-job-runner", "kitsune-language", + "kitsune-observability", "kitsune-search", "kitsune-storage", "kitsune-test", "kitsune-type", "metrics", - "metrics-exporter-prometheus", - "metrics-tracing-context", - "metrics-util", "mimalloc", "mime", "mime_guess", @@ -2822,8 +2791,6 @@ dependencies = [ "tower", "tower-http", "tracing", - "tracing-error", - "tracing-subscriber", "typed-builder", "url", "utoipa", @@ -2881,6 +2848,17 @@ dependencies = [ "vergen", ] +[[package]] +name = "kitsune-config" +version = "0.0.1-pre.3" +dependencies = [ + "eyre", + "serde", + "smol_str", + "tokio", + "toml 0.8.2", +] + [[package]] name = "kitsune-core" version = "0.0.1-pre.3" @@ -2910,6 +2888,7 @@ dependencies = [ "iso8601-timestamp", "kitsune-cache", "kitsune-captcha", + "kitsune-config", "kitsune-db", "kitsune-email", "kitsune-embed", @@ -2944,7 +2923,6 @@ dependencies = [ "thiserror", "time", "tokio", - "toml 0.8.2", "tower", "tracing", "typed-builder", @@ -3050,8 +3028,10 @@ dependencies = [ "clap", "color-eyre", "deadpool-redis", + "kitsune-config", "kitsune-core", "kitsune-db", + "kitsune-observability", "kitsune-retry-policies", "mimalloc", "tokio", @@ -3088,6 +3068,28 @@ dependencies = [ "tracing", ] +[[package]] +name = "kitsune-observability" +version = "0.0.1-pre.3" +dependencies = [ + "async-trait", + "eyre", + "hyper", + "kitsune-config", + "kitsune-http-client", + "metrics", + "metrics-opentelemetry", + "metrics-tracing-context", + "metrics-util", + "opentelemetry 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "opentelemetry-http", + "opentelemetry-otlp", + "tracing", + "tracing-error", + "tracing-opentelemetry", + "tracing-subscriber", +] + [[package]] name = "kitsune-retry-policies" version = "0.0.1-pre.3" @@ -3516,24 +3518,6 @@ dependencies = [ "portable-atomic", ] -[[package]] -name = "metrics-exporter-prometheus" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a4964177ddfdab1e3a2b37aec7cf320e14169abb0ed73999f558136409178d5" -dependencies = [ - "base64 0.21.4", - "hyper", - "indexmap 1.9.3", - "ipnet", - "metrics", - "metrics-util", - "quanta", - "thiserror", - "tokio", - "tracing", -] - [[package]] name = "metrics-macros" version = "0.7.0" @@ -3545,6 +3529,16 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "metrics-opentelemetry" +version = "0.1.0" +source = "git+https://github.com/aumetra/metrics-opentelemetry.git?rev=7c3176266c215bb9a7cbc31b3c32f75a22824928#7c3176266c215bb9a7cbc31b3c32f75a22824928" +dependencies = [ + "metrics", + "opentelemetry_api", + "tracing", +] + [[package]] name = "metrics-tracing-context" version = "0.14.0" @@ -3947,6 +3941,108 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "opentelemetry" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9591d937bc0e6d2feb6f71a559540ab300ea49955229c347a517a28d27784c54" +dependencies = [ + "opentelemetry_api", + "opentelemetry_sdk", +] + +[[package]] +name = "opentelemetry" +version = "0.20.0" +source = "git+https://github.com/open-telemetry/opentelemetry-rust.git?rev=41e8d63652b323866c03981b4b2ca62b9b8d6d44#41e8d63652b323866c03981b4b2ca62b9b8d6d44" +dependencies = [ + "opentelemetry_api", + "opentelemetry_sdk", +] + +[[package]] +name = "opentelemetry-http" +version = "0.9.0" +source = "git+https://github.com/open-telemetry/opentelemetry-rust.git?rev=41e8d63652b323866c03981b4b2ca62b9b8d6d44#41e8d63652b323866c03981b4b2ca62b9b8d6d44" +dependencies = [ + "async-trait", + "bytes", + "http", + "opentelemetry_api", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.13.0" +source = "git+https://github.com/open-telemetry/opentelemetry-rust.git?rev=41e8d63652b323866c03981b4b2ca62b9b8d6d44#41e8d63652b323866c03981b4b2ca62b9b8d6d44" +dependencies = [ + "async-trait", + "futures-core", + "http", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry-semantic-conventions", + "opentelemetry_api", + "opentelemetry_sdk", + "prost", + "thiserror", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.3.0" +source = "git+https://github.com/open-telemetry/opentelemetry-rust.git?rev=41e8d63652b323866c03981b4b2ca62b9b8d6d44#41e8d63652b323866c03981b4b2ca62b9b8d6d44" +dependencies = [ + "opentelemetry_api", + "opentelemetry_sdk", + "prost", + "tonic", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.12.0" +source = "git+https://github.com/open-telemetry/opentelemetry-rust.git?rev=41e8d63652b323866c03981b4b2ca62b9b8d6d44#41e8d63652b323866c03981b4b2ca62b9b8d6d44" +dependencies = [ + "opentelemetry 0.20.0 (git+https://github.com/open-telemetry/opentelemetry-rust.git?rev=41e8d63652b323866c03981b4b2ca62b9b8d6d44)", +] + +[[package]] +name = "opentelemetry_api" +version = "0.20.0" +source = "git+https://github.com/open-telemetry/opentelemetry-rust.git?rev=41e8d63652b323866c03981b4b2ca62b9b8d6d44#41e8d63652b323866c03981b4b2ca62b9b8d6d44" +dependencies = [ + "futures-channel", + "futures-util", + "indexmap 2.0.2", + "js-sys", + "once_cell", + "pin-project-lite", + "thiserror", + "urlencoding", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.20.0" +source = "git+https://github.com/open-telemetry/opentelemetry-rust.git?rev=41e8d63652b323866c03981b4b2ca62b9b8d6d44#41e8d63652b323866c03981b4b2ca62b9b8d6d44" +dependencies = [ + "async-trait", + "crossbeam-channel", + "futures-channel", + "futures-executor", + "futures-util", + "once_cell", + "opentelemetry_api", + "ordered-float 3.9.2", + "percent-encoding", + "rand", + "regex", + "thiserror", + "tokio", + "tokio-stream", +] + [[package]] name = "ordered-float" version = "2.10.1" @@ -4464,6 +4560,29 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "psm" version = "0.1.21" @@ -5897,6 +6016,28 @@ dependencies = [ "winnow", ] +[[package]] +name = "tonic" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" +dependencies = [ + "async-trait", + "base64 0.21.4", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "percent-encoding", + "pin-project", + "prost", + "tokio-stream", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" version = "0.4.13" @@ -6019,6 +6160,20 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-opentelemetry" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75327c6b667828ddc28f5e3f169036cb793c3f588d83bf0f262a7f062ffed3c8" +dependencies = [ + "once_cell", + "opentelemetry 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "opentelemetry_sdk", + "tracing", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "tracing-subscriber" version = "0.3.17" @@ -6162,6 +6317,12 @@ dependencies = [ "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf-8" version = "0.7.6" diff --git a/Cargo.toml b/Cargo.toml index 871fb7d1f..63a1d4a2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ exclude = ["xtask"] members = [ "crates/kitsune-cache", "crates/kitsune-captcha", + "crates/kitsune-config", "crates/kitsune-core", "crates/kitsune-db", "crates/kitsune-email", @@ -22,6 +23,7 @@ members = [ "crates/kitsune-http-signatures", "crates/kitsune-language", "crates/kitsune-messaging", + "crates/kitsune-observability", "crates/kitsune-retry-policies", "crates/kitsune-search", "crates/kitsune-storage", @@ -41,4 +43,10 @@ edition = "2021" version = "0.0.1-pre.3" [patch.crates-io] +# Patch the opentelemetry crate versions to get rid of a few tonic dependencies +opentelemetry_api = { git = "https://github.com/open-telemetry/opentelemetry-rust.git", rev = "41e8d63652b323866c03981b4b2ca62b9b8d6d44" } +opentelemetry-http = { git = "https://github.com/open-telemetry/opentelemetry-rust.git", rev = "41e8d63652b323866c03981b4b2ca62b9b8d6d44" } +opentelemetry-otlp = { git = "https://github.com/open-telemetry/opentelemetry-rust.git", rev = "41e8d63652b323866c03981b4b2ca62b9b8d6d44" } +opentelemetry_sdk = { git = "https://github.com/open-telemetry/opentelemetry-rust.git", rev = "41e8d63652b323866c03981b4b2ca62b9b8d6d44" } + redis = { git = "https://github.com/aumetra/redis-rs.git", rev = "3c4ee09d432a69e1d87d66dcba14c519467c9b81" } diff --git a/config.docker.toml b/config.docker.toml index 11e4d51b4..e9fd859d0 100644 --- a/config.docker.toml +++ b/config.docker.toml @@ -27,7 +27,6 @@ frontend-dir = "./kitsune-fe/dist" max-upload-size = 5242880 # 5MB media-proxy-enabled = false port = 5000 -prometheus-port = 9000 request-timeout-secs = 60 [search] @@ -39,4 +38,4 @@ upload-dir = "./uploads" [url] scheme = "http" -domain = "localhost:5000" \ No newline at end of file +domain = "localhost:5000" diff --git a/config.example.toml b/config.example.toml index d3e1ee596..54d949408 100644 --- a/config.example.toml +++ b/config.example.toml @@ -27,7 +27,6 @@ frontend-dir = "./kitsune-fe/dist" max-upload-size = 5242880 # 5MB media-proxy-enabled = false port = 5000 -prometheus-port = 9000 request-timeout-secs = 60 [search] diff --git a/crates/kitsune-config/Cargo.toml b/crates/kitsune-config/Cargo.toml new file mode 100644 index 000000000..058a279e4 --- /dev/null +++ b/crates/kitsune-config/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "kitsune-config" +edition.workspace = true +version.workspace = true + +[dependencies] +eyre = "0.6.8" +serde = { version = "1.0.189", features = ["derive"] } +smol_str = { version = "0.2.0", features = ["serde"] } +tokio = { version = "1.33.0", features = ["fs"] } +toml = { version = "0.8.2", default-features = false, features = ["parse"] } diff --git a/crates/kitsune-core/src/config.rs b/crates/kitsune-config/src/lib.rs similarity index 96% rename from crates/kitsune-core/src/config.rs rename to crates/kitsune-config/src/lib.rs index b5f7eec36..3b69eaa2f 100644 --- a/crates/kitsune-core/src/config.rs +++ b/crates/kitsune-config/src/lib.rs @@ -117,6 +117,12 @@ pub struct MeiliSearchConfiguration { pub api_key: SmolStr, } +#[derive(Clone, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case")] +pub struct OpenTelemetryConfiguration { + pub http_endpoint: String, +} + #[derive(Clone, Deserialize, Serialize)] #[serde(rename_all = "kebab-case", tag = "type")] pub enum SearchConfiguration { @@ -133,7 +139,6 @@ pub struct ServerConfiguration { pub media_proxy_enabled: bool, pub oidc: Option, pub port: u16, - pub prometheus_port: u16, pub request_timeout_secs: u64, } @@ -179,6 +184,7 @@ pub struct Configuration { pub instance: InstanceConfiguration, pub job_queue: JobQueueConfiguration, pub messaging: MessagingConfiguration, + pub opentelemetry: Option, pub server: ServerConfiguration, pub search: SearchConfiguration, pub storage: StorageConfiguration, diff --git a/crates/kitsune-core/Cargo.toml b/crates/kitsune-core/Cargo.toml index a37af9816..12e8eee37 100644 --- a/crates/kitsune-core/Cargo.toml +++ b/crates/kitsune-core/Cargo.toml @@ -10,7 +10,9 @@ async-recursion = "1.0.5" async-stream = "0.3.5" async-trait = "0.1.74" athena = { path = "../../lib/athena" } -autometrics = { version = "0.6.0", default-features = false } +autometrics = { version = "0.6.0", default-features = false, features = [ + "metrics", +] } base64-simd = "0.8.0" bytes = "1.5.0" const_format = "0.2.32" @@ -34,6 +36,7 @@ img-parts = "0.3.0" iso8601-timestamp = "0.2.12" kitsune-cache = { path = "../kitsune-cache" } kitsune-captcha = { path = "../kitsune-captcha" } +kitsune-config = { path = "../kitsune-config" } kitsune-db = { path = "../kitsune-db" } kitsune-email = { path = "../kitsune-email" } kitsune-embed = { path = "../kitsune-embed" } @@ -65,7 +68,6 @@ speedy-uuid = { path = "../../lib/speedy-uuid", features = ["diesel"] } thiserror = "1.0.50" time = "0.3.30" tokio = { version = "1.33.0", features = ["macros", "rt"] } -toml = { version = "0.8.2", default-features = false, features = ["parse"] } tracing = "0.1.40" typed-builder = "0.18.0" url = "2.4.1" @@ -75,7 +77,6 @@ zxcvbn = { version = "2.2.2", default-features = false } default = [] mastodon-api = [] meilisearch = ["kitsune-search/meilisearch"] -metrics = ["autometrics/metrics"] [build-dependencies] vergen = { version = "8.2.5", features = ["build", "git", "gitcl"] } diff --git a/crates/kitsune-core/src/activitypub/fetcher.rs b/crates/kitsune-core/src/activitypub/fetcher.rs index 8e6ea10fe..b8f67f5da 100644 --- a/crates/kitsune-core/src/activitypub/fetcher.rs +++ b/crates/kitsune-core/src/activitypub/fetcher.rs @@ -324,7 +324,6 @@ mod test { use super::MAX_FETCH_DEPTH; use crate::{ activitypub::Fetcher, - config::FederationFilterConfiguration, error::{ApiError, Error}, service::federation_filter::FederationFilterService, webfinger::Webfinger, @@ -335,6 +334,7 @@ mod test { use hyper::{Body, Request, Response, StatusCode, Uri}; use iso8601_timestamp::Timestamp; use kitsune_cache::NoopCache; + use kitsune_config::FederationFilterConfiguration; use kitsune_db::{model::account::Account, schema::accounts}; use kitsune_http_client::Client; use kitsune_search::NoopSearchService; diff --git a/crates/kitsune-core/src/lib.rs b/crates/kitsune-core/src/lib.rs index 7c2d43e5e..50c42a93b 100644 --- a/crates/kitsune-core/src/lib.rs +++ b/crates/kitsune-core/src/lib.rs @@ -14,7 +14,6 @@ extern crate tracing; pub mod activitypub; pub mod blocking; -pub mod config; pub mod consts; pub mod error; pub mod event; @@ -29,10 +28,6 @@ pub mod webfinger; use self::{ activitypub::Fetcher, - config::{ - CacheConfiguration, CaptchaConfiguration, Configuration, EmailConfiguration, - MessagingConfiguration, SearchConfiguration, StorageConfiguration, - }, job::KitsuneContextRepo, resolve::PostResolver, service::{ @@ -48,6 +43,10 @@ use athena::JobQueue; use eyre::Context; use kitsune_cache::{ArcCache, InMemoryCache, NoopCache, RedisCache}; use kitsune_captcha::{hcaptcha::Captcha as HCaptcha, mcaptcha::Captcha as MCaptcha, Captcha}; +use kitsune_config::{ + CacheConfiguration, CaptchaConfiguration, Configuration, EmailConfiguration, + MessagingConfiguration, SearchConfiguration, StorageConfiguration, +}; use kitsune_db::PgPool; use kitsune_email::{ lettre::{message::Mailbox, AsyncSmtpTransport, Tokio1Executor}, diff --git a/crates/kitsune-core/src/resolve/post.rs b/crates/kitsune-core/src/resolve/post.rs index 0d5c9a097..0978b830a 100644 --- a/crates/kitsune-core/src/resolve/post.rs +++ b/crates/kitsune-core/src/resolve/post.rs @@ -78,7 +78,6 @@ mod test { use super::PostResolver; use crate::{ activitypub::Fetcher, - config::FederationFilterConfiguration, job::KitsuneContextRepo, service::{ account::AccountService, attachment::AttachmentService, @@ -92,6 +91,7 @@ mod test { use diesel_async::RunQueryDsl; use hyper::{Body, Request, Response}; use kitsune_cache::NoopCache; + use kitsune_config::FederationFilterConfiguration; use kitsune_db::{model::account::Account, schema::accounts}; use kitsune_http_client::Client; use kitsune_search::NoopSearchService; diff --git a/crates/kitsune-core/src/service/federation_filter.rs b/crates/kitsune-core/src/service/federation_filter.rs index 19e43ee44..f879bba55 100644 --- a/crates/kitsune-core/src/service/federation_filter.rs +++ b/crates/kitsune-core/src/service/federation_filter.rs @@ -1,5 +1,6 @@ -use crate::{config::FederationFilterConfiguration, error::FederationFilterError}; +use crate::error::FederationFilterError; use globset::{Glob, GlobSet, GlobSetBuilder}; +use kitsune_config::FederationFilterConfiguration; use kitsune_type::ap::{actor::Actor, Activity, Object}; use std::sync::Arc; use url::Url; diff --git a/crates/kitsune-http-client/src/lib.rs b/crates/kitsune-http-client/src/lib.rs index 3188ef832..9e8258caf 100644 --- a/crates/kitsune-http-client/src/lib.rs +++ b/crates/kitsune-http-client/src/lib.rs @@ -336,6 +336,12 @@ pub struct Response { } impl Response { + /// Convert the response into its inner `hyper` representation + #[must_use] + pub fn into_inner(self) -> HyperResponse> { + self.inner + } + /// Read the body into a `Bytes` /// /// # Errors diff --git a/crates/kitsune-observability/Cargo.toml b/crates/kitsune-observability/Cargo.toml new file mode 100644 index 000000000..6a21b352b --- /dev/null +++ b/crates/kitsune-observability/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "kitsune-observability" +edition.workspace = true +version.workspace = true + +[dependencies] +async-trait = "0.1.74" +eyre = "0.6.8" +hyper = { version = "0.14.27", default-features = false } +kitsune-config = { path = "../kitsune-config" } +kitsune-http-client = { path = "../kitsune-http-client" } +metrics = "0.21.1" +metrics-opentelemetry = { git = "https://github.com/aumetra/metrics-opentelemetry.git", rev = "7c3176266c215bb9a7cbc31b3c32f75a22824928" } +metrics-tracing-context = "0.14.0" +metrics-util = "0.15.1" +opentelemetry = { version = "0.20.0", default-features = false, features = [ + "rt-tokio", + "trace", +] } +opentelemetry-http = "0.9.0" +opentelemetry-otlp = { version = "0.13.0", default-features = false, features = [ + "http-proto", + "metrics", + "trace", +] } +tracing = "0.1.40" +tracing-error = "0.2.0" +tracing-opentelemetry = { version = "0.21.0", default-features = false } +tracing-subscriber = "0.3.17" diff --git a/crates/kitsune-observability/src/lib.rs b/crates/kitsune-observability/src/lib.rs new file mode 100644 index 000000000..33fd56625 --- /dev/null +++ b/crates/kitsune-observability/src/lib.rs @@ -0,0 +1,116 @@ +use async_trait::async_trait; +use eyre::Context; +use hyper::body::Body; +use kitsune_config::Configuration; +use metrics_opentelemetry::OpenTelemetryRecorder; +use metrics_tracing_context::{MetricsLayer, TracingContextLayer}; +use metrics_util::layers::Layer as _; +use opentelemetry::{ + metrics::{noop::NoopMeterProvider, Meter, MeterProvider}, + runtime::Tokio, + trace::{noop::NoopTracer, Tracer}, +}; +use opentelemetry_http::{Bytes, HttpClient, HttpError, Request, Response}; +use opentelemetry_otlp::WithExportConfig; +use std::{env, fmt}; +use tracing_error::ErrorLayer; +use tracing_opentelemetry::{OpenTelemetryLayer, PreSampledTracer}; +use tracing_subscriber::{ + filter::{LevelFilter, Targets}, + layer::SubscriberExt, + Layer as _, Registry, +}; + +#[derive(Clone)] +struct HttpClientAdapter { + inner: kitsune_http_client::Client, +} + +impl fmt::Debug for HttpClientAdapter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "HttpClientAdapter") + } +} + +#[async_trait] +impl HttpClient for HttpClientAdapter { + async fn send(&self, request: Request>) -> Result, HttpError> { + let (parts, body) = request.into_parts(); + let body = Body::from(body); + let request = Request::from_parts(parts, body); + + let response = self.inner.execute(request).await?.into_inner(); + + let (parts, body) = response.into_parts(); + let body = hyper::body::to_bytes(body).await?; + + Ok(Response::from_parts(parts, body)) + } +} + +fn initialise_logging(tracer: T) -> eyre::Result<()> +where + T: Tracer + PreSampledTracer + Send + Sync + 'static, +{ + let env_filter = env::var("RUST_LOG") + .map_err(eyre::Report::from) + .and_then(|targets| targets.parse().context("Failed to parse RUST_LOG value")) + .unwrap_or_else(|_| Targets::default().with_default(LevelFilter::INFO)); + + let subscriber = Registry::default() + .with(tracing_subscriber::fmt::layer().with_filter(env_filter)) + .with(ErrorLayer::default()) + .with(OpenTelemetryLayer::new(tracer)); + + let subscriber = subscriber.with(MetricsLayer::new()); + + tracing::subscriber::set_global_default(subscriber) + .context("Couldn't install the global tracing subscriber")?; + + Ok(()) +} + +fn initialise_metrics(meter: Meter) -> eyre::Result<()> { + let recorder = TracingContextLayer::all().layer(OpenTelemetryRecorder::new(meter)); + metrics::set_boxed_recorder(Box::new(recorder)) + .context("Couldn't install the global metrics recorder")?; + + Ok(()) +} + +pub fn initialise(app_name: &'static str, config: &Configuration) -> eyre::Result<()> { + if let Some(ref opentelemetry_config) = config.opentelemetry { + let http_client = HttpClientAdapter { + inner: kitsune_http_client::Client::default(), + }; + + let tracer = opentelemetry_otlp::new_pipeline() + .tracing() + .with_exporter( + opentelemetry_otlp::new_exporter() + .http() + .with_endpoint(opentelemetry_config.http_endpoint.as_str()) + .with_http_client(http_client.clone()), + ) + .install_batch(Tokio)?; + + initialise_logging(tracer)?; + + let meter_provider = opentelemetry_otlp::new_pipeline() + .metrics(Tokio) + .with_exporter( + opentelemetry_otlp::new_exporter() + .http() + .with_endpoint(opentelemetry_config.http_endpoint.as_str()) + .with_http_client(http_client.clone()), + ) + .build()?; + + initialise_metrics(meter_provider.meter(app_name))?; + } else { + initialise_logging(NoopTracer::new())?; + initialise_metrics(NoopMeterProvider::new().meter(app_name))?; + } + + Ok(()) +} diff --git a/kitsune-job-runner/Cargo.toml b/kitsune-job-runner/Cargo.toml index 4608f1f1c..3b60526b4 100644 --- a/kitsune-job-runner/Cargo.toml +++ b/kitsune-job-runner/Cargo.toml @@ -8,8 +8,10 @@ athena = { path = "../lib/athena" } clap = { version = "4.4.6", features = ["derive"] } color-eyre = "0.6.2" deadpool-redis = "0.13.0" +kitsune-config = { path = "../crates/kitsune-config" } kitsune-core = { path = "../crates/kitsune-core" } kitsune-db = { path = "../crates/kitsune-db" } +kitsune-observability = { path = "../crates/kitsune-observability" } kitsune-retry-policies = { path = "../crates/kitsune-retry-policies" } mimalloc = "0.1.39" tokio = { version = "1.33.0", features = ["full"] } diff --git a/kitsune-job-runner/src/lib.rs b/kitsune-job-runner/src/lib.rs index 355988f49..d0d4c156a 100644 --- a/kitsune-job-runner/src/lib.rs +++ b/kitsune-job-runner/src/lib.rs @@ -2,9 +2,9 @@ extern crate tracing; use athena::JobQueue; +use kitsune_config::JobQueueConfiguration; use kitsune_core::{ activitypub::Deliverer, - config::JobQueueConfiguration, job::{JobRunnerContext, KitsuneContextRepo}, state::State as CoreState, }; diff --git a/kitsune-job-runner/src/main.rs b/kitsune-job-runner/src/main.rs index bb0dc2acc..974afc0c5 100644 --- a/kitsune-job-runner/src/main.rs +++ b/kitsune-job-runner/src/main.rs @@ -1,6 +1,7 @@ use clap::Parser; use color_eyre::eyre; -use kitsune_core::{config::Configuration, consts::VERSION}; +use kitsune_config::Configuration; +use kitsune_core::consts::VERSION; use std::path::PathBuf; use tokio::fs; @@ -24,6 +25,8 @@ async fn main() -> eyre::Result<()> { let raw_config = fs::read_to_string(args.config).await?; let config: Configuration = toml::from_str(&raw_config)?; + kitsune_observability::initialise(env!("CARGO_PKG_NAME"), &config)?; + let db_pool = kitsune_db::connect( &config.database.url, config.database.max_connections as usize, diff --git a/kitsune/Cargo.toml b/kitsune/Cargo.toml index 0722ddbbc..925a05e8b 100644 --- a/kitsune/Cargo.toml +++ b/kitsune/Cargo.toml @@ -36,12 +36,9 @@ http = "0.2.9" human-panic = "1.2.1" hyper = { version = "0.14.27", features = ["deprecated"] } iso8601-timestamp = "0.2.12" -mimalloc = "0.1.39" -mime = "0.3.17" -mime_guess = { version = "2.0.4", default-features = false } -pkcs8 = "0.10.2" kitsune-cache = { path = "../crates/kitsune-cache" } kitsune-captcha = { path = "../crates/kitsune-captcha" } +kitsune-config = { path = "../crates/kitsune-config" } kitsune-core = { path = "../crates/kitsune-core" } kitsune-db = { path = "../crates/kitsune-db" } kitsune-embed = { path = "../crates/kitsune-embed" } @@ -49,9 +46,15 @@ kitsune-http-client = { path = "../crates/kitsune-http-client" } kitsune-http-signatures = { path = "../crates/kitsune-http-signatures" } kitsune-job-runner = { path = "../kitsune-job-runner" } kitsune-language = { path = "../crates/kitsune-language" } +kitsune-observability = { path = "../crates/kitsune-observability" } kitsune-search = { path = "../crates/kitsune-search" } kitsune-storage = { path = "../crates/kitsune-storage" } kitsune-type = { path = "../crates/kitsune-type" } +metrics = "0.21.1" +mimalloc = "0.1.39" +mime = "0.3.17" +mime_guess = { version = "2.0.4", default-features = false } +pkcs8 = "0.10.2" oxide-auth = "0.5.4" oxide-auth-async = "0.1.1" oxide-auth-axum = "0.3.0" @@ -74,8 +77,6 @@ tower-http = { version = "0.4.4", features = [ "trace", ] } tracing = "0.1.40" -tracing-error = "0.2.0" -tracing-subscriber = "0.3.17" typed-builder = "0.18.0" url = "2.4.1" utoipa = { version = "4.0.0", features = ["axum_extras", "uuid"] } @@ -94,13 +95,6 @@ async-graphql = { version = "6.0.9", default-features = false, features = [ ], optional = true } async-graphql-axum = { version = "6.0.9", optional = true } -# "metrics" feature -axum-prometheus = { version = "0.4.0", optional = true } -metrics = { version = "0.21.1", optional = true } -metrics-exporter-prometheus = { version = "0.12.1", optional = true } -metrics-tracing-context = { version = "0.14.0", optional = true } -metrics-util = { version = "0.15.1", optional = true } - # "oidc" feature openidconnect = { version = "3.4.0", default-features = false, optional = true } @@ -111,7 +105,7 @@ serial_test = "2.0.0" tower = "0.4.13" [features] -default = ["graphql-api", "mastodon-api", "metrics"] +default = ["graphql-api", "mastodon-api"] graphql-api = [ "dep:async-graphql", "dep:async-graphql-axum", @@ -119,12 +113,4 @@ graphql-api = [ ] mastodon-api = ["kitsune-core/mastodon-api"] meilisearch = ["kitsune-core/meilisearch"] -metrics = [ - "kitsune-core/metrics", - "dep:axum-prometheus", - "dep:metrics", - "dep:metrics-exporter-prometheus", - "dep:metrics-tracing-context", - "dep:metrics-util", -] oidc = ["dep:openidconnect"] diff --git a/kitsune/src/http/handler/users/inbox.rs b/kitsune/src/http/handler/users/inbox.rs index 4c0216db6..d1206fb2c 100644 --- a/kitsune/src/http/handler/users/inbox.rs +++ b/kitsune/src/http/handler/users/inbox.rs @@ -345,7 +345,6 @@ pub async fn post( State(federation_filter): State, SignedActivity(author, activity): SignedActivity, ) -> Result<()> { - #[cfg(feature = "metrics")] increment_counter!("received_activities"); if !federation_filter diff --git a/kitsune/src/http/mod.rs b/kitsune/src/http/mod.rs index fe82912bc..504e50819 100644 --- a/kitsune/src/http/mod.rs +++ b/kitsune/src/http/mod.rs @@ -4,7 +4,7 @@ use self::{ }; use crate::state::Zustand; use axum::{extract::DefaultBodyLimit, Router}; -use kitsune_core::config::ServerConfiguration; +use kitsune_config::ServerConfiguration; use std::time::Duration; use tower_http::{ catch_panic::CatchPanicLayer, @@ -71,14 +71,6 @@ pub fn create_router(state: Zustand, server_config: &ServerConfiguration) -> Rou ServeDir::new(frontend_dir.as_str()).fallback(ServeFile::new(frontend_index_path)), ); - #[cfg(feature = "metrics")] - { - use axum_prometheus::PrometheusMetricLayer; - - // Even though this explicity has "prometheus" in the name, it just emits regular `metrics` calls - router = router.layer(PrometheusMetricLayer::new()); - } - router .layer(CatchPanicLayer::new()) .layer(CorsLayer::permissive()) diff --git a/kitsune/src/lib.rs b/kitsune/src/lib.rs index 96bc609ff..7a72264ca 100644 --- a/kitsune/src/lib.rs +++ b/kitsune/src/lib.rs @@ -7,7 +7,6 @@ forbidden_lint_groups )] -#[cfg(feature = "metrics")] #[macro_use] extern crate metrics; @@ -27,7 +26,8 @@ use self::{ state::{SessionConfig, Zustand}, }; use athena::JobQueue; -use kitsune_core::{config::Configuration, job::KitsuneContextRepo}; +use kitsune_config::Configuration; +use kitsune_core::job::KitsuneContextRepo; use kitsune_db::PgPool; use oauth2::OAuthEndpoint; @@ -35,7 +35,8 @@ use oauth2::OAuthEndpoint; use { self::oidc::{async_client, OidcService}, futures_util::future::OptionFuture, - kitsune_core::{config::OidcConfiguration, service::url::UrlService}, + kitsune_config::OidcConfiguration, + kitsune_core::service::url::UrlService, openidconnect::{ core::{CoreClient, CoreProviderMetadata}, ClientId, ClientSecret, IssuerUrl, RedirectUrl, diff --git a/kitsune/src/main.rs b/kitsune/src/main.rs index 382f20865..f37002f41 100644 --- a/kitsune/src/main.rs +++ b/kitsune/src/main.rs @@ -5,16 +5,14 @@ use clap::Parser; use color_eyre::{config::HookBuilder, Help}; use eyre::Context; use kitsune::consts::STARTUP_FIGLET; -use kitsune_core::{config::Configuration, consts::VERSION}; +use kitsune_config::Configuration; +use kitsune_core::consts::VERSION; use std::{ borrow::Cow, env, future, panic::{self, PanicInfo}, path::PathBuf, }; -use tracing::level_filters::LevelFilter; -use tracing_error::ErrorLayer; -use tracing_subscriber::{filter::Targets, layer::SubscriberExt, Layer, Registry}; use url::Url; #[global_allocator] @@ -50,63 +48,6 @@ fn install_handlers() -> eyre::Result<()> { Ok(()) } -#[cfg(feature = "metrics")] -fn initialise_metrics(config: &Configuration) -> impl Layer -where - S: for<'a> tracing_subscriber::registry::LookupSpan<'a> + tracing::Subscriber, -{ - use axum_prometheus::{ - utils::SECONDS_DURATION_BUCKETS, AXUM_HTTP_REQUESTS_DURATION_SECONDS, - PREFIXED_HTTP_REQUESTS_DURATION_SECONDS, - }; - use metrics_exporter_prometheus::{Matcher, PrometheusBuilder}; - use metrics_tracing_context::{MetricsLayer, TracingContextLayer}; - use metrics_util::layers::Layer as _; - - let (prometheus_recorder, server_future) = PrometheusBuilder::new() - // Some defaults that would have been set by the `axum-prometheus` crate - .set_buckets_for_metric( - Matcher::Full( - PREFIXED_HTTP_REQUESTS_DURATION_SECONDS - .get() - .map_or(AXUM_HTTP_REQUESTS_DURATION_SECONDS, |s| s.as_str()) - .to_string(), - ), - SECONDS_DURATION_BUCKETS, - ) - .unwrap() - .with_http_listener(([0, 0, 0, 0], config.server.prometheus_port)) - .build() - .unwrap(); - - tokio::spawn(server_future); - - let recorder = TracingContextLayer::all().layer(prometheus_recorder); - metrics::set_boxed_recorder(Box::new(recorder)).unwrap(); - - MetricsLayer::new() -} - -fn initialise_logging(_config: &Configuration) -> eyre::Result<()> { - let env_filter = env::var("RUST_LOG") - .map_err(eyre::Report::from) - .and_then(|targets| targets.parse().context("Failed to parse RUST_LOG value")) - .unwrap_or_else(|_| Targets::default().with_default(LevelFilter::INFO)); - - let subscriber = Registry::default() - .with(tracing_subscriber::fmt::layer().with_filter(env_filter)) - .with(ErrorLayer::default()); - - #[cfg(feature = "metrics")] - #[allow(clippy::used_underscore_binding)] - let subscriber = subscriber.with(initialise_metrics(_config)); - - tracing::subscriber::set_global_default(subscriber) - .context("Couldn't install the global tracing subscriber")?; - - Ok(()) -} - fn postgres_url_diagnostics(db_url: &str) -> String { let url = match Url::parse(db_url) { Ok(url) => url, @@ -142,7 +83,7 @@ async fn boot() -> eyre::Result<()> { let args = Args::parse(); let config = Configuration::load(args.config).await?; - initialise_logging(&config)?; + kitsune_observability::initialise(env!("CARGO_PKG_NAME"), &config)?; let conn = kitsune_db::connect( &config.database.url, diff --git a/module.nix b/module.nix index 650a470dc..143a7630d 100644 --- a/module.nix +++ b/module.nix @@ -172,10 +172,6 @@ in type = types.port; default = 5000; }; - prometheus-port = mkOption { - type = types.port; - default = 9000; - }; request-timeout-secs = mkOption { type = types.ints.positive; default = 60;