Skip to content

Commit

Permalink
Analytics: register SDK language and data source (#1371)
Browse files Browse the repository at this point in the history
* Fix analytics recording_source for Rust

We would always log `Python SDK` as the source

* New analytics event: data source

* Refactor: remove unused App::new

* new_recording -> open_recording

* Log `open_recording` event when opening files

* Store data_source as part of the LogDb and put it in the same avent

* Register what app environment we are running the viewer in

* Also register web viewer

* Add Python version

* More explicitly handle recording source in analytics

* compilation fix

* Fix web build

* Silence some clippy warnings on wasm builds

* rerun_cli

Co-authored-by: Clement Rey <cr.rey.clement@gmail.com>

---------

Co-authored-by: Clement Rey <cr.rey.clement@gmail.com>
  • Loading branch information
emilk and teh-cmc committed Mar 2, 2023
1 parent c81eb90 commit 1e24498
Show file tree
Hide file tree
Showing 16 changed files with 266 additions and 77 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/re_analytics/src/config_web.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(clippy::todo, clippy::unused_self)]

use std::collections::HashMap;

use uuid::Uuid;
Expand Down
6 changes: 6 additions & 0 deletions crates/re_analytics/src/pipeline_web.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
#![allow(
clippy::needless_pass_by_value,
clippy::unnecessary_wraps,
clippy::unused_self
)]

use std::time::Duration;

use crate::{Config, Event, PostHogSink};
Expand Down
1 change: 1 addition & 0 deletions crates/re_data_store/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ serde = ["dep:serde", "re_log_types/serde"]
re_arrow_store.workspace = true
re_log_types.workspace = true
re_log.workspace = true
re_smart_channel.workspace = true
re_string_interner.workspace = true

ahash = "0.8"
Expand Down
5 changes: 5 additions & 0 deletions crates/re_data_store/src/log_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ pub struct LogDb {
/// that are created after they were logged.
timeless_message_ids: Vec<MsgId>,

/// Set by whomever created this [`LogDb`].
pub data_source: Option<re_smart_channel::Source>,

/// Comes in a special message, [`LogMsg::BeginRecordingMsg`].
recording_info: Option<RecordingInfo>,

/// Where we store the entities.
Expand Down Expand Up @@ -266,6 +270,7 @@ impl LogDb {
chronological_message_ids,
log_messages,
timeless_message_ids,
data_source: _,
recording_info: _,
entity_db,
} = self;
Expand Down
2 changes: 1 addition & 1 deletion crates/re_log_types/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ fn test_encode_decode() {
recording_id: crate::RecordingId::random(),
is_official_example: true,
started: Time::now(),
recording_source: crate::RecordingSource::PythonSdk,
recording_source: crate::RecordingSource::RustSdk,
},
})];

Expand Down
39 changes: 37 additions & 2 deletions crates/re_log_types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,11 +216,44 @@ pub struct RecordingInfo {
pub recording_source: RecordingSource,
}

#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct PythonVersion {
/// e.g. 3
pub major: u8,

/// e.g. 11
pub minor: u8,

/// e.g. 0
pub patch: u8,

/// e.g. `a0` for alpha releases.
pub suffix: String,
}

impl std::fmt::Display for PythonVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self {
major,
minor,
patch,
suffix,
} = self;
write!(f, "{}.{}.{}{}", major, minor, patch, suffix)
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum RecordingSource {
Unknown,

/// The official Rerun Python Logging SDK
PythonSdk,
PythonSdk(PythonVersion),

/// The official Rerun Rust Logging SDK
RustSdk,

/// Perhaps from some manual data ingestion?
Other(String),
Expand All @@ -229,7 +262,9 @@ pub enum RecordingSource {
impl std::fmt::Display for RecordingSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::PythonSdk => "Python SDK".fmt(f),
Self::Unknown => "Unknown".fmt(f),
Self::PythonSdk(version) => write!(f, "Python {version} SDK"),
Self::RustSdk => "Rust SDK".fmt(f),
Self::Other(string) => format!("{string:?}").fmt(f), // put it in quotes
}
}
Expand Down
31 changes: 27 additions & 4 deletions crates/re_sdk/src/session.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::net::SocketAddr;

use re_log_types::{
ApplicationId, BeginRecordingMsg, LogMsg, MsgId, PathOp, RecordingId, RecordingInfo, Time,
TimePoint,
ApplicationId, BeginRecordingMsg, LogMsg, MsgId, PathOp, RecordingId, RecordingInfo,
RecordingSource, Time, TimePoint,
};

/// This is the main object you need to create to use the Rerun SDK.
Expand All @@ -14,6 +14,8 @@ pub struct Session {
/// If not, all calls into it are ignored!
enabled: bool,

recording_source: RecordingSource,

#[cfg(feature = "web")]
tokio_rt: tokio::runtime::Runtime,

Expand Down Expand Up @@ -101,6 +103,8 @@ impl Session {
Self {
enabled,

recording_source: RecordingSource::RustSdk,

#[cfg(feature = "web")]
tokio_rt: tokio::runtime::Runtime::new().unwrap(),

Expand Down Expand Up @@ -157,6 +161,12 @@ impl Session {
}
}

/// Set where the recording is coming from.
/// The default is [`RecordingSource::RustSdk`].
pub fn set_recording_source(&mut self, recording_source: RecordingSource) {
self.recording_source = recording_source;
}

/// Send log data to a remote server.
///
/// Send all currently buffered messages.
Expand Down Expand Up @@ -317,7 +327,7 @@ impl Session {
recording_id,
is_official_example: self.is_official_example.unwrap_or_default(),
started: Time::now(),
recording_source: re_log_types::RecordingSource::PythonSdk,
recording_source: self.recording_source.clone(),
},
}
.into(),
Expand Down Expand Up @@ -384,6 +394,17 @@ impl Session {

#[cfg(feature = "re_viewer")]
impl Session {
fn app_env(&self) -> re_viewer::AppEnvironment {
match &self.recording_source {
RecordingSource::PythonSdk(python_version) => {
re_viewer::AppEnvironment::PythonSdk(python_version.clone())
}
RecordingSource::Unknown | RecordingSource::RustSdk | RecordingSource::Other(_) => {
re_viewer::AppEnvironment::RustSdk
}
}
}

/// Drains all pending log messages and starts a Rerun viewer to visualize everything that has
/// been logged so far.
pub fn show(&mut self) -> re_viewer::external::eframe::Result<()> {
Expand All @@ -394,7 +415,7 @@ impl Session {

let log_messages = self.drain_log_messages_buffer();
let startup_options = re_viewer::StartupOptions::default();
re_viewer::run_native_viewer_with_messages(startup_options, log_messages)
re_viewer::run_native_viewer_with_messages(self.app_env(), startup_options, log_messages)
}

/// Starts a Rerun viewer on the current thread and migrates the given callback, along with
Expand Down Expand Up @@ -426,6 +447,7 @@ impl Session {
}

self.sender = Sender::NativeViewer(tx);
let app_env = self.app_env();

// NOTE: Forget the handle on purpose, leave that thread be.
_ = std::thread::spawn(move || run(self));
Expand All @@ -437,6 +459,7 @@ impl Session {
let rx = re_viewer::wake_up_ui_thread_on_each_msg(rx, cc.egui_ctx.clone());
let startup_options = re_viewer::StartupOptions::default();
Box::new(re_viewer::App::from_receiver(
app_env,
startup_options,
re_ui,
cc.storage,
Expand Down
55 changes: 28 additions & 27 deletions crates/re_viewer/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,11 @@ pub struct App {
impl App {
/// Create a viewer that receives new log messages over time
pub fn from_receiver(
app_env: crate::AppEnvironment,
startup_options: StartupOptions,
re_ui: re_ui::ReUi,
storage: Option<&dyn eframe::Storage>,
rx: Receiver<LogMsg>,
) -> Self {
Self::new(startup_options, re_ui, storage, rx, Default::default())
}

fn new(
startup_options: StartupOptions,
re_ui: re_ui::ReUi,
storage: Option<&dyn eframe::Storage>,
rx: Receiver<LogMsg>,
log_db: LogDb,
) -> Self {
#[cfg(not(target_arch = "wasm32"))]
let ctrl_c = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
Expand All @@ -128,25 +119,19 @@ impl App {
.expect("Error setting Ctrl-C handler");
}

let mut state: AppState = storage
let state: AppState = storage
.and_then(|storage| eframe::get_value(storage, eframe::APP_KEY))
.unwrap_or_default();

let mut log_dbs = IntMap::default();
if !log_db.is_empty() {
state.selected_rec_id = log_db.recording_id();
log_dbs.insert(log_db.recording_id(), log_db);
}

let analytics = ViewerAnalytics::new();
analytics.on_viewer_started();
let mut analytics = ViewerAnalytics::new();
analytics.on_viewer_started(app_env);

Self {
startup_options,
re_ui,
component_ui_registry: Default::default(),
rx,
log_dbs,
log_dbs: Default::default(),
state,
#[cfg(not(target_arch = "wasm32"))]
ctrl_c,
Expand Down Expand Up @@ -609,18 +594,31 @@ impl App {
let start = instant::Instant::now();

while let Ok(msg) = self.rx.try_recv() {
if let LogMsg::BeginRecordingMsg(msg) = &msg {
re_log::debug!("Beginning a new recording: {:?}", msg.info);
let is_new_recording = if let LogMsg::BeginRecordingMsg(msg) = &msg {
re_log::debug!("Opening a new recording: {:?}", msg.info);
self.state.selected_rec_id = msg.info.recording_id;

self.analytics.on_new_recording(msg);
}
true
} else {
false
};

let log_db = self.log_dbs.entry(self.state.selected_rec_id).or_default();

if log_db.data_source.is_none() {
log_db.data_source = Some(self.rx.source().clone());
}

if let Err(err) = log_db.add(msg) {
re_log::error!("Failed to add incoming msg: {err}");
};

if is_new_recording {
// Do analytics after ingesting the new message,
// because thats when the `log_db.recording_info` is set,
// which we use in the analytics call.
self.analytics.on_open_recording(log_db);
}

if start.elapsed() > instant::Duration::from_millis(10) {
egui_ctx.request_repaint(); // make sure we keep receiving messages asap
break; // don't block the main thread for too long
Expand Down Expand Up @@ -746,6 +744,7 @@ impl App {
}

fn show_log_db(&mut self, log_db: LogDb) {
self.analytics.on_open_recording(&log_db);
self.state.selected_rec_id = log_db.recording_id();
self.log_dbs.insert(log_db.recording_id(), log_db);
}
Expand Down Expand Up @@ -1622,8 +1621,9 @@ fn load_file_path(path: &std::path::Path) -> Option<LogDb> {
re_log::info!("Loading {path:?}…");

match load_file_path_impl(path) {
Ok(new_log_db) => {
Ok(mut new_log_db) => {
re_log::info!("Loaded {path:?}");
new_log_db.data_source = Some(re_smart_channel::Source::File { path: path.into() });
Some(new_log_db)
}
Err(err) => {
Expand All @@ -1641,8 +1641,9 @@ fn load_file_path(path: &std::path::Path) -> Option<LogDb> {
#[must_use]
fn load_file_contents(name: &str, read: impl std::io::Read) -> Option<LogDb> {
match load_rrd_to_log_db(read) {
Ok(log_db) => {
Ok(mut log_db) => {
re_log::info!("Loaded {name:?}");
log_db.data_source = Some(re_smart_channel::Source::File { path: name.into() });
Some(log_db)
}
Err(err) => {
Expand Down
19 changes: 19 additions & 0 deletions crates/re_viewer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod viewer_analytics;

pub use self::misc::color_map;
pub(crate) use misc::{mesh_loader, Item, TimeControl, TimeView, ViewerContext};
use re_log_types::PythonVersion;
pub(crate) use ui::{event_log_view, memory_panel, selection_panel, time_panel, UiVerbosity};

pub use app::{App, StartupOptions};
Expand Down Expand Up @@ -68,6 +69,24 @@ macro_rules! profile_scope {

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

/// Where is this App running in?
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AppEnvironment {
/// Created from the Rerun Python SDK.
PythonSdk(PythonVersion),

/// Created from the Rerun Rust SDK.
RustSdk,

/// Running the Rust `rerun` binary from the CLI.
RerunCli,

/// We are a web-viewer running in a browser as Wasm.
Web,
}

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

#[allow(dead_code)]
const APPLICATION_NAME: &str = "Rerun Viewer";

Expand Down
2 changes: 2 additions & 0 deletions crates/re_viewer/src/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub fn run_native_app(app_creator: AppCreator) -> eframe::Result<()> {
}

pub fn run_native_viewer_with_messages(
app_env: crate::AppEnvironment,
startup_options: crate::StartupOptions,
log_messages: Vec<LogMsg>,
) -> eframe::Result<()> {
Expand All @@ -49,6 +50,7 @@ pub fn run_native_viewer_with_messages(
}
run_native_app(Box::new(move |cc, re_ui| {
Box::new(crate::App::from_receiver(
app_env,
startup_options,
re_ui,
cc.storage,
Expand Down

0 comments on commit 1e24498

Please sign in to comment.