diff --git a/Cargo.lock b/Cargo.lock index b07af792e2..ce1ec7a807 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2305,6 +2305,8 @@ dependencies = [ "ksuid", "md5", "mime", + "opentelemetry", + "opentelemetry-appender-tracing", "refinery", "secrecy", "serde", @@ -2325,7 +2327,7 @@ dependencies = [ [[package]] name = "hulyrs" version = "0.1.0" -source = "git+https://github.com/hcengineering/hulyrs.git#dcdbe20997369704619bfd1231c6ff458201df42" +source = "git+https://github.com/hcengineering/hulyrs.git#24b211c4d1b013e2ec1e2daae470168a1841dacf" dependencies = [ "actix-web", "bytes", @@ -2337,6 +2339,10 @@ dependencies = [ "itoa", "jsonwebtoken", "num-traits", + "opentelemetry", + "opentelemetry-otlp", + "opentelemetry-stdout", + "opentelemetry_sdk", "rand 0.9.2", "reqwest", "reqwest-middleware", @@ -3257,6 +3263,71 @@ dependencies = [ "tracing", ] +[[package]] +name = "opentelemetry-appender-tracing" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e68f63eca5fad47e570e00e893094fc17be959c80c79a7d6ec1abdd5ae6ffc16" +dependencies = [ + "opentelemetry", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "opentelemetry-http" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f6639e842a97dbea8886e3439710ae463120091e2e064518ba8e716e6ac36d" +dependencies = [ + "async-trait", + "bytes", + "http 1.3.1", + "opentelemetry", + "reqwest", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbee664a43e07615731afc539ca60c6d9f1a9425e25ca09c57bc36c87c55852b" +dependencies = [ + "http 1.3.1", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost", + "reqwest", + "thiserror 2.0.16", + "tracing", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e046fd7660710fe5a05e8748e70d9058dc15c94ba914e7c4faa7c728f0e8ddc" +dependencies = [ + "opentelemetry", + "opentelemetry_sdk", + "prost", + "tonic", +] + +[[package]] +name = "opentelemetry-stdout" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447191061af41c3943e082ea359ab8b64ff27d6d34d30d327df309ddef1eef6f" +dependencies = [ + "chrono", + "opentelemetry", + "opentelemetry_sdk", +] + [[package]] name = "opentelemetry_sdk" version = "0.30.0" @@ -3269,6 +3340,7 @@ dependencies = [ "opentelemetry", "percent-encoding", "rand 0.9.2", + "serde_json", "thiserror 2.0.16", ] @@ -3629,6 +3701,29 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools 0.13.0", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "psl-types" version = "2.0.11" @@ -4005,6 +4100,7 @@ dependencies = [ "cookie 0.18.1", "cookie_store", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "h2 0.4.12", @@ -5453,6 +5549,27 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "tonic" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9" +dependencies = [ + "async-trait", + "base64 0.22.1", + "bytes", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "percent-encoding", + "pin-project", + "prost", + "tokio-stream", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" version = "0.5.2" diff --git a/server/Cargo.toml b/server/Cargo.toml index 1d60cef795..254aad40d3 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -25,9 +25,7 @@ md5 = "0.8.0" size = { version = "0.5.0", features = ["serde"] } uuid = { version = "1.18", features = ["v4", "serde"] } serde_json = "1.0" -hulyrs = { git = "https://github.com/hcengineering/hulyrs.git", features = [ - "actix", -] } +hulyrs = { git = "https://github.com/hcengineering/hulyrs.git", features = ["actix", "otel"] } secrecy = "0.10.3" tracing-actix-web = "0.7.19" aws-config = { version = "1.8.5" } @@ -46,3 +44,5 @@ strum = { version = "0.27.2", features = ["derive"] } json-patch = "4.0.0" jsonptr = "0.7.1" chrono = { version = "0.4.42", features = ["now"] } +opentelemetry = "0.30.0" +opentelemetry-appender-tracing = "0.30.1" diff --git a/server/src/main.rs b/server/src/main.rs index 7f2c532a13..01aa191cb3 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -13,6 +13,7 @@ use tracing_actix_web::TracingLogger; use uuid::Uuid; use hulyrs::services::jwt::actix::ServiceRequestExt; +use hulyrs::services::otel; mod blob; mod conditional; @@ -27,17 +28,54 @@ mod s3; use config::CONFIG; fn initialize_tracing() { + use opentelemetry::trace::TracerProvider; + use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge; + use tracing::Level; + use tracing_opentelemetry::OpenTelemetryLayer; use tracing_subscriber::{filter::targets::Targets, prelude::*}; + let otel_config = otel::OtelConfig { + mode: config::hulyrs::CONFIG.otel_mode.clone(), + service_name: env!("CARGO_PKG_NAME").to_string(), + service_version: env!("CARGO_PKG_VERSION").to_string(), + }; + + otel::init(&otel_config); + let filter = Targets::default() .with_target(env!("CARGO_BIN_NAME"), config::hulyrs::CONFIG.log) .with_target("actix", Level::WARN); let format = tracing_subscriber::fmt::layer().compact(); - tracing_subscriber::registry() - .with(filter) - .with(format) - .init(); + match &config::hulyrs::CONFIG.otel_mode { + otel::OtelMode::Off => { + tracing_subscriber::registry() + .with(filter) + .with(format) + .init(); + } + + _ => { + tracing_subscriber::registry() + .with(filter) + .with(format) + .with(otel::tracer_provider(&otel_config).map(|provider| { + let filter = Targets::default() + .with_default(Level::DEBUG) + .with_target(env!("CARGO_PKG_NAME"), config::hulyrs::CONFIG.log); + + OpenTelemetryLayer::new(provider.tracer("hulylake")).with_filter(filter) + })) + .with(otel::logger_provider(&otel_config).as_ref().map(|logger| { + let filter = Targets::default() + .with_default(Level::DEBUG) + .with_target(env!("CARGO_PKG_NAME"), Level::DEBUG); + + OpenTelemetryTracingBridge::new(logger).with_filter(filter) + })) + .init(); + } + } } #[tokio::main] diff --git a/server/src/merge.rs b/server/src/merge.rs index 21c2478727..827dafe55e 100644 --- a/server/src/merge.rs +++ b/server/src/merge.rs @@ -85,6 +85,7 @@ pub fn validate_patch_body(merge_strategy: MergeStrategy, blob: &Blob) -> Handle } } +#[instrument(level = "debug", skip_all)] pub async fn stream( s3: Arc, parts: Vec>, diff --git a/server/src/patch.rs b/server/src/patch.rs index 9df966dd34..0f1b2e4d81 100644 --- a/server/src/patch.rs +++ b/server/src/patch.rs @@ -3,6 +3,7 @@ use jsonptr::{Pointer, PointerBuf}; use serde::{Deserialize, Serialize}; use serde_json::{Number, Value, json}; use thiserror::Error; +use tracing::*; /// 'add' operation - increments a numeric value #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -91,7 +92,11 @@ impl From for HulyPatchError { } } +#[instrument(level = "debug", skip_all, fields(patches))] pub fn apply(doc: &mut Value, patches: &[PatchOperation]) -> Result<(), HulyPatchError> { + let span = Span::current(); + span.record("patches", &patches.len()); + for patch in patches { if let Some(op) = match patch { PatchOperation::Huly(huly_op) => match huly_op { diff --git a/server/src/recovery.rs b/server/src/recovery.rs index 8f3411cb3b..cf3a399157 100644 --- a/server/src/recovery.rs +++ b/server/src/recovery.rs @@ -41,6 +41,7 @@ pub fn object_etag(parts: Vec<&PartData>) -> anyhow::Result { Ok(format!("{:x}", digest)) } +#[tracing::instrument(level = "debug", skip_all)] pub async fn set_object( s3: &S3Client, workspace: uuid::Uuid, @@ -71,6 +72,7 @@ pub async fn set_object( Ok(()) } +#[tracing::instrument(level = "debug", skip_all)] pub async fn set_blob(s3: &S3Client, key: &str, hash: &str) -> Result<(), RecoveryError> { let s3_bucket = &CONFIG.s3_bucket;