From 7dfb52f80b31e46b2448e6f85dd5678d2b6b18cd Mon Sep 17 00:00:00 2001 From: Khor Shu Heng <32997938+khorshuheng@users.noreply.github.com> Date: Fri, 28 Jun 2024 13:44:59 +0800 Subject: [PATCH] feat: add telemetry to appflowy collaborate service (#662) --- Cargo.lock | 1 + services/appflowy-collaborate/Cargo.toml | 1 + services/appflowy-collaborate/src/config.rs | 37 ++++++++++++++++- services/appflowy-collaborate/src/lib.rs | 1 + services/appflowy-collaborate/src/main.rs | 2 + .../appflowy-collaborate/src/telemetry.rs | 41 +++++++++++++++++++ 6 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 services/appflowy-collaborate/src/telemetry.rs diff --git a/Cargo.lock b/Cargo.lock index e25f3010..5b1d9c56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -730,6 +730,7 @@ dependencies = [ "tokio-stream", "tokio-util", "tracing", + "tracing-subscriber", "uuid", "validator", "workspace-access", diff --git a/services/appflowy-collaborate/Cargo.toml b/services/appflowy-collaborate/Cargo.toml index c13854a6..ea4c47d1 100644 --- a/services/appflowy-collaborate/Cargo.toml +++ b/services/appflowy-collaborate/Cargo.toml @@ -36,6 +36,7 @@ serde_json.workspace = true serde_repr.workspace = true sqlx = { workspace = true, default-features = false, features = ["runtime-tokio-rustls", "macros", "postgres", "uuid", "chrono"] } thiserror = "1.0.56" +tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } anyhow = "1" bytes.workspace = true diff --git a/services/appflowy-collaborate/src/config.rs b/services/appflowy-collaborate/src/config.rs index ebd7acf4..73abfe34 100644 --- a/services/appflowy-collaborate/src/config.rs +++ b/services/appflowy-collaborate/src/config.rs @@ -3,10 +3,12 @@ use std::str::FromStr; use anyhow::Context; use secrecy::Secret; +use serde::Deserialize; use sqlx::postgres::{PgConnectOptions, PgSslMode}; #[derive(Clone, Debug)] pub struct Config { + pub app_env: Environment, pub application: ApplicationSetting, pub websocket: WebsocketSetting, pub db_settings: DatabaseSetting, @@ -21,6 +23,36 @@ pub struct ApplicationSetting { pub host: String, } +#[derive(Clone, Debug, Deserialize)] +pub enum Environment { + Local, + Production, +} + +impl Environment { + pub fn as_str(&self) -> &'static str { + match self { + Environment::Local => "local", + Environment::Production => "production", + } + } +} + +impl FromStr for Environment { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "local" => Ok(Self::Local), + "production" => Ok(Self::Production), + other => anyhow::bail!( + "{} is not a supported environment. Use either `local` or `production`.", + other + ), + } + } +} + #[derive(Clone, Debug)] pub struct WebsocketSetting { pub heartbeat_interval: u8, @@ -60,7 +92,7 @@ impl DatabaseSetting { } } -#[derive(serde::Deserialize, Clone, Debug)] +#[derive(Clone, Debug, Deserialize)] pub struct GoTrueSetting { pub jwt_secret: Secret, } @@ -85,6 +117,9 @@ pub fn get_env_var(key: &str, default: &str) -> String { pub fn get_configuration() -> Result { let config = Config { + app_env: get_env_var("APPFLOWY_ENVIRONMENT", "local") + .parse() + .context("fail to get APPFLOWY_ENVIRONMENT")?, application: ApplicationSetting { port: get_env_var("APPFLOWY_COLLAB_SERVICE_PORT", "8001").parse()?, host: get_env_var("APPFLOWY_COLLAB_SERVICE_HOST", "0.0.0.0"), diff --git a/services/appflowy-collaborate/src/lib.rs b/services/appflowy-collaborate/src/lib.rs index 2f5625d9..7986f440 100644 --- a/services/appflowy-collaborate/src/lib.rs +++ b/services/appflowy-collaborate/src/lib.rs @@ -16,6 +16,7 @@ mod rt_server; pub mod shared_state; pub mod snapshot; mod state; +pub mod telemetry; mod util; pub use metrics::*; diff --git a/services/appflowy-collaborate/src/main.rs b/services/appflowy-collaborate/src/main.rs index d5f7131b..5b89a9da 100644 --- a/services/appflowy-collaborate/src/main.rs +++ b/services/appflowy-collaborate/src/main.rs @@ -1,5 +1,6 @@ use appflowy_collaborate::application::{init_state, Application}; use appflowy_collaborate::config::get_configuration; +use appflowy_collaborate::telemetry::init_subscriber; #[actix_web::main] async fn main() -> anyhow::Result<()> { @@ -8,6 +9,7 @@ async fn main() -> anyhow::Result<()> { let conf = get_configuration().map_err(|e| anyhow::anyhow!("Failed to read configuration: {}", e))?; + init_subscriber(&conf.app_env); let (tx, rx) = tokio::sync::mpsc::channel(1000); let state = init_state(&conf, tx) diff --git a/services/appflowy-collaborate/src/telemetry.rs b/services/appflowy-collaborate/src/telemetry.rs new file mode 100644 index 00000000..b1b632f9 --- /dev/null +++ b/services/appflowy-collaborate/src/telemetry.rs @@ -0,0 +1,41 @@ +use crate::config::Environment; +use std::sync::Once; +use tracing::subscriber::set_global_default; +use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::EnvFilter; + +pub fn init_subscriber(app_env: &Environment) { + static START: Once = Once::new(); + START.call_once(|| { + let level = std::env::var("RUST_LOG").unwrap_or("info".to_string()); + let mut filters = vec![]; + filters.push(format!("appflowy_collaborate={}", level)); + filters.push(format!("collab={}", level)); + filters.push(format!("collab_plugins={}", level)); + filters.push(format!("database={}", level)); + let env_filter = EnvFilter::new(filters.join(",")); + + let builder = tracing_subscriber::fmt() + .with_target(true) + .with_max_level(tracing::Level::TRACE) + .with_thread_ids(false) + .with_file(false); + + match app_env { + Environment::Local => { + let subscriber = builder + .with_ansi(true) + .with_target(false) + .with_file(false) + .pretty() + .finish() + .with(env_filter); + set_global_default(subscriber).unwrap(); + }, + Environment::Production => { + let subscriber = builder.json().finish().with(env_filter); + set_global_default(subscriber).unwrap(); + }, + } + }); +}