Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions crates/store/re_data_source/src/data_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,92 @@ impl LogDataSource {
Self::RedapProxy(uri) => Ok(re_grpc_client::stream(uri)),
}
}

/// Returns analytics data for this data source.
pub fn analytics(&self) -> LogDataSourceAnalytics {
match self {
Self::RrdHttpUrl { url, .. } => {
let file_extension = std::path::Path::new(url.path())
.extension()
.and_then(|e| e.to_str())
.map(|s| s.to_lowercase());
LogDataSourceAnalytics {
source_type: "rrd_http_url",
file_extension,
file_source: None,
}
}

#[cfg(not(target_arch = "wasm32"))]
Self::FilePath(file_src, path) => {
let file_extension = path
.extension()
.and_then(|e| e.to_str())
.map(|s| s.to_lowercase());
LogDataSourceAnalytics {
source_type: "file_path",
file_extension,
file_source: Some(Self::file_source_to_analytics_str(file_src)),
}
}

Self::FileContents(file_src, file_contents) => {
let file_extension = std::path::Path::new(&file_contents.name)
.extension()
.and_then(|e| e.to_str())
.map(|s| s.to_lowercase());
LogDataSourceAnalytics {
source_type: "file_contents",
file_extension,
file_source: Some(Self::file_source_to_analytics_str(file_src)),
}
}

#[cfg(not(target_arch = "wasm32"))]
Self::Stdin => LogDataSourceAnalytics {
source_type: "stdin",
file_extension: None,
file_source: None,
},

Self::RedapDatasetSegment { .. } => LogDataSourceAnalytics {
source_type: "redap_dataset_segment",
file_extension: None,
file_source: None,
},

Self::RedapProxy(_) => LogDataSourceAnalytics {
source_type: "redap_proxy",
file_extension: None,
file_source: None,
},
}
}

fn file_source_to_analytics_str(file_source: &re_log_types::FileSource) -> &'static str {
use re_log_types::FileSource;
match file_source {
FileSource::Cli => "cli",
FileSource::Uri => "uri",
FileSource::DragAndDrop { .. } => "drag_and_drop",
FileSource::FileDialog { .. } => "file_dialog",
FileSource::Sdk => "sdk",
}
}
}

/// Analytics data extracted from a [`LogDataSource`].
#[derive(Clone, Debug)]
pub struct LogDataSourceAnalytics {
/// The type of data source (e.g., "file", "http", ``redap_grpc``, "stdin").
pub source_type: &'static str,

/// The file extension if applicable (e.g., "rrd", "png", "glb").
pub file_extension: Option<String>,

/// How the file was opened (e.g., "cli", ``file_dialog``, ``drag_and_drop``).
/// Only applicable for file-based sources.
pub file_source: Option<&'static str>,
}

// TODO(ab, andreas): This should be replaced by the use of `AsyncRuntimeHandle`. However, this
Expand Down
2 changes: 1 addition & 1 deletion crates/store/re_data_source/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ mod data_source;
#[cfg(not(target_arch = "wasm32"))]
mod load_stdin;

pub use self::data_source::LogDataSource;
pub use self::data_source::{LogDataSource, LogDataSourceAnalytics};

// ----------------------------------------------------------------------------

Expand Down
41 changes: 41 additions & 0 deletions crates/utils/re_analytics/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,47 @@ impl Properties for SetPersonProperty {

// -----------------------------------------------

/// Tracks when a data source is loaded from the viewer.
///
/// This is sent when a user opens a file, URL, or other data source.
pub struct LoadDataSource {
/// The type of data source being loaded (e.g., "file", "http" etc.).
pub source_type: &'static str,

/// The file extension if applicable (e.g., "rrd", "png", "glb").
/// None for non-file sources like stdin or gRPC streams.
pub file_extension: Option<String>,

/// How the file was opened (e.g., "cli", "`file_dialog`" etc.).
/// Only applicable for file-based sources.
pub file_source: Option<&'static str>,

/// Whether the data source stream was started successfully.
pub started_successfully: bool,
}

impl Event for LoadDataSource {
const NAME: &'static str = "load_data_source";
}

impl Properties for LoadDataSource {
fn serialize(self, event: &mut AnalyticsEvent) {
let Self {
source_type,
file_extension,
file_source,
started_successfully,
} = self;

event.insert("source_type", source_type);
event.insert_opt("file_extension", file_extension);
event.insert_opt("file_source", file_source.map(|s| s.to_owned()));
event.insert("started_successfully", started_successfully);
}
}

// -----------------------------------------------

#[cfg(test)]
mod tests {
use super::*;
Expand Down
14 changes: 13 additions & 1 deletion crates/viewer/re_viewer/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1360,7 +1360,19 @@ impl App {
}
}

match data_source.clone().stream(&self.connection_registry) {
let stream = data_source.clone().stream(&self.connection_registry);
#[cfg(feature = "analytics")]
if let Some(analytics) = re_analytics::Analytics::global_or_init() {
let data_source_analytics = data_source.analytics();
analytics.record(re_analytics::event::LoadDataSource {
source_type: data_source_analytics.source_type,
file_extension: data_source_analytics.file_extension,
file_source: data_source_analytics.file_source,
started_successfully: stream.is_ok(),
});
}

match stream {
Ok(rx) => self.add_log_receiver(rx),
Err(err) => {
re_log::error!("Failed to open data source: {}", re_error::format(err));
Expand Down
Loading