diff --git a/meilisearch-http/src/analytics.rs b/meilisearch-http/src/analytics.rs index daed4c233..037c68524 100644 --- a/meilisearch-http/src/analytics.rs +++ b/meilisearch-http/src/analytics.rs @@ -1,3 +1,4 @@ +use actix_web::HttpRequest; use serde_json::Value; use std::fmt::Display; use std::fs::read_to_string; @@ -8,6 +9,8 @@ use crate::Opt; #[cfg(all(not(debug_assertions), feature = "analytics"))] mod segment { use crate::analytics::Analytics; + use actix_web::http::header::USER_AGENT; + use actix_web::HttpRequest; use meilisearch_lib::index_controller::Stats; use meilisearch_lib::MeiliSearch; use once_cell::sync::Lazy; @@ -105,7 +108,7 @@ mod segment { // batch the launched for the first time track event if first_time_run { - segment.publish("Launched for the first time".to_string(), json!({})); + segment.publish("Launched for the first time".to_string(), json!({}), None); } // start the runtime tick @@ -142,7 +145,12 @@ mod segment { #[async_trait::async_trait] impl super::Analytics for SegmentAnalytics { - fn publish(&'static self, event_name: String, send: Value) { + fn publish(&'static self, event_name: String, send: Value, request: Option<&HttpRequest>) { + let content_type = request + .map(|req| req.headers().get(USER_AGENT)) + .flatten() + .map(|header| header.to_str().unwrap_or("unknown").to_string()); + tokio::spawn(async move { println!("ANALYTICS pushing {} in the batcher", event_name); let _ = self @@ -152,6 +160,7 @@ mod segment { .push(Track { user: self.user.clone(), event: event_name.clone(), + context: content_type.map(|user_agent| json!({ "user-agent": user_agent.split(";").map(|u| u.trim()).collect::>() })), properties: send, ..Default::default() }) @@ -191,7 +200,7 @@ impl MockAnalytics { #[async_trait::async_trait] impl Analytics for MockAnalytics { /// This is a noop and should be optimized out - fn publish(&'static self, _event_name: String, _send: Value) {} + fn publish(&'static self, _event_name: String, _send: Value, _request: Option<&HttpRequest>) {} } impl Display for MockAnalytics { @@ -202,5 +211,5 @@ impl Display for MockAnalytics { #[async_trait::async_trait] pub trait Analytics: Display + Sync + Send { - fn publish(&'static self, event_name: String, send: Value); + fn publish(&'static self, event_name: String, send: Value, request: Option<&HttpRequest>); } diff --git a/meilisearch-http/src/routes/indexes/documents.rs b/meilisearch-http/src/routes/indexes/documents.rs index 1e8a803dc..8d3630713 100644 --- a/meilisearch-http/src/routes/indexes/documents.rs +++ b/meilisearch-http/src/routes/indexes/documents.rs @@ -148,6 +148,7 @@ pub async fn add_documents( "with_primary_key": params.primary_key, "index_creation": meilisearch.get_index(path.index_uid.clone()).await.is_ok(), }), + Some(&req), ); document_addition( @@ -182,6 +183,7 @@ pub async fn update_documents( "with_primary_key": params.primary_key, "index_creation": meilisearch.get_index(path.index_uid.clone()).await.is_ok(), }), + Some(&req), ); document_addition( diff --git a/meilisearch-http/src/routes/indexes/mod.rs b/meilisearch-http/src/routes/indexes/mod.rs index 18a2e42e8..743da5b6a 100644 --- a/meilisearch-http/src/routes/indexes/mod.rs +++ b/meilisearch-http/src/routes/indexes/mod.rs @@ -1,4 +1,4 @@ -use actix_web::{web, HttpResponse}; +use actix_web::{web, HttpRequest, HttpResponse}; use chrono::{DateTime, Utc}; use log::debug; use meilisearch_lib::index_controller::IndexSettings; @@ -56,6 +56,7 @@ pub struct IndexCreateRequest { pub async fn create_index( meilisearch: GuardedData, body: web::Json, + req: HttpRequest, analytics: web::Data<&'static dyn Analytics>, ) -> Result { let body = body.into_inner(); @@ -63,6 +64,7 @@ pub async fn create_index( analytics.publish( "Index Created".to_string(), json!({ "with_primary_key": body.primary_key}), + Some(&req), ); let meta = meilisearch.create_index(body.uid, body.primary_key).await?; Ok(HttpResponse::Created().json(meta)) @@ -98,6 +100,7 @@ pub async fn update_index( meilisearch: GuardedData, path: web::Path, body: web::Json, + req: HttpRequest, analytics: web::Data<&'static dyn Analytics>, ) -> Result { debug!("called with params: {:?}", body); @@ -105,6 +108,7 @@ pub async fn update_index( analytics.publish( "Index Updated".to_string(), json!({ "with_primary_key": body.primary_key}), + Some(&req), ); let settings = IndexSettings { uid: body.uid, diff --git a/meilisearch-http/src/routes/indexes/search.rs b/meilisearch-http/src/routes/indexes/search.rs index beefd06ed..c0d3f1462 100644 --- a/meilisearch-http/src/routes/indexes/search.rs +++ b/meilisearch-http/src/routes/indexes/search.rs @@ -1,4 +1,4 @@ -use actix_web::{web, HttpResponse}; +use actix_web::{web, HttpRequest, HttpResponse}; use log::debug; use meilisearch_lib::index::{default_crop_length, SearchQuery, DEFAULT_SEARCH_LIMIT}; use meilisearch_lib::MeiliSearch; @@ -110,6 +110,7 @@ pub async fn search_with_url_query( meilisearch: GuardedData, path: web::Path, params: web::Query, + req: HttpRequest, analytics: web::Data<&'static dyn Analytics>, ) -> Result { debug!("called with params: {:?}", params); @@ -127,7 +128,11 @@ pub async fn search_with_url_query( assert!(!search_result.exhaustive_nb_hits); analytics_value["response_time"] = json!(search_result.processing_time_ms as u64); - analytics.publish("Documents Searched".to_string(), analytics_value); + analytics.publish( + "Documents Searched".to_string(), + analytics_value, + Some(&req), + ); debug!("returns: {:?}", search_result); Ok(HttpResponse::Ok().json(search_result)) @@ -137,6 +142,7 @@ pub async fn search_with_post( meilisearch: GuardedData, path: web::Path, params: web::Json, + req: HttpRequest, analytics: web::Data<&'static dyn Analytics>, ) -> Result { let query = params.into_inner(); @@ -154,7 +160,11 @@ pub async fn search_with_post( assert!(!search_result.exhaustive_nb_hits); analytics_value["response_time"] = json!(search_result.processing_time_ms as u64); - analytics.publish("Documents Searched".to_string(), analytics_value); + analytics.publish( + "Documents Searched".to_string(), + analytics_value, + Some(&req), + ); debug!("returns: {:?}", search_result); Ok(HttpResponse::Ok().json(search_result)) diff --git a/meilisearch-http/src/routes/indexes/settings.rs b/meilisearch-http/src/routes/indexes/settings.rs index 8eed67d7d..f70eb1222 100644 --- a/meilisearch-http/src/routes/indexes/settings.rs +++ b/meilisearch-http/src/routes/indexes/settings.rs @@ -1,6 +1,6 @@ use log::debug; -use actix_web::{web, HttpResponse}; +use actix_web::{web, HttpRequest, HttpResponse}; use meilisearch_lib::index::{Settings, Unchecked}; use meilisearch_lib::index_controller::Update; use meilisearch_lib::MeiliSearch; @@ -15,7 +15,7 @@ macro_rules! make_setting_route { ($route:literal, $type:ty, $attr:ident, $camelcase_attr:literal, $analytics_var:ident, $analytics:expr) => { pub mod $attr { use log::debug; - use actix_web::{web, HttpResponse, Resource}; + use actix_web::{web, HttpResponse, HttpRequest, Resource}; use meilisearch_lib::milli::update::Setting; use meilisearch_lib::{MeiliSearch, index::Settings, index_controller::Update}; @@ -42,11 +42,12 @@ macro_rules! make_setting_route { meilisearch: GuardedData, index_uid: actix_web::web::Path, body: actix_web::web::Json>, + req: HttpRequest, $analytics_var: web::Data<&'static dyn Analytics>, ) -> std::result::Result { let body = body.into_inner(); - $analytics(&body); + $analytics(&body, &req); let settings = Settings { $attr: match body { @@ -82,7 +83,7 @@ macro_rules! make_setting_route { } }; ($route:literal, $type:ty, $attr:ident, $camelcase_attr:literal) => { - make_setting_route!($route, $type, $attr, $camelcase_attr, _analytics, |_| {}); + make_setting_route!($route, $type, $attr, $camelcase_attr, _analytics, |_, _| {}); }; } @@ -92,7 +93,7 @@ make_setting_route!( filterable_attributes, "filterableAttributes", analytics, - |setting: &Option>| { + |setting: &Option>, req: &HttpRequest| { use serde_json::json; analytics.publish( @@ -101,6 +102,7 @@ make_setting_route!( "total": setting.as_ref().map(|filter| filter.len()), "has_geo": setting.as_ref().map(|filter| filter.contains("_geo")).unwrap_or(false), }), + Some(&req), ); } ); @@ -111,7 +113,7 @@ make_setting_route!( sortable_attributes, "sortableAttributes", analytics, - |setting: &Option>| { + |setting: &Option>, req: &HttpRequest| { use serde_json::json; analytics.publish( @@ -120,6 +122,7 @@ make_setting_route!( "total": setting.as_ref().map(|sort| sort.len()), "has_geo": setting.as_ref().map(|sort| sort.contains("_geo")).unwrap_or(false), }), + Some(&req), ); } ); @@ -165,7 +168,7 @@ make_setting_route!( ranking_rules, "rankingRules", analytics, - |setting: &Option>| { + |setting: &Option>, req: &HttpRequest| { use serde_json::json; analytics.publish( @@ -173,6 +176,7 @@ make_setting_route!( json!({ "sort_position": setting.as_ref().map(|sort| sort.iter().filter(|s| s.contains(":")).count()), }), + Some(&req), ); } ); @@ -205,6 +209,7 @@ pub async fn update_all( meilisearch: GuardedData, index_uid: web::Path, body: web::Json>, + req: HttpRequest, analytics: web::Data<&'static dyn Analytics>, ) -> Result { let settings = body.into_inner(); @@ -224,6 +229,7 @@ pub async fn update_all( "has_geo": settings.filterable_attributes.as_ref().set().map(|filter| filter.iter().any(|s| s == "_geo")).unwrap_or(false), }, }), + Some(&req), ); let update = Update::Settings(settings);