From bec0557ab4929e3c5f78df538a1e2013acadab21 Mon Sep 17 00:00:00 2001 From: Maciej Kozuszek Date: Mon, 9 Dec 2024 14:27:03 +0100 Subject: [PATCH 01/15] Add telemetry apu env variable --- src-tauri/Cargo.toml | 1 + src-tauri/src/app_in_memory_config.rs | 17 ++++++++++++++++- src-tauri/src/main.rs | 2 ++ src-tauri/src/telemetry_manager.rs | 4 ++-- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 1c6638ad8..ef93403b6 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -112,6 +112,7 @@ sys-locale = "0.3.1" [features] airdrop-env = [] +telemetry-env = [] airdrop-local = [] custom-protocol = [ "tauri/custom-protocol", diff --git a/src-tauri/src/app_in_memory_config.rs b/src-tauri/src/app_in_memory_config.rs index 57d6c8713..8323f57e7 100644 --- a/src-tauri/src/app_in_memory_config.rs +++ b/src-tauri/src/app_in_memory_config.rs @@ -35,6 +35,9 @@ const AIRDROP_TWITTER_AUTH_URL: &str = std::env!( "AIRDROP_TWITTER_AUTH_URL", "AIRDROP_TWITTER_AUTH_URL env var not defined" ); +#[cfg(feature = "telemetry-env")] +const TELEMETRY_API_URL: &str = + std::env!("TELEMETRY_API_URL", "TELEMETRY_API_URL env var not defined"); #[derive(Debug, Serialize, Deserialize, Clone)] pub struct AppInMemoryConfig { @@ -42,6 +45,8 @@ pub struct AppInMemoryConfig { pub airdrop_api_url: String, pub airdrop_twitter_auth_url: String, pub airdrop_access_token: Option, + pub telemetry_api_url: String, + pub telemetry_access_token: Option, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -69,6 +74,8 @@ impl Default for AppInMemoryConfig { airdrop_api_url: "https://ut.tari.com".into(), airdrop_twitter_auth_url: "https://airdrop.tari.com/auth".into(), airdrop_access_token: None, + telemetry_api_url: "https://ut.tari.com".into(), + telemetry_access_token: None, } } } @@ -81,6 +88,8 @@ impl AppInMemoryConfig { airdrop_api_url: AIRDROP_API_BASE_URL.into(), airdrop_twitter_auth_url: AIRDROP_TWITTER_AUTH_URL.into(), airdrop_access_token: None, + telemetry_api_url: TELEMETRY_API_URL.into(), + telemetry_access_token: None, }; #[cfg(feature = "airdrop-local")] @@ -89,9 +98,15 @@ impl AppInMemoryConfig { airdrop_api_url: "http://localhost:3004".into(), airdrop_twitter_auth_url: "http://localhost:3004/auth/twitter".into(), airdrop_access_token: None, + telemetry_api_url: "http://localhost:3004".into(), + telemetry_access_token: None, }; - #[cfg(not(any(feature = "airdrop-local", feature = "airdrop-env")))] + #[cfg(not(any( + feature = "airdrop-local", + feature = "airdrop-env", + feature = "telemetry-env" + )))] AppInMemoryConfig::default() } } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 93a72416a..059d04bde 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -574,6 +574,7 @@ struct UniverseAppState { telemetry_manager: Arc>, feedback: Arc>, airdrop_access_token: Arc>>, + telemetry_access_token: Arc>>, p2pool_manager: P2poolManager, tor_manager: TorManager, cached_p2pool_stats: Arc>>>, @@ -669,6 +670,7 @@ fn main() { telemetry_manager: Arc::new(RwLock::new(telemetry_manager)), feedback: Arc::new(RwLock::new(feedback)), airdrop_access_token: Arc::new(RwLock::new(None)), + telemetry_access_token: Arc::new(RwLock::new(None)), tor_manager: TorManager::new(), cached_p2pool_stats: Arc::new(RwLock::new(None)), cached_p2pool_connections: Arc::new(RwLock::new(None)), diff --git a/src-tauri/src/telemetry_manager.rs b/src-tauri/src/telemetry_manager.rs index 56e5e6298..3d79a1954 100644 --- a/src-tauri/src/telemetry_manager.rs +++ b/src-tauri/src/telemetry_manager.rs @@ -313,8 +313,8 @@ impl TelemetryManager { if telemetry_collection_enabled { let airdrop_access_token_validated = validate_jwt(airdrop_access_token.clone()).await; let telemetry_data = get_telemetry_data(cpu_miner.clone(), gpu_miner.clone(), node_manager.clone(), p2pool_manager_cloned.clone(), config.clone(), network).await; - let airdrop_api_url = in_memory_config_cloned.read().await.airdrop_api_url.clone(); - handle_telemetry_data(telemetry_data, airdrop_api_url, airdrop_access_token_validated, window.clone()).await; + let telemetry_api_url = in_memory_config_cloned.read().await.telemetry_api_url.clone(); + handle_telemetry_data(telemetry_data, telemetry_api_url, airdrop_access_token_validated, window.clone()).await; } sleep(timeout); } From 9d1a67686ef480e17f137fb79a053b46c4a0d82f Mon Sep 17 00:00:00 2001 From: Maciej Kozuszek Date: Tue, 10 Dec 2024 22:36:28 +0100 Subject: [PATCH 02/15] Basic telemetry service --- src-tauri/src/main.rs | 1 + src-tauri/src/telemetry_manager.rs | 4 +- src-tauri/src/telemetry_service.rs | 133 +++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 src-tauri/src/telemetry_service.rs diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 059d04bde..6568e0194 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -109,6 +109,7 @@ mod process_watcher; mod progress_tracker; mod setup_status_event; mod telemetry_manager; +mod telemetry_service; mod tests; mod tor_adapter; mod tor_manager; diff --git a/src-tauri/src/telemetry_manager.rs b/src-tauri/src/telemetry_manager.rs index 3d79a1954..56e5e6298 100644 --- a/src-tauri/src/telemetry_manager.rs +++ b/src-tauri/src/telemetry_manager.rs @@ -313,8 +313,8 @@ impl TelemetryManager { if telemetry_collection_enabled { let airdrop_access_token_validated = validate_jwt(airdrop_access_token.clone()).await; let telemetry_data = get_telemetry_data(cpu_miner.clone(), gpu_miner.clone(), node_manager.clone(), p2pool_manager_cloned.clone(), config.clone(), network).await; - let telemetry_api_url = in_memory_config_cloned.read().await.telemetry_api_url.clone(); - handle_telemetry_data(telemetry_data, telemetry_api_url, airdrop_access_token_validated, window.clone()).await; + let airdrop_api_url = in_memory_config_cloned.read().await.airdrop_api_url.clone(); + handle_telemetry_data(telemetry_data, airdrop_api_url, airdrop_access_token_validated, window.clone()).await; } sleep(timeout); } diff --git a/src-tauri/src/telemetry_service.rs b/src-tauri/src/telemetry_service.rs new file mode 100644 index 000000000..9322f5339 --- /dev/null +++ b/src-tauri/src/telemetry_service.rs @@ -0,0 +1,133 @@ +use std::{ + sync::Arc, + time::{Duration, SystemTime}, +}; + +use log::{debug, warn}; +use serde::Serialize; +use serde_json::Value; +use tokio::sync::{ + mpsc::{self, Sender}, + RwLock, +}; +use tokio_util::sync::CancellationToken; + +use crate::{app_config::AppConfig, app_in_memory_config::AppInMemoryConfig}; + +const LOG_TARGET: &str = "tari::universe::telemetry_service"; + +#[derive(Debug, Serialize)] +pub struct TelemetryData { + event_name: String, + event_value: Value, + created_at: SystemTime, + user_id: Option, + app_id: String, + version: String, +} + +#[derive(Debug, thiserror::Error)] +pub enum TelemetryServiceError { + #[error("Other error: {0}")] + Other(#[from] anyhow::Error), + + #[error("Reqwest error: {0}")] + ReqwestError(#[from] reqwest::Error), +} + +pub struct TelemetryService { + app_id: String, + version: String, + frequency: Duration, + tx_channel: Option>, + cancellation_token: CancellationToken, + config: Arc>, + in_memory_config: Arc>, +} + +impl TelemetryService { + pub fn new( + app_id: String, + version: String, + frequency: Duration, + config: Arc>, + in_memory_config: Arc>, + ) -> Self { + let cancellation_token = CancellationToken::new(); + TelemetryService { + app_id, + version, + frequency, + tx_channel: None, + cancellation_token, + config, + in_memory_config, + } + } + pub async fn init(&mut self) -> Result<(), TelemetryServiceError> { + let cancellation_token = self.cancellation_token.clone(); + let config_cloned = self.config.clone(); + let in_memory_config_cloned = self.in_memory_config.clone(); + let telemetry_api_url = in_memory_config_cloned + .read() + .await + .telemetry_api_url + .clone(); + let (tx, mut rx) = mpsc::channel(128); + self.tx_channel = Some(tx); + tokio::spawn(async move { + tokio::select! { + _ = async { + debug!(target: LOG_TARGET, "TelemetryService::init has been started"); + while let Some(telemetry_data) = rx.recv().await { + let telemetry_collection_enabled = config_cloned.read().await.allow_telemetry(); + if telemetry_collection_enabled { + drop(send_telemetry_data(telemetry_data, telemetry_api_url.clone()).await); + } + } + } => {}, + _ = cancellation_token.cancelled() => { + debug!(target: LOG_TARGET,"TelemetryService::init has been cancelled"); + } + } + }); + Ok(()) + } +} + +async fn send_telemetry_data( + data: TelemetryData, + api_url: String, +) -> Result<(), TelemetryServiceError> { + let request = reqwest::Client::new(); + let request_builder = request + .post(api_url) + .header( + "User-Agent".to_string(), + format!("tari-universe/{}", data.version.clone()), + ) + .json(&data); + + let response = request_builder.send().await?; + + if response.status() == 429 { + warn!(target: LOG_TARGET,"TelemetryService::send_telemetry_data Telemetry data rate limited by http {:?}", response.status()); + return Ok(()); + } + + if response.status() != 200 { + let status = response.status(); + let response = response.text().await?; + let response_as_json: Result = serde_json::from_str(&response); + return Err(anyhow::anyhow!( + "Telemetry data sending error. Status {:?} response text: {:?}", + status.to_string(), + response_as_json.unwrap_or(response.into()), + ) + .into()); + } + + debug!(target: LOG_TARGET,"TelemetryService::send_telemetry_data Telemetry data sent"); + + Ok(()) +} From 9c6e5e2dfb5aa02953180ca127c09e126f165c57 Mon Sep 17 00:00:00 2001 From: Maciej Kozuszek Date: Tue, 10 Dec 2024 22:46:45 +0100 Subject: [PATCH 03/15] Fix event format --- src-tauri/src/app_in_memory_config.rs | 4 ---- src-tauri/src/main.rs | 2 -- src-tauri/src/telemetry_service.rs | 32 ++++++++++++++++++--------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src-tauri/src/app_in_memory_config.rs b/src-tauri/src/app_in_memory_config.rs index 8323f57e7..084ebe303 100644 --- a/src-tauri/src/app_in_memory_config.rs +++ b/src-tauri/src/app_in_memory_config.rs @@ -46,7 +46,6 @@ pub struct AppInMemoryConfig { pub airdrop_twitter_auth_url: String, pub airdrop_access_token: Option, pub telemetry_api_url: String, - pub telemetry_access_token: Option, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -75,7 +74,6 @@ impl Default for AppInMemoryConfig { airdrop_twitter_auth_url: "https://airdrop.tari.com/auth".into(), airdrop_access_token: None, telemetry_api_url: "https://ut.tari.com".into(), - telemetry_access_token: None, } } } @@ -89,7 +87,6 @@ impl AppInMemoryConfig { airdrop_twitter_auth_url: AIRDROP_TWITTER_AUTH_URL.into(), airdrop_access_token: None, telemetry_api_url: TELEMETRY_API_URL.into(), - telemetry_access_token: None, }; #[cfg(feature = "airdrop-local")] @@ -99,7 +96,6 @@ impl AppInMemoryConfig { airdrop_twitter_auth_url: "http://localhost:3004/auth/twitter".into(), airdrop_access_token: None, telemetry_api_url: "http://localhost:3004".into(), - telemetry_access_token: None, }; #[cfg(not(any( diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 6568e0194..306eb3b3b 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -575,7 +575,6 @@ struct UniverseAppState { telemetry_manager: Arc>, feedback: Arc>, airdrop_access_token: Arc>>, - telemetry_access_token: Arc>>, p2pool_manager: P2poolManager, tor_manager: TorManager, cached_p2pool_stats: Arc>>>, @@ -671,7 +670,6 @@ fn main() { telemetry_manager: Arc::new(RwLock::new(telemetry_manager)), feedback: Arc::new(RwLock::new(feedback)), airdrop_access_token: Arc::new(RwLock::new(None)), - telemetry_access_token: Arc::new(RwLock::new(None)), tor_manager: TorManager::new(), cached_p2pool_stats: Arc::new(RwLock::new(None)), cached_p2pool_connections: Arc::new(RwLock::new(None)), diff --git a/src-tauri/src/telemetry_service.rs b/src-tauri/src/telemetry_service.rs index 9322f5339..c62faf46a 100644 --- a/src-tauri/src/telemetry_service.rs +++ b/src-tauri/src/telemetry_service.rs @@ -1,7 +1,4 @@ -use std::{ - sync::Arc, - time::{Duration, SystemTime}, -}; +use std::{sync::Arc, time::SystemTime}; use log::{debug, warn}; use serde::Serialize; @@ -20,6 +17,12 @@ const LOG_TARGET: &str = "tari::universe::telemetry_service"; pub struct TelemetryData { event_name: String, event_value: Value, +} + +#[derive(Debug, Serialize)] +pub struct FullTelemetryData { + event_name: String, + event_value: Value, created_at: SystemTime, user_id: Option, app_id: String, @@ -38,7 +41,6 @@ pub enum TelemetryServiceError { pub struct TelemetryService { app_id: String, version: String, - frequency: Duration, tx_channel: Option>, cancellation_token: CancellationToken, config: Arc>, @@ -49,7 +51,6 @@ impl TelemetryService { pub fn new( app_id: String, version: String, - frequency: Duration, config: Arc>, in_memory_config: Arc>, ) -> Self { @@ -57,7 +58,6 @@ impl TelemetryService { TelemetryService { app_id, version, - frequency, tx_channel: None, cancellation_token, config, @@ -73,6 +73,8 @@ impl TelemetryService { .await .telemetry_api_url .clone(); + let app_id = self.app_id.clone(); + let version = self.version.clone(); let (tx, mut rx) = mpsc::channel(128); self.tx_channel = Some(tx); tokio::spawn(async move { @@ -82,7 +84,7 @@ impl TelemetryService { while let Some(telemetry_data) = rx.recv().await { let telemetry_collection_enabled = config_cloned.read().await.allow_telemetry(); if telemetry_collection_enabled { - drop(send_telemetry_data(telemetry_data, telemetry_api_url.clone()).await); + drop(send_telemetry_data(telemetry_data, telemetry_api_url.clone(), app_id.clone(), version.clone()).await); } } } => {}, @@ -98,15 +100,25 @@ impl TelemetryService { async fn send_telemetry_data( data: TelemetryData, api_url: String, + app_id: String, + version: String, ) -> Result<(), TelemetryServiceError> { let request = reqwest::Client::new(); + let full_data = FullTelemetryData { + event_name: data.event_name, + event_value: data.event_value, + created_at: SystemTime::now(), + user_id: None, + app_id, + version, + }; let request_builder = request .post(api_url) .header( "User-Agent".to_string(), - format!("tari-universe/{}", data.version.clone()), + format!("tari-universe/{}", full_data.version.clone()), ) - .json(&data); + .json(&full_data); let response = request_builder.send().await?; From f89b1fa9f758bb31248f8c33bef59f920d66a109 Mon Sep 17 00:00:00 2001 From: Maciej Kozuszek Date: Wed, 11 Dec 2024 16:11:42 +0100 Subject: [PATCH 04/15] Send new telemetry data during app init --- src-tauri/src/app_in_memory_config.rs | 2 +- src-tauri/src/commands.rs | 18 ++++ src-tauri/src/main.rs | 147 ++++++++++++++++++++++++++ src-tauri/src/telemetry_service.rs | 25 +++++ src/types/invoke.ts | 3 +- 5 files changed, 193 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/app_in_memory_config.rs b/src-tauri/src/app_in_memory_config.rs index 084ebe303..57a61ab24 100644 --- a/src-tauri/src/app_in_memory_config.rs +++ b/src-tauri/src/app_in_memory_config.rs @@ -73,7 +73,7 @@ impl Default for AppInMemoryConfig { airdrop_api_url: "https://ut.tari.com".into(), airdrop_twitter_auth_url: "https://airdrop.tari.com/auth".into(), airdrop_access_token: None, - telemetry_api_url: "https://ut.tari.com".into(), + telemetry_api_url: "https://ut.tari.com/push".into(), } } } diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index da8cd6d66..372112dbc 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -44,6 +44,7 @@ use log::{debug, error, info, warn}; use monero_address_creator::Seed as MoneroSeed; use regex::Regex; use serde::Serialize; +use serde_json::Value; use std::fs::{read_dir, remove_dir_all, remove_file, File}; use std::sync::atomic::Ordering; use std::thread::{available_parallelism, sleep}; @@ -993,6 +994,23 @@ pub async fn set_allow_telemetry( Ok(()) } +#[tauri::command] +pub async fn send_data_telemetry_service( + state: tauri::State<'_, UniverseAppState>, + event_name: String, + data: Value, +) -> Result<(), String> { + state + .telemetry_service + .read() + .await + .send(event_name, data) + .await + .inspect_err(|e| error!(target: LOG_TARGET, "error at send_data_telemetry_service {:?}", e)) + .map_err(|e| e.to_string())?; + Ok(()) +} + #[tauri::command] pub async fn set_application_language( state: tauri::State<'_, UniverseAppState>, diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 306eb3b3b..dcafc3021 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -28,7 +28,10 @@ use hardware::hardware_status_monitor::HardwareStatusMonitor; use log::trace; use log::{debug, error, info, warn}; use p2pool::models::Connections; +use serde_json::json; use std::fs::{remove_dir_all, remove_file}; +use std::ops::Deref; +use telemetry_service::TelemetryService; use tokio::sync::watch::{self}; use log4rs::config::RawConfig; @@ -233,6 +236,10 @@ async fn setup_inner( .await .initialize(state.airdrop_access_token.clone(), window.clone()) .await?; + state.telemetry_service.write().await.init().await?; + let telemetry_service = state.telemetry_service.clone(); + let telemetry_service = telemetry_service.read().await; + let telemetry_service = telemetry_service.deref(); let mut binary_resolver = BinaryResolver::current().write().await; let should_check_for_update = now @@ -241,6 +248,15 @@ async fn setup_inner( > Duration::from_secs(60 * 60 * 6); if use_tor && !cfg!(target_os = "macos") { + telemetry_service + .send( + "tor_init".to_string(), + json!({ + "service": "tor_manager", + "percentage": 0, + }), + ) + .await?; progress.set_max(5).await; progress .update("checking-latest-version-tor".to_string(), None, 0) @@ -256,6 +272,17 @@ async fn setup_inner( sleep(Duration::from_secs(1)); } + drop( + telemetry_service + .send( + "minotari_node_init".to_string(), + json!({ + "service": "node_manager", + "percentage": 5, + }), + ) + .await, + ); progress.set_max(10).await; progress .update("checking-latest-version-node".to_string(), None, 0) @@ -270,6 +297,17 @@ async fn setup_inner( .await?; sleep(Duration::from_secs(1)); + drop( + telemetry_service + .send( + "mmproxy_init".to_string(), + json!({ + "service": "mmproxy", + "percentage": 10, + }), + ) + .await, + ); progress.set_max(15).await; progress .update("checking-latest-version-mmproxy".to_string(), None, 0) @@ -284,6 +322,17 @@ async fn setup_inner( .await?; sleep(Duration::from_secs(1)); + drop( + telemetry_service + .send( + "wallet_init".to_string(), + json!({ + "service": "wallet", + "percentage": 15, + }), + ) + .await, + ); progress.set_max(20).await; progress .update("checking-latest-version-wallet".to_string(), None, 0) @@ -298,6 +347,17 @@ async fn setup_inner( .await?; sleep(Duration::from_secs(1)); + drop( + telemetry_service + .send( + "gpuminer_init".to_string(), + json!({ + "service": "gpuminer", + "percentage":20, + }), + ) + .await, + ); progress.set_max(25).await; progress .update("checking-latest-version-gpuminer".to_string(), None, 0) @@ -312,6 +372,17 @@ async fn setup_inner( .await?; sleep(Duration::from_secs(1)); + drop( + telemetry_service + .send( + "xmrig_init".to_string(), + json!({ + "service": "xmrig", + "percentage":25, + }), + ) + .await, + ); progress.set_max(30).await; progress .update("checking-latest-version-xmrig".to_string(), None, 0) @@ -326,6 +397,17 @@ async fn setup_inner( .await?; sleep(Duration::from_secs(1)); + drop( + telemetry_service + .send( + "sha_p2pool_init".to_string(), + json!({ + "service": "sha_p2pool", + "percentage":30, + }), + ) + .await, + ); progress.set_max(35).await; progress .update("checking-latest-version-sha-p2pool".to_string(), None, 0) @@ -406,6 +488,17 @@ async fn setup_inner( } info!(target: LOG_TARGET, "Node has started and is ready"); + drop( + telemetry_service + .send( + "wallet_init".to_string(), + json!({ + "service": "wallet", + "percentage":35, + }), + ) + .await, + ); progress.set_max(40).await; progress .update("waiting-for-wallet".to_string(), None, 0) @@ -420,6 +513,17 @@ async fn setup_inner( ) .await?; + drop( + telemetry_service + .send( + "initial_sync".to_string(), + json!({ + "service": "initial_sync", + "percentage":40, + }), + ) + .await, + ); progress.set_max(75).await; progress .update("preparing-for-initial-sync".to_string(), None, 0) @@ -427,6 +531,17 @@ async fn setup_inner( state.node_manager.wait_synced(progress.clone()).await?; if state.config.read().await.p2pool_enabled() { + drop( + telemetry_service + .send( + "starting_p2pool".to_string(), + json!({ + "service": "starting_p2pool", + "percentage":75, + }), + ) + .await, + ); progress.set_max(85).await; progress .update("starting-p2pool".to_string(), None, 0) @@ -450,6 +565,17 @@ async fn setup_inner( .await?; } + drop( + telemetry_service + .send( + "starting_mmproxy".to_string(), + json!({ + "service": "starting_mmproxy", + "percentage":85, + }), + ) + .await, + ); progress.set_max(100).await; progress .update("starting-mmproxy".to_string(), None, 0) @@ -486,6 +612,17 @@ async fn setup_inner( .await?; mm_proxy_manager.wait_ready().await?; *state.is_setup_finished.write().await = true; + drop( + telemetry_service + .send( + "setup_finished".to_string(), + json!({ + "service": "setup_finished", + "percentage":100, + }), + ) + .await, + ); drop( app.clone() .emit( @@ -573,6 +710,7 @@ struct UniverseAppState { node_manager: NodeManager, wallet_manager: WalletManager, telemetry_manager: Arc>, + telemetry_service: Arc>, feedback: Arc>, airdrop_access_token: Arc>>, p2pool_manager: P2poolManager, @@ -645,6 +783,13 @@ fn main() { p2pool_manager.clone(), ); + let telemetry_service = TelemetryService::new( + app_config_raw.anon_id().to_string(), + "0.0.0".to_string(), + app_config.clone(), + app_in_memory_config.clone(), + ); + let feedback = Feedback::new(app_in_memory_config.clone(), app_config.clone()); let mm_proxy_manager = MmProxyManager::new(); @@ -668,6 +813,7 @@ fn main() { wallet_manager, p2pool_manager, telemetry_manager: Arc::new(RwLock::new(telemetry_manager)), + telemetry_service: Arc::new(RwLock::new(telemetry_service)), feedback: Arc::new(RwLock::new(feedback)), airdrop_access_token: Arc::new(RwLock::new(None)), tor_manager: TorManager::new(), @@ -849,6 +995,7 @@ fn main() { commands::send_feedback, commands::set_airdrop_access_token, commands::set_allow_telemetry, + commands::send_data_telemetry_service, commands::set_application_language, commands::set_auto_update, commands::set_cpu_mining_enabled, diff --git a/src-tauri/src/telemetry_service.rs b/src-tauri/src/telemetry_service.rs index c62faf46a..125fdcd7d 100644 --- a/src-tauri/src/telemetry_service.rs +++ b/src-tauri/src/telemetry_service.rs @@ -95,6 +95,31 @@ impl TelemetryService { }); Ok(()) } + + pub async fn send( + &self, + event_name: String, + event_value: Value, + ) -> Result<(), TelemetryServiceError> { + let data = TelemetryData { + event_name, + event_value, + }; + if let Some(tx) = &self.tx_channel { + if let Err(_) = tx.send(data).await { + warn!(target: LOG_TARGET,"TelemetryService::send_telemetry_data Telemetry data sending failed"); + return Err(TelemetryServiceError::Other(anyhow::anyhow!( + "Telemetry data sending failed" + ))); + } + Ok(()) + } else { + warn!(target: LOG_TARGET,"TelemetryService::send_telemetry_data Telemetry data sending failed - Service is not initialized"); + Err(TelemetryServiceError::Other(anyhow::anyhow!( + "Telemetry data sending failed - Service is not initialized" + ))) + } + } } async fn send_telemetry_data( diff --git a/src/types/invoke.ts b/src/types/invoke.ts index d191f663b..4cf0336db 100644 --- a/src/types/invoke.ts +++ b/src/types/invoke.ts @@ -35,7 +35,8 @@ declare module '@tauri-apps/api/core' { function invoke(param: 'start_mining'): Promise; function invoke(param: 'stop_mining'): Promise; function invoke(param: 'set_allow_telemetry', payload: { allow_telemetry: boolean }): Promise; - function invoke(param: 'set_user_inactivity_timeout', payload: { timeout: number }): Promise; + function invoke(param: 'send_data_telemetry_service', payload: { threads: number }): Promise; + function invoke(param: 'set_user_inactivity_timeout', payload: { event_name: string; data: object }): Promise; function invoke(param: 'update_applications'): Promise; function invoke( param: 'set_mode', From dba87af7d3d1be9559ffbb0b96ebb37af9e3c8a2 Mon Sep 17 00:00:00 2001 From: Maciej Kozuszek Date: Thu, 12 Dec 2024 14:29:09 +0100 Subject: [PATCH 05/15] Add retries with backoffs --- src-tauri/src/main.rs | 33 ++++++++++++++---------- src-tauri/src/process_utils.rs | 37 ++++++++++++++++++++++++++- src-tauri/src/telemetry_manager.rs | 40 +----------------------------- src-tauri/src/telemetry_service.rs | 27 ++++++++++++++++---- 4 files changed, 79 insertions(+), 58 deletions(-) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index dcafc3021..4f1a15c51 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -236,7 +236,14 @@ async fn setup_inner( .await .initialize(state.airdrop_access_token.clone(), window.clone()) .await?; - state.telemetry_service.write().await.init().await?; + + let app_version = app.package_info().version.clone(); + state + .telemetry_service + .write() + .await + .init(app_version.to_string()) + .await?; let telemetry_service = state.telemetry_service.clone(); let telemetry_service = telemetry_service.read().await; let telemetry_service = telemetry_service.deref(); @@ -250,7 +257,7 @@ async fn setup_inner( if use_tor && !cfg!(target_os = "macos") { telemetry_service .send( - "tor_init".to_string(), + "checking-latest-version-tor".to_string(), json!({ "service": "tor_manager", "percentage": 0, @@ -275,7 +282,7 @@ async fn setup_inner( drop( telemetry_service .send( - "minotari_node_init".to_string(), + "checking-latest-version-node".to_string(), json!({ "service": "node_manager", "percentage": 5, @@ -300,7 +307,7 @@ async fn setup_inner( drop( telemetry_service .send( - "mmproxy_init".to_string(), + "checking-latest-version-mmproxy".to_string(), json!({ "service": "mmproxy", "percentage": 10, @@ -325,7 +332,7 @@ async fn setup_inner( drop( telemetry_service .send( - "wallet_init".to_string(), + "checking-latest-version-wallet".to_string(), json!({ "service": "wallet", "percentage": 15, @@ -350,7 +357,7 @@ async fn setup_inner( drop( telemetry_service .send( - "gpuminer_init".to_string(), + "checking-latest-version-gpuminer".to_string(), json!({ "service": "gpuminer", "percentage":20, @@ -375,7 +382,7 @@ async fn setup_inner( drop( telemetry_service .send( - "xmrig_init".to_string(), + "checking-latest-version-xmrig".to_string(), json!({ "service": "xmrig", "percentage":25, @@ -400,7 +407,7 @@ async fn setup_inner( drop( telemetry_service .send( - "sha_p2pool_init".to_string(), + "checking-latest-version-sha-p2pool".to_string(), json!({ "service": "sha_p2pool", "percentage":30, @@ -491,7 +498,7 @@ async fn setup_inner( drop( telemetry_service .send( - "wallet_init".to_string(), + "waiting-for-wallet".to_string(), json!({ "service": "wallet", "percentage":35, @@ -516,7 +523,7 @@ async fn setup_inner( drop( telemetry_service .send( - "initial_sync".to_string(), + "preparing-for-initial-sync".to_string(), json!({ "service": "initial_sync", "percentage":40, @@ -534,7 +541,7 @@ async fn setup_inner( drop( telemetry_service .send( - "starting_p2pool".to_string(), + "starting-p2pool".to_string(), json!({ "service": "starting_p2pool", "percentage":75, @@ -568,7 +575,7 @@ async fn setup_inner( drop( telemetry_service .send( - "starting_mmproxy".to_string(), + "starting-mmproxy".to_string(), json!({ "service": "starting_mmproxy", "percentage":85, @@ -615,7 +622,7 @@ async fn setup_inner( drop( telemetry_service .send( - "setup_finished".to_string(), + "setup-finished".to_string(), json!({ "service": "setup_finished", "percentage":100, diff --git a/src-tauri/src/process_utils.rs b/src-tauri/src/process_utils.rs index 48b94159d..1be861473 100644 --- a/src-tauri/src/process_utils.rs +++ b/src-tauri/src/process_utils.rs @@ -20,7 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::path::Path; +use std::{future::Future, path::Path, pin::Pin, time::Duration}; pub fn launch_child_process( file_path: &Path, @@ -84,3 +84,38 @@ pub fn launch_child_process( // Ok(output.stdout.as_slice().to_vec()) // } // } + +pub async fn retry_with_backoff( + mut f: T, + increment_in_secs: u64, + max_retries: u64, + operation_name: &str, +) -> anyhow::Result +where + T: FnMut() -> Pin> + Send>>, + E: std::error::Error, +{ + let range_size = increment_in_secs * max_retries + 1; + + for i in (0..range_size).step_by(usize::try_from(increment_in_secs)?) { + tokio::time::sleep(Duration::from_secs(i)).await; + + let result = f().await; + match result { + Ok(res) => return Ok(res), + Err(e) => { + if i == range_size - 1 { + return Err(anyhow::anyhow!( + "Max retries reached, {} failed. Last error: {:?}", + operation_name, + e + )); + } + } + } + } + Err(anyhow::anyhow!( + "Max retries reached, {} failed without capturing error", + operation_name + )) +} diff --git a/src-tauri/src/telemetry_manager.rs b/src-tauri/src/telemetry_manager.rs index 56e5e6298..6ef55b6ff 100644 --- a/src-tauri/src/telemetry_manager.rs +++ b/src-tauri/src/telemetry_manager.rs @@ -23,6 +23,7 @@ use crate::app_in_memory_config::AppInMemoryConfig; use crate::hardware::hardware_status_monitor::HardwareStatusMonitor; use crate::p2pool_manager::{self, P2poolManager}; +use crate::process_utils::retry_with_backoff; use crate::{ app_config::{AppConfig, MiningMode}, cpu_miner::CpuMiner, @@ -41,9 +42,7 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use sha2::Digest; use std::collections::HashMap; -use std::future::Future; use std::ops::Div; -use std::pin::Pin; use std::{sync::Arc, thread::sleep, time::Duration}; use tari_common::configuration::Network; use tari_core::transactions::tari_amount::MicroMinotari; @@ -681,40 +680,3 @@ async fn send_telemetry_data( } Ok(None) } - -async fn retry_with_backoff( - mut f: T, - increment_in_secs: u64, - max_retries: u64, - operation_name: &str, -) -> anyhow::Result -where - T: FnMut() -> Pin> + Send>>, - E: std::error::Error, -{ - let range_size = increment_in_secs * max_retries + 1; - - for i in (0..range_size).step_by(usize::try_from(increment_in_secs)?) { - tokio::time::sleep(Duration::from_secs(i)).await; - - let result = f().await; - match result { - Ok(res) => return Ok(res), - Err(e) => { - if i == range_size - 1 { - return Err(anyhow::anyhow!( - "Max retries reached, {} failed. Last error: {:?}", - operation_name, - e - )); - } else { - warn!(target: LOG_TARGET, "Retrying {} due to failure: {:?}", operation_name, e); - } - } - } - } - Err(anyhow::anyhow!( - "Max retries reached, {} failed without capturing error", - operation_name - )) -} diff --git a/src-tauri/src/telemetry_service.rs b/src-tauri/src/telemetry_service.rs index 125fdcd7d..baabdd76c 100644 --- a/src-tauri/src/telemetry_service.rs +++ b/src-tauri/src/telemetry_service.rs @@ -1,6 +1,6 @@ use std::{sync::Arc, time::SystemTime}; -use log::{debug, warn}; +use log::{debug, error, warn}; use serde::Serialize; use serde_json::Value; use tokio::sync::{ @@ -9,11 +9,14 @@ use tokio::sync::{ }; use tokio_util::sync::CancellationToken; -use crate::{app_config::AppConfig, app_in_memory_config::AppInMemoryConfig}; +use crate::{ + app_config::AppConfig, app_in_memory_config::AppInMemoryConfig, + process_utils::retry_with_backoff, +}; const LOG_TARGET: &str = "tari::universe::telemetry_service"; -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Clone)] pub struct TelemetryData { event_name: String, event_value: Value, @@ -64,7 +67,8 @@ impl TelemetryService { in_memory_config, } } - pub async fn init(&mut self) -> Result<(), TelemetryServiceError> { + pub async fn init(&mut self, app_version: String) -> Result<(), TelemetryServiceError> { + self.version = app_version; let cancellation_token = self.cancellation_token.clone(); let config_cloned = self.config.clone(); let in_memory_config_cloned = self.in_memory_config.clone(); @@ -84,7 +88,20 @@ impl TelemetryService { while let Some(telemetry_data) = rx.recv().await { let telemetry_collection_enabled = config_cloned.read().await.allow_telemetry(); if telemetry_collection_enabled { - drop(send_telemetry_data(telemetry_data, telemetry_api_url.clone(), app_id.clone(), version.clone()).await); + drop(retry_with_backoff( + || { + Box::pin(send_telemetry_data( + telemetry_data.clone(), + telemetry_api_url.clone(), + app_id.clone(), + version.clone(), + )) + }, + 3, + 2, + "send_telemetry_data", + ) + .await); } } } => {}, From d0ae3eb017ccb2deccab8b2be79029a7b596987a Mon Sep 17 00:00:00 2001 From: Maciej Kozuszek Date: Thu, 12 Dec 2024 15:26:03 +0100 Subject: [PATCH 06/15] Add envs for workflows --- .github/workflows/ci.yml | 1 + .github/workflows/release.yml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff0c18778..d52a70644 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,6 +64,7 @@ jobs: AIRDROP_BASE_URL: http://localhost:4000 AIRDROP_API_BASE_URL: http://localhost:3004 AIRDROP_TWITTER_AUTH_URL: http://localhost:3004/auth/twitter + TELEMETRY_API_URL: http://localhost:3004 run: | cargo install cargo-lints cargo lints clippy --all-targets --all-features diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3e0f8818a..465d32376 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,6 +57,7 @@ jobs: AIRDROP_BASE_URL: ${{ secrets.BETA_AIRDROP_BASE_URL }} AIRDROP_API_BASE_URL: ${{ secrets.BETA_AIRDROP_API_BASE_URL }} AIRDROP_TWITTER_AUTH_URL: ${{ secrets.BETA_AIRDROP_TWITTER_AUTH_URL }} + TELEMETRY_API_URL: ${{ secrets.BETA_TELEMETRY_API_URL }} shell: bash run: | #set -xueo pipefail @@ -65,6 +66,7 @@ jobs: echo "AIRDROP_BASE_URL=${{ env.AIRDROP_BASE_URL }}" >> $GITHUB_ENV echo "AIRDROP_API_BASE_URL=${{ env.AIRDROP_API_BASE_URL }}" >> $GITHUB_ENV echo "AIRDROP_TWITTER_AUTH_URL=${{ env.AIRDROP_TWITTER_AUTH_URL }}" >> $GITHUB_ENV + echo "TELEMETRY_API_URL=${{ env.TELEMETRY_API_URL }}" >> $GITHUB_ENV echo "TS_FEATURES=release-ci-beta, airdrop-env" >> $GITHUB_ENV # numeric-only and cannot be greater than 65535 for msi target export BETA_DATE=$(date +%m%d) From 331eda61d8247f828675c84b31f9a9a4baf33320 Mon Sep 17 00:00:00 2001 From: Maciej Kozuszek Date: Thu, 12 Dec 2024 15:42:38 +0100 Subject: [PATCH 07/15] Fix CI errors --- src-tauri/src/telemetry_service.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/telemetry_service.rs b/src-tauri/src/telemetry_service.rs index baabdd76c..7fa9949be 100644 --- a/src-tauri/src/telemetry_service.rs +++ b/src-tauri/src/telemetry_service.rs @@ -1,8 +1,29 @@ -use std::{sync::Arc, time::SystemTime}; +// Copyright 2024. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use log::{debug, error, warn}; use serde::Serialize; use serde_json::Value; +use std::{sync::Arc, time::SystemTime}; use tokio::sync::{ mpsc::{self, Sender}, RwLock, @@ -123,7 +144,7 @@ impl TelemetryService { event_value, }; if let Some(tx) = &self.tx_channel { - if let Err(_) = tx.send(data).await { + if (tx.send(data).await).is_err() { warn!(target: LOG_TARGET,"TelemetryService::send_telemetry_data Telemetry data sending failed"); return Err(TelemetryServiceError::Other(anyhow::anyhow!( "Telemetry data sending failed" From 3177799b06b300f3aa48eab1916ccb97b9648ce4 Mon Sep 17 00:00:00 2001 From: Maciej Kozuszek Date: Fri, 13 Dec 2024 15:46:44 +0100 Subject: [PATCH 08/15] Enrich event message with system info --- src-tauri/src/main.rs | 20 +++++++++--------- src-tauri/src/telemetry_service.rs | 30 ++++++++++++++++++++++++++- src-tauri/src/utils/platform_utils.rs | 12 +++++++++++ 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 2908ff7cf..4f0f8da71 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -237,6 +237,16 @@ async fn setup_inner( let last_binaries_update_timestamp = state.config.read().await.last_binaries_update_timestamp(); let now = SystemTime::now(); + let _unused = state + .gpu_miner + .write() + .await + .detect(config_dir.clone()) + .await + .inspect_err(|e| error!(target: LOG_TARGET, "Could not detect gpu miner: {:?}", e)); + + HardwareStatusMonitor::current().initialize().await?; + state .telemetry_manager .write() @@ -448,16 +458,6 @@ async fn setup_inner( //drop binary resolver to release the lock drop(binary_resolver); - let _unused = state - .gpu_miner - .write() - .await - .detect(config_dir.clone()) - .await - .inspect_err(|e| error!(target: LOG_TARGET, "Could not detect gpu miner: {:?}", e)); - - HardwareStatusMonitor::current().initialize().await?; - let mut tor_control_port = None; if use_tor && !cfg!(target_os = "macos") { state diff --git a/src-tauri/src/telemetry_service.rs b/src-tauri/src/telemetry_service.rs index 7fa9949be..d23dbe2d8 100644 --- a/src-tauri/src/telemetry_service.rs +++ b/src-tauri/src/telemetry_service.rs @@ -32,7 +32,8 @@ use tokio_util::sync::CancellationToken; use crate::{ app_config::AppConfig, app_in_memory_config::AppInMemoryConfig, - process_utils::retry_with_backoff, + hardware::hardware_status_monitor::HardwareStatusMonitor, process_utils::retry_with_backoff, + utils::platform_utils::PlatformUtils, }; const LOG_TARGET: &str = "tari::universe::telemetry_service"; @@ -51,6 +52,9 @@ pub struct FullTelemetryData { user_id: Option, app_id: String, version: String, + os: String, + cpu_name: String, + gpu_name: String, } #[derive(Debug, thiserror::Error)] @@ -167,6 +171,27 @@ async fn send_telemetry_data( version: String, ) -> Result<(), TelemetryServiceError> { let request = reqwest::Client::new(); + let hardware = HardwareStatusMonitor::current(); + let cpu_name = hardware + .get_cpu_devices() + .await + .unwrap() + .get(0) + .unwrap() + .public_properties + .name + .clone(); + let gpu_name = hardware + .get_gpu_devices() + .await + .unwrap() + .get(0) + .unwrap() + .public_properties + .name + .clone(); + let os = PlatformUtils::detect_current_os(); + let full_data = FullTelemetryData { event_name: data.event_name, event_value: data.event_value, @@ -174,6 +199,9 @@ async fn send_telemetry_data( user_id: None, app_id, version, + os: os.to_string(), + cpu_name, + gpu_name, }; let request_builder = request .post(api_url) diff --git a/src-tauri/src/utils/platform_utils.rs b/src-tauri/src/utils/platform_utils.rs index 58352c5bf..0c27eeeb6 100644 --- a/src-tauri/src/utils/platform_utils.rs +++ b/src-tauri/src/utils/platform_utils.rs @@ -20,12 +20,24 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::fmt::Display; + pub enum CurrentOperatingSystem { Windows, Linux, MacOS, } +impl Display for CurrentOperatingSystem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + CurrentOperatingSystem::Windows => write!(f, "Windows"), + CurrentOperatingSystem::Linux => write!(f, "Linux"), + CurrentOperatingSystem::MacOS => write!(f, "MacOS"), + } + } +} + pub struct PlatformUtils {} impl PlatformUtils { pub fn detect_current_os() -> CurrentOperatingSystem { From 4cd115967cf1e8321176d58e24408bb37fbf0629 Mon Sep 17 00:00:00 2001 From: Maciej Kozuszek Date: Fri, 13 Dec 2024 16:57:44 +0100 Subject: [PATCH 09/15] Remove unwraps --- src-tauri/src/telemetry_service.rs | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src-tauri/src/telemetry_service.rs b/src-tauri/src/telemetry_service.rs index d23dbe2d8..e5f3678e9 100644 --- a/src-tauri/src/telemetry_service.rs +++ b/src-tauri/src/telemetry_service.rs @@ -172,24 +172,16 @@ async fn send_telemetry_data( ) -> Result<(), TelemetryServiceError> { let request = reqwest::Client::new(); let hardware = HardwareStatusMonitor::current(); - let cpu_name = hardware - .get_cpu_devices() - .await - .unwrap() - .get(0) - .unwrap() - .public_properties - .name - .clone(); - let gpu_name = hardware - .get_gpu_devices() - .await - .unwrap() - .get(0) - .unwrap() - .public_properties - .name - .clone(); + let cpu_name = hardware.get_cpu_devices().await?; + let cpu_name = match cpu_name.first() { + Some(cpu) => cpu.public_properties.name.clone(), + None => "Unknown".to_string(), + }; + let gpu_name = hardware.get_gpu_devices().await?; + let gpu_name = match gpu_name.first() { + Some(gpu) => gpu.public_properties.name.clone(), + None => "Unknown".to_string(), + }; let os = PlatformUtils::detect_current_os(); let full_data = FullTelemetryData { From 7b0e53eeaa9a3c8d30758141f4d18fd3caa76fe9 Mon Sep 17 00:00:00 2001 From: Maciej Kozuszek Date: Fri, 13 Dec 2024 17:23:41 +0100 Subject: [PATCH 10/15] Add user id to event message --- src-tauri/src/main.rs | 22 +++++++++++----------- src-tauri/src/telemetry_service.rs | 12 +++++++++--- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 4f0f8da71..8d025fa5d 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -254,12 +254,22 @@ async fn setup_inner( .initialize(state.airdrop_access_token.clone(), window.clone()) .await?; + let mut telemetry_id = state + .telemetry_manager + .read() + .await + .get_unique_string() + .await; + if telemetry_id.is_empty() { + telemetry_id = "unknown_miner_tari_universe".to_string(); + } + let app_version = app.package_info().version.clone(); state .telemetry_service .write() .await - .init(app_version.to_string()) + .init(app_version.to_string(), telemetry_id.clone()) .await?; let telemetry_service = state.telemetry_service.clone(); let telemetry_service = telemetry_service.read().await; @@ -597,16 +607,6 @@ async fn setup_inner( let base_node_grpc_port = state.node_manager.get_grpc_port().await?; - let mut telemetry_id = state - .telemetry_manager - .read() - .await - .get_unique_string() - .await; - if telemetry_id.is_empty() { - telemetry_id = "unknown_miner_tari_universe".to_string(); - } - let config = state.config.read().await; let p2pool_port = state.p2pool_manager.grpc_port().await; mm_proxy_manager diff --git a/src-tauri/src/telemetry_service.rs b/src-tauri/src/telemetry_service.rs index e5f3678e9..d44e966b0 100644 --- a/src-tauri/src/telemetry_service.rs +++ b/src-tauri/src/telemetry_service.rs @@ -49,7 +49,7 @@ pub struct FullTelemetryData { event_name: String, event_value: Value, created_at: SystemTime, - user_id: Option, + user_id: String, app_id: String, version: String, os: String, @@ -92,7 +92,11 @@ impl TelemetryService { in_memory_config, } } - pub async fn init(&mut self, app_version: String) -> Result<(), TelemetryServiceError> { + pub async fn init( + &mut self, + app_version: String, + user: String, + ) -> Result<(), TelemetryServiceError> { self.version = app_version; let cancellation_token = self.cancellation_token.clone(); let config_cloned = self.config.clone(); @@ -120,6 +124,7 @@ impl TelemetryService { telemetry_api_url.clone(), app_id.clone(), version.clone(), + user.clone(), )) }, 3, @@ -169,6 +174,7 @@ async fn send_telemetry_data( api_url: String, app_id: String, version: String, + user_id: String, ) -> Result<(), TelemetryServiceError> { let request = reqwest::Client::new(); let hardware = HardwareStatusMonitor::current(); @@ -188,7 +194,7 @@ async fn send_telemetry_data( event_name: data.event_name, event_value: data.event_value, created_at: SystemTime::now(), - user_id: None, + user_id, app_id, version, os: os.to_string(), From b8bad2e56af907db4469a446fce036018abea84d Mon Sep 17 00:00:00 2001 From: Maciej Kozuszek Date: Tue, 7 Jan 2025 10:49:00 +0100 Subject: [PATCH 11/15] Remove redundant imports --- src-tauri/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index c8272986b..599379924 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -31,7 +31,7 @@ use log::{debug, error, info, warn}; use node_adapter::BaseNodeStatus; use p2pool::models::Connections; use serde_json::json; -use std::fs::{create_dir_all, remove_dir_all, remove_file, File}; +use std::fs::{remove_dir_all, remove_file}; use std::ops::Deref; use std::path::Path; use tauri_plugin_cli::CliExt; From c9632f4f4fdd2cf4d50b8fc71e50a48234134a96 Mon Sep 17 00:00:00 2001 From: Maciej Kozuszek Date: Tue, 7 Jan 2025 13:49:02 +0100 Subject: [PATCH 12/15] Fix invoke typings --- src/types/invoke.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/invoke.ts b/src/types/invoke.ts index 21c079b44..0ddaffcb6 100644 --- a/src/types/invoke.ts +++ b/src/types/invoke.ts @@ -35,8 +35,8 @@ declare module '@tauri-apps/api/core' { function invoke(param: 'start_mining'): Promise; function invoke(param: 'stop_mining'): Promise; function invoke(param: 'set_allow_telemetry', payload: { allow_telemetry: boolean }): Promise; - function invoke(param: 'send_data_telemetry_service', payload: { threads: number }): Promise; - function invoke(param: 'set_user_inactivity_timeout', payload: { event_name: string; data: object }): Promise; + function invoke(param: 'send_data_telemetry_service', payload: { eventName: string; data: object }): Promise; + function invoke(param: 'set_user_inactivity_timeout', payload: { timeout: number }): Promise; function invoke(param: 'update_applications'): Promise; function invoke( param: 'set_mode', From 7c5994ccf0fa84a34e9e054a7177ece0699e7211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ko=C5=BCuszek?= Date: Fri, 10 Jan 2025 16:30:15 +0100 Subject: [PATCH 13/15] Refactor after review --- package-lock.json | 4 +- src-tauri/src/main.rs | 5 +- src-tauri/src/telemetry_service.rs | 74 ++++++++++++++++----------- src-tauri/src/utils/platform_utils.rs | 1 + 4 files changed, 49 insertions(+), 35 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0d5a94600..8b945af52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "tari-universe", - "version": "0.8.38", + "version": "0.8.41", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "tari-universe", - "version": "0.8.38", + "version": "0.8.41", "dependencies": { "@floating-ui/react": "^0.26.28", "@lottiefiles/dotlottie-react": "^0.10.1", diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 204ca7e5f..a26f27e21 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -31,7 +31,6 @@ use node_adapter::BaseNodeStatus; use p2pool::models::Connections; use serde_json::json; use std::fs::{remove_dir_all, remove_file}; -use std::ops::Deref; use std::path::Path; use tauri_plugin_cli::CliExt; use telemetry_service::TelemetryService; @@ -275,8 +274,7 @@ async fn setup_inner( .init(app_version.to_string(), telemetry_id.clone()) .await?; let telemetry_service = state.telemetry_service.clone(); - let telemetry_service = telemetry_service.read().await; - let telemetry_service = telemetry_service.deref(); + let telemetry_service = &telemetry_service.read().await; let mut binary_resolver = BinaryResolver::current().write().await; let should_check_for_update = now @@ -816,7 +814,6 @@ fn main() { ); let telemetry_service = TelemetryService::new( app_config_raw.anon_id().to_string(), - "0.0.0".to_string(), app_config.clone(), app_in_memory_config.clone(), ); diff --git a/src-tauri/src/telemetry_service.rs b/src-tauri/src/telemetry_service.rs index d44e966b0..702959457 100644 --- a/src-tauri/src/telemetry_service.rs +++ b/src-tauri/src/telemetry_service.rs @@ -31,9 +31,11 @@ use tokio::sync::{ use tokio_util::sync::CancellationToken; use crate::{ - app_config::AppConfig, app_in_memory_config::AppInMemoryConfig, - hardware::hardware_status_monitor::HardwareStatusMonitor, process_utils::retry_with_backoff, - utils::platform_utils::PlatformUtils, + app_config::AppConfig, + app_in_memory_config::AppInMemoryConfig, + hardware::hardware_status_monitor::HardwareStatusMonitor, + process_utils::retry_with_backoff, + utils::platform_utils::{CurrentOperatingSystem, PlatformUtils}, }; const LOG_TARGET: &str = "tari::universe::telemetry_service"; @@ -78,14 +80,13 @@ pub struct TelemetryService { impl TelemetryService { pub fn new( app_id: String, - version: String, config: Arc>, in_memory_config: Arc>, ) -> Self { let cancellation_token = CancellationToken::new(); TelemetryService { app_id, - version, + version: "0.0.0".to_string(), tx_channel: None, cancellation_token, config, @@ -97,6 +98,19 @@ impl TelemetryService { app_version: String, user: String, ) -> Result<(), TelemetryServiceError> { + let hardware = HardwareStatusMonitor::current(); + let cpu_name = hardware.get_cpu_devices().await?; + let cpu_name = match cpu_name.first() { + Some(cpu) => cpu.public_properties.name.clone(), + None => "Unknown".to_string(), + }; + let gpu_name = hardware.get_gpu_devices().await?; + let gpu_name = match gpu_name.first() { + Some(gpu) => gpu.public_properties.name.clone(), + None => "Unknown".to_string(), + }; + let os = PlatformUtils::detect_current_os(); + self.version = app_version; let cancellation_token = self.cancellation_token.clone(); let config_cloned = self.config.clone(); @@ -111,6 +125,14 @@ impl TelemetryService { let (tx, mut rx) = mpsc::channel(128); self.tx_channel = Some(tx); tokio::spawn(async move { + let system_info = SystemInfo { + app_id, + version, + user_id: user, + cpu_name, + gpu_name, + os, + }; tokio::select! { _ = async { debug!(target: LOG_TARGET, "TelemetryService::init has been started"); @@ -122,9 +144,7 @@ impl TelemetryService { Box::pin(send_telemetry_data( telemetry_data.clone(), telemetry_api_url.clone(), - app_id.clone(), - version.clone(), - user.clone(), + system_info.clone(), )) }, 3, @@ -169,37 +189,33 @@ impl TelemetryService { } } -async fn send_telemetry_data( - data: TelemetryData, - api_url: String, +#[derive(Clone)] +struct SystemInfo { app_id: String, version: String, user_id: String, + os: CurrentOperatingSystem, + cpu_name: String, + gpu_name: String, +} + +async fn send_telemetry_data( + data: TelemetryData, + api_url: String, + system_info: SystemInfo, ) -> Result<(), TelemetryServiceError> { let request = reqwest::Client::new(); - let hardware = HardwareStatusMonitor::current(); - let cpu_name = hardware.get_cpu_devices().await?; - let cpu_name = match cpu_name.first() { - Some(cpu) => cpu.public_properties.name.clone(), - None => "Unknown".to_string(), - }; - let gpu_name = hardware.get_gpu_devices().await?; - let gpu_name = match gpu_name.first() { - Some(gpu) => gpu.public_properties.name.clone(), - None => "Unknown".to_string(), - }; - let os = PlatformUtils::detect_current_os(); let full_data = FullTelemetryData { event_name: data.event_name, event_value: data.event_value, created_at: SystemTime::now(), - user_id, - app_id, - version, - os: os.to_string(), - cpu_name, - gpu_name, + user_id: system_info.user_id, + app_id: system_info.app_id, + version: system_info.version, + os: system_info.os.to_string(), + cpu_name: system_info.cpu_name, + gpu_name: system_info.gpu_name, }; let request_builder = request .post(api_url) diff --git a/src-tauri/src/utils/platform_utils.rs b/src-tauri/src/utils/platform_utils.rs index 0c27eeeb6..6c5da82ac 100644 --- a/src-tauri/src/utils/platform_utils.rs +++ b/src-tauri/src/utils/platform_utils.rs @@ -22,6 +22,7 @@ use std::fmt::Display; +#[derive(Clone)] pub enum CurrentOperatingSystem { Windows, Linux, From 7be06d7938d54ec1bee4bc0323af0522faae427a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ko=C5=BCuszek?= Date: Mon, 13 Jan 2025 11:26:26 +0100 Subject: [PATCH 14/15] Replace drop(..) with let _ = .. for telemetry service --- src-tauri/src/main.rs | 245 +++++++++++++++++++++--------------------- 1 file changed, 125 insertions(+), 120 deletions(-) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index a26f27e21..382b51ef1 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -307,17 +307,15 @@ async fn setup_inner( sleep(Duration::from_secs(1)); } - drop( - telemetry_service - .send( - "checking-latest-version-node".to_string(), - json!({ - "service": "node_manager", - "percentage": 5, - }), - ) - .await, - ); + let _ = telemetry_service + .send( + "checking-latest-version-node".to_string(), + json!({ + "service": "node_manager", + "percentage": 5, + }), + ) + .await; progress.set_max(10).await; progress .update("checking-latest-version-node".to_string(), None, 0) @@ -332,17 +330,15 @@ async fn setup_inner( .await?; sleep(Duration::from_secs(1)); - drop( - telemetry_service - .send( - "checking-latest-version-mmproxy".to_string(), - json!({ - "service": "mmproxy", - "percentage": 10, - }), - ) - .await, - ); + let _ = telemetry_service + .send( + "checking-latest-version-mmproxy".to_string(), + json!({ + "service": "mmproxy", + "percentage": 10, + }), + ) + .await; progress.set_max(15).await; progress .update("checking-latest-version-mmproxy".to_string(), None, 0) @@ -357,17 +353,15 @@ async fn setup_inner( .await?; sleep(Duration::from_secs(1)); - drop( - telemetry_service - .send( - "checking-latest-version-wallet".to_string(), - json!({ - "service": "wallet", - "percentage": 15, - }), - ) - .await, - ); + let _ = telemetry_service + .send( + "checking-latest-version-wallet".to_string(), + json!({ + "service": "wallet", + "percentage": 15, + }), + ) + .await; progress.set_max(20).await; progress .update("checking-latest-version-wallet".to_string(), None, 0) @@ -382,17 +376,15 @@ async fn setup_inner( .await?; sleep(Duration::from_secs(1)); - drop( - telemetry_service - .send( - "checking-latest-version-gpuminer".to_string(), - json!({ - "service": "gpuminer", - "percentage":20, - }), - ) - .await, - ); + let _ = telemetry_service + .send( + "checking-latest-version-gpuminer".to_string(), + json!({ + "service": "gpuminer", + "percentage":20, + }), + ) + .await; progress.set_max(25).await; progress .update("checking-latest-version-gpuminer".to_string(), None, 0) @@ -407,17 +399,15 @@ async fn setup_inner( .await?; sleep(Duration::from_secs(1)); - drop( - telemetry_service - .send( - "checking-latest-version-xmrig".to_string(), - json!({ - "service": "xmrig", - "percentage":25, - }), - ) - .await, - ); + let _ = telemetry_service + .send( + "checking-latest-version-xmrig".to_string(), + json!({ + "service": "xmrig", + "percentage":25, + }), + ) + .await; progress.set_max(30).await; progress .update("checking-latest-version-xmrig".to_string(), None, 0) @@ -432,17 +422,15 @@ async fn setup_inner( .await?; sleep(Duration::from_secs(1)); - drop( - telemetry_service - .send( - "checking-latest-version-sha-p2pool".to_string(), - json!({ - "service": "sha_p2pool", - "percentage":30, - }), - ) - .await, - ); + let _ = telemetry_service + .send( + "checking-latest-version-sha-p2pool".to_string(), + json!({ + "service": "sha_p2pool", + "percentage":30, + }), + ) + .await; progress.set_max(35).await; progress .update("checking-latest-version-sha-p2pool".to_string(), None, 0) @@ -482,6 +470,15 @@ async fn setup_inner( .await?; tor_control_port = state.tor_manager.get_control_port().await?; } + let _ = telemetry_service + .send( + "waiting-for-minotari-node-to-start".to_string(), + json!({ + "service": "minotari_node", + "percentage":35, + }), + ) + .await; progress.set_max(37).await; progress .update("waiting-for-minotari-node-to-start".to_string(), None, 0) @@ -505,6 +502,15 @@ async fn setup_inner( if code == 114 { warn!(target: LOG_TARGET, "Database for node is corrupt or needs a reset, deleting and trying again."); state.node_manager.clean_data_folder(&data_dir).await?; + let _ = telemetry_service + .send( + "resetting-minotari-node-database".to_string(), + json!({ + "service": "minotari_node", + "percentage":37, + }), + ) + .await; progress.set_max(38).await; progress .update("minotari-node-restarting".to_string(), None, 0) @@ -521,17 +527,15 @@ async fn setup_inner( } info!(target: LOG_TARGET, "Node has started and is ready"); - drop( - telemetry_service - .send( - "waiting-for-wallet".to_string(), - json!({ - "service": "wallet", - "percentage":35, - }), - ) - .await, - ); + let _ = telemetry_service + .send( + "waiting-for-wallet".to_string(), + json!({ + "service": "wallet", + "percentage":35, + }), + ) + .await; progress.set_max(40).await; progress .update("waiting-for-wallet".to_string(), None, 0) @@ -546,37 +550,42 @@ async fn setup_inner( ) .await?; + let _ = telemetry_service + .send( + "wallet-started".to_string(), + json!({ + "service": "wallet", + "percentage":40, + }), + ) + .await; progress.set_max(45).await; progress.update("wallet-started".to_string(), None, 0).await; progress .update("waiting-for-node".to_string(), None, 0) .await; + let _ = telemetry_service + .send( + "preparing-for-initial-sync".to_string(), + json!({ + "service": "initial_sync", + "percentage":45, + }), + ) + .await; progress.set_max(75).await; - drop( - telemetry_service + state.node_manager.wait_synced(progress.clone()).await?; + + if state.config.read().await.p2pool_enabled() { + let _ = telemetry_service .send( - "preparing-for-initial-sync".to_string(), + "starting-p2pool".to_string(), json!({ - "service": "initial_sync", - "percentage":45, + "service": "starting_p2pool", + "percentage":75, }), ) - .await, - ); - state.node_manager.wait_synced(progress.clone()).await?; - - if state.config.read().await.p2pool_enabled() { - drop( - telemetry_service - .send( - "starting-p2pool".to_string(), - json!({ - "service": "starting_p2pool", - "percentage":75, - }), - ) - .await, - ); + .await; progress.set_max(85).await; progress .update("starting-p2pool".to_string(), None, 0) @@ -600,17 +609,15 @@ async fn setup_inner( .await?; } - drop( - telemetry_service - .send( - "starting-mmproxy".to_string(), - json!({ - "service": "starting_mmproxy", - "percentage":85, - }), - ) - .await, - ); + let _ = telemetry_service + .send( + "starting-mmproxy".to_string(), + json!({ + "service": "starting_mmproxy", + "percentage":85, + }), + ) + .await; progress.set_max(100).await; progress .update("starting-mmproxy".to_string(), None, 0) @@ -637,17 +644,15 @@ async fn setup_inner( .await?; mm_proxy_manager.wait_ready().await?; *state.is_setup_finished.write().await = true; - drop( - telemetry_service - .send( - "setup-finished".to_string(), - json!({ - "service": "setup_finished", - "percentage":100, - }), - ) - .await, - ); + let _ = telemetry_service + .send( + "setup-finished".to_string(), + json!({ + "service": "setup_finished", + "percentage":100, + }), + ) + .await; drop( app.clone() .emit( From f7b0bcf727ba4c5cfcb0e9c0cf3e4b6b106932b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Ko=C5=BCuszek?= Date: Mon, 13 Jan 2025 11:59:23 +0100 Subject: [PATCH 15/15] Fix CI linter errors --- src-tauri/src/main.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 75df84d1d..8f3a42896 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -307,7 +307,7 @@ async fn setup_inner( sleep(Duration::from_secs(1)); } - let _ = telemetry_service + let _unused = telemetry_service .send( "checking-latest-version-node".to_string(), json!({ @@ -330,7 +330,7 @@ async fn setup_inner( .await?; sleep(Duration::from_secs(1)); - let _ = telemetry_service + let _unused = telemetry_service .send( "checking-latest-version-mmproxy".to_string(), json!({ @@ -353,7 +353,7 @@ async fn setup_inner( .await?; sleep(Duration::from_secs(1)); - let _ = telemetry_service + let _unused = telemetry_service .send( "checking-latest-version-wallet".to_string(), json!({ @@ -376,7 +376,7 @@ async fn setup_inner( .await?; sleep(Duration::from_secs(1)); - let _ = telemetry_service + let _unused = telemetry_service .send( "checking-latest-version-gpuminer".to_string(), json!({ @@ -399,7 +399,7 @@ async fn setup_inner( .await?; sleep(Duration::from_secs(1)); - let _ = telemetry_service + let _unused = telemetry_service .send( "checking-latest-version-xmrig".to_string(), json!({ @@ -422,7 +422,7 @@ async fn setup_inner( .await?; sleep(Duration::from_secs(1)); - let _ = telemetry_service + let _unused = telemetry_service .send( "checking-latest-version-sha-p2pool".to_string(), json!({ @@ -470,7 +470,7 @@ async fn setup_inner( .await?; tor_control_port = state.tor_manager.get_control_port().await?; } - let _ = telemetry_service + let _unused = telemetry_service .send( "waiting-for-minotari-node-to-start".to_string(), json!({ @@ -502,7 +502,7 @@ async fn setup_inner( if code == 114 { warn!(target: LOG_TARGET, "Database for node is corrupt or needs a reset, deleting and trying again."); state.node_manager.clean_data_folder(&data_dir).await?; - let _ = telemetry_service + let _unused = telemetry_service .send( "resetting-minotari-node-database".to_string(), json!({ @@ -527,7 +527,7 @@ async fn setup_inner( } info!(target: LOG_TARGET, "Node has started and is ready"); - let _ = telemetry_service + let _unused = telemetry_service .send( "waiting-for-wallet".to_string(), json!({ @@ -550,7 +550,7 @@ async fn setup_inner( ) .await?; - let _ = telemetry_service + let _unused = telemetry_service .send( "wallet-started".to_string(), json!({ @@ -564,7 +564,7 @@ async fn setup_inner( progress .update("waiting-for-node".to_string(), None, 0) .await; - let _ = telemetry_service + let _unused = telemetry_service .send( "preparing-for-initial-sync".to_string(), json!({ @@ -577,7 +577,7 @@ async fn setup_inner( state.node_manager.wait_synced(progress.clone()).await?; if state.config.read().await.p2pool_enabled() { - let _ = telemetry_service + let _unused = telemetry_service .send( "starting-p2pool".to_string(), json!({ @@ -609,7 +609,7 @@ async fn setup_inner( .await?; } - let _ = telemetry_service + let _unused = telemetry_service .send( "starting-mmproxy".to_string(), json!({ @@ -644,7 +644,7 @@ async fn setup_inner( .await?; mm_proxy_manager.wait_ready().await?; *state.is_setup_finished.write().await = true; - let _ = telemetry_service + let _unused = telemetry_service .send( "setup-finished".to_string(), json!({