mirror of
https://github.com/meilisearch/MeiliSearch
synced 2025-07-03 11:57:07 +02:00
Merge #209
209: Integrate amplitude r=MarinPostma a=irevoire And merge the sentry and amplitude usage under one “Enable analytics” flag closes #180 Co-authored-by: Tamo <tamo@meilisearch.com> Co-authored-by: Irevoire <tamo@meilisearch.com>
This commit is contained in:
commit
2062b10b79
7 changed files with 221 additions and 89 deletions
|
@ -1,14 +1,10 @@
|
|||
use std::hash::{Hash, Hasher};
|
||||
use std::{error, thread};
|
||||
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use log::error;
|
||||
use log::debug;
|
||||
use serde::Serialize;
|
||||
use serde_qs as qs;
|
||||
use siphasher::sip::SipHasher;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::helpers::EnvSizer;
|
||||
use crate::Data;
|
||||
use crate::Opt;
|
||||
|
||||
|
@ -22,26 +18,21 @@ struct EventProperties {
|
|||
}
|
||||
|
||||
impl EventProperties {
|
||||
fn from(data: Data) -> Result<EventProperties, Box<dyn error::Error>> {
|
||||
let mut index_list = Vec::new();
|
||||
async fn from(data: Data) -> anyhow::Result<EventProperties> {
|
||||
let stats = data.index_controller.get_all_stats().await?;
|
||||
|
||||
let reader = data.db.main_read_txn()?;
|
||||
|
||||
for index_uid in data.db.indexes_uids() {
|
||||
if let Some(index) = data.db.open_index(&index_uid) {
|
||||
let number_of_documents = index.main.number_of_documents(&reader)?;
|
||||
index_list.push(number_of_documents);
|
||||
}
|
||||
}
|
||||
|
||||
let database_size = data.env.size();
|
||||
|
||||
let last_update_timestamp = data.db.last_update(&reader)?.map(|u| u.timestamp());
|
||||
let database_size = stats.database_size;
|
||||
let last_update_timestamp = stats.last_update.map(|u| u.timestamp());
|
||||
let number_of_documents = stats
|
||||
.indexes
|
||||
.values()
|
||||
.map(|index| index.number_of_documents)
|
||||
.collect();
|
||||
|
||||
Ok(EventProperties {
|
||||
database_size,
|
||||
last_update_timestamp,
|
||||
number_of_documents: index_list,
|
||||
number_of_documents,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -68,10 +59,10 @@ struct Event<'a> {
|
|||
#[derive(Debug, Serialize)]
|
||||
struct AmplitudeRequest<'a> {
|
||||
api_key: &'a str,
|
||||
event: &'a str,
|
||||
events: Vec<Event<'a>>,
|
||||
}
|
||||
|
||||
pub fn analytics_sender(data: Data, opt: Opt) {
|
||||
pub async fn analytics_sender(data: Data, opt: Opt) {
|
||||
let username = whoami::username();
|
||||
let hostname = whoami::hostname();
|
||||
let platform = whoami::platform();
|
||||
|
@ -93,7 +84,7 @@ pub fn analytics_sender(data: Data, opt: Opt) {
|
|||
let time = n.as_secs();
|
||||
let event_type = "runtime_tick";
|
||||
let elapsed_since_start = first_start.elapsed().as_secs() / 86_400; // One day
|
||||
let event_properties = EventProperties::from(data.clone()).ok();
|
||||
let event_properties = EventProperties::from(data.clone()).await.ok();
|
||||
let app_version = env!("CARGO_PKG_VERSION").to_string();
|
||||
let app_version = app_version.as_str();
|
||||
let user_email = std::env::var("MEILI_USER_EMAIL").ok();
|
||||
|
@ -114,20 +105,22 @@ pub fn analytics_sender(data: Data, opt: Opt) {
|
|||
user_properties,
|
||||
event_properties,
|
||||
};
|
||||
let event = serde_json::to_string(&event).unwrap();
|
||||
|
||||
let request = AmplitudeRequest {
|
||||
api_key: AMPLITUDE_API_KEY,
|
||||
event: &event,
|
||||
events: vec![event],
|
||||
};
|
||||
|
||||
let body = qs::to_string(&request).unwrap();
|
||||
let response = ureq::post("https://api.amplitude.com/httpapi").send_string(&body);
|
||||
if !response.ok() {
|
||||
let body = response.into_string().unwrap();
|
||||
error!("Unsuccessful call to Amplitude: {}", body);
|
||||
let response = reqwest::Client::new()
|
||||
.post("https://api2.amplitude.com/2/httpapi")
|
||||
.timeout(Duration::from_secs(60)) // 1 minute max
|
||||
.json(&request)
|
||||
.send()
|
||||
.await;
|
||||
if let Err(e) = response {
|
||||
debug!("Unsuccessful call to Amplitude: {}", e);
|
||||
}
|
||||
|
||||
thread::sleep(Duration::from_secs(3600)) // one hour
|
||||
tokio::time::sleep(Duration::from_secs(3600)).await;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@ mod index_controller;
|
|||
pub mod option;
|
||||
pub mod routes;
|
||||
|
||||
#[cfg(all(not(debug_assertions), feature = "analytics"))]
|
||||
pub mod analytics;
|
||||
|
||||
pub use self::data::Data;
|
||||
pub use option::Opt;
|
||||
|
||||
|
|
|
@ -5,12 +5,16 @@ use main_error::MainError;
|
|||
use meilisearch_http::{create_app, Data, Opt};
|
||||
use structopt::StructOpt;
|
||||
|
||||
//mod analytics;
|
||||
#[cfg(all(not(debug_assertions), feature = "analytics"))]
|
||||
use meilisearch_http::analytics;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[global_allocator]
|
||||
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
||||
|
||||
#[cfg(all(not(debug_assertions), feature = "analytics"))]
|
||||
const SENTRY_DSN: &str = "https://5ddfa22b95f241198be2271aaf028653@sentry.io/3060337";
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> Result<(), MainError> {
|
||||
let opt = Opt::from_args();
|
||||
|
@ -26,16 +30,17 @@ async fn main() -> Result<(), MainError> {
|
|||
.into(),
|
||||
);
|
||||
}
|
||||
#[cfg(all(not(debug_assertions), feature = "sentry"))]
|
||||
if !opt.no_sentry {
|
||||
let logger = sentry::integrations::log::SentryLogger::with_dest(log_builder.build());
|
||||
#[cfg(all(not(debug_assertions), feature = "analytics"))]
|
||||
if !opt.no_analytics {
|
||||
let logger =
|
||||
sentry::integrations::log::SentryLogger::with_dest(log_builder.build());
|
||||
log::set_boxed_logger(Box::new(logger))
|
||||
.map(|()| log::set_max_level(log::LevelFilter::Info))
|
||||
.unwrap();
|
||||
|
||||
let sentry = sentry::init(sentry::ClientOptions {
|
||||
release: sentry::release_name!(),
|
||||
dsn: Some(opt.sentry_dsn.parse()?),
|
||||
dsn: Some(SENTRY_DSN.parse()?),
|
||||
..Default::default()
|
||||
});
|
||||
// sentry must stay alive as long as possible
|
||||
|
@ -50,25 +55,14 @@ async fn main() -> Result<(), MainError> {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
//if let Some(path) = &opt.import_snapshot {
|
||||
//snapshot::load_snapshot(&opt.db_path, path, opt.ignore_snapshot_if_db_exists, opt.ignore_missing_snapshot)?;
|
||||
//}
|
||||
|
||||
let data = Data::new(opt.clone())?;
|
||||
|
||||
//if !opt.no_analytics {
|
||||
//let analytics_data = data.clone();
|
||||
//let analytics_opt = opt.clone();
|
||||
//thread::spawn(move || analytics::analytics_sender(analytics_data, analytics_opt));
|
||||
//}
|
||||
|
||||
//if let Some(path) = &opt.import_dump {
|
||||
//dump::import_dump(&data, path, opt.dump_batch_size)?;
|
||||
//}
|
||||
|
||||
//if opt.schedule_snapshot {
|
||||
//snapshot::schedule_snapshot(data.clone(), &opt.snapshot_dir, opt.snapshot_interval_sec.unwrap_or(86400))?;
|
||||
//}
|
||||
#[cfg(all(not(debug_assertions), feature = "analytics"))]
|
||||
if !opt.no_analytics {
|
||||
let analytics_data = data.clone();
|
||||
let analytics_opt = opt.clone();
|
||||
tokio::task::spawn(analytics::analytics_sender(analytics_data, analytics_opt));
|
||||
}
|
||||
|
||||
print_launch_resume(&opt, &data);
|
||||
|
||||
|
@ -127,24 +121,21 @@ pub fn print_launch_resume(opt: &Opt, data: &Data) {
|
|||
env!("CARGO_PKG_VERSION").to_string()
|
||||
);
|
||||
|
||||
#[cfg(all(not(debug_assertions), feature = "sentry"))]
|
||||
eprintln!(
|
||||
"Sentry DSN:\t\t{:?}",
|
||||
if !opt.no_sentry {
|
||||
&opt.sentry_dsn
|
||||
#[cfg(all(not(debug_assertions), feature = "analytics"))]
|
||||
{
|
||||
if opt.no_analytics {
|
||||
eprintln!("Anonymous telemetry:\t\"Disabled\"");
|
||||
} else {
|
||||
"Disabled"
|
||||
}
|
||||
);
|
||||
eprintln!(
|
||||
"
|
||||
Thank you for using MeiliSearch!
|
||||
|
||||
eprintln!(
|
||||
"Anonymous telemetry:\t{:?}",
|
||||
if !opt.no_analytics {
|
||||
"Enabled"
|
||||
} else {
|
||||
"Disabled"
|
||||
We collect anonymized analytics to improve our product and your experience. To learn more, including how to turn off analytics, visit our dedicated documentation page: https://docs.meilisearch.com/reference/features/configuration.html#analytics
|
||||
|
||||
Anonymous telemetry: \"Enabled\""
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
eprintln!();
|
||||
|
||||
|
|
|
@ -96,21 +96,6 @@ pub struct Opt {
|
|||
#[structopt(long, env = "MEILI_MASTER_KEY")]
|
||||
pub master_key: Option<String>,
|
||||
|
||||
/// The Sentry DSN to use for error reporting. This defaults to the MeiliSearch Sentry project.
|
||||
/// You can disable sentry all together using the `--no-sentry` flag or `MEILI_NO_SENTRY` environment variable.
|
||||
#[cfg(all(not(debug_assertions), feature = "sentry"))]
|
||||
#[structopt(
|
||||
long,
|
||||
env = "SENTRY_DSN",
|
||||
default_value = "https://5ddfa22b95f241198be2271aaf028653@sentry.io/3060337"
|
||||
)]
|
||||
pub sentry_dsn: String,
|
||||
|
||||
/// Disable Sentry error reporting.
|
||||
#[structopt(long, env = "MEILI_NO_SENTRY")]
|
||||
#[cfg(all(not(debug_assertions), feature = "sentry"))]
|
||||
pub no_sentry: bool,
|
||||
|
||||
/// This environment variable must be set to `production` if you are running in production.
|
||||
/// If the server is running in development mode more logs will be displayed,
|
||||
/// and the master key can be avoided which implies that there is no security on the updates routes.
|
||||
|
@ -119,6 +104,7 @@ pub struct Opt {
|
|||
pub env: String,
|
||||
|
||||
/// Do not send analytics to Meili.
|
||||
#[cfg(all(not(debug_assertions), feature = "analytics"))]
|
||||
#[structopt(long, env = "MEILI_NO_ANALYTICS")]
|
||||
pub no_analytics: bool,
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue