Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(telemetry): add run and repo events #6876

Merged
Merged
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
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.

3 changes: 1 addition & 2 deletions crates/turborepo-ci/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ impl Vendor {
None
}

#[allow(dead_code)]
fn get_name() -> Option<&'static str> {
pub fn get_name() -> Option<&'static str> {
Self::infer().map(|v| v.name)
}

Expand Down
9 changes: 9 additions & 0 deletions crates/turborepo-env/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ pub enum ResolvedEnvMode {
Strict,
}

impl std::fmt::Display for ResolvedEnvMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ResolvedEnvMode::Loose => write!(f, "loose"),
ResolvedEnvMode::Strict => write!(f, "strict"),
}
}
}

#[derive(Clone, Debug, Error)]
pub enum Error {
#[error("Failed to parse regex: {0}")]
Expand Down
2 changes: 1 addition & 1 deletion crates/turborepo-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ pub extern "C" fn get_package_file_hashes(buffer: Buffer) -> Buffer {
};
let inputs = req.inputs.as_slice();
let hasher = turborepo_scm::SCM::new(&turbo_root);
let response = match hasher.get_package_file_hashes(&turbo_root, &package_path, inputs) {
let response = match hasher.get_package_file_hashes(&turbo_root, &package_path, inputs, None) {
Ok(hashes) => {
let mut to_return = HashMap::new();
for (filename, hash) in hashes {
Expand Down
30 changes: 28 additions & 2 deletions crates/turborepo-lib/src/run/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use itertools::Itertools;
use rayon::iter::ParallelBridge;
use tracing::debug;
use turborepo_analytics::{start_analytics, AnalyticsHandle, AnalyticsSender};
use turborepo_api_client::{APIAuth, APIClient};
use turborepo_api_client::{APIAuth, APIClient, Client};
use turborepo_cache::{AsyncCache, RemoteCacheOpts};
use turborepo_ci::Vendor;
use turborepo_env::EnvironmentVariableMap;
Expand All @@ -32,6 +32,10 @@ use turborepo_repository::{
package_json::PackageJson,
};
use turborepo_scm::SCM;
use turborepo_telemetry::events::{
generic::{DaemonInitStatus, GenericEventBuilder},
repo::{RepoEventBuilder, RepoType},
};
use turborepo_ui::{cprint, cprintln, ColorSelector, BOLD_GREY, GREY};

use self::task_id::TaskName;
Expand Down Expand Up @@ -170,6 +174,8 @@ impl<'a> Run<'a> {
let package_json_path = self.base.repo_root.join_component("package.json");
let root_package_json = PackageJson::load(&package_json_path)?;
let mut opts = self.opts()?;
let run_telemetry = GenericEventBuilder::new();
let repo_telemetry = RepoEventBuilder::new(&self.base.repo_root.to_string());

let config = self.base.config()?;

Expand All @@ -184,16 +190,28 @@ impl<'a> Run<'a> {
// value
opts.cache_opts.skip_remote = !enabled;
}

run_telemetry.track_is_linked(is_linked);
// we only track the remote cache if we're linked because this defaults to
// Vercel
if is_linked {
run_telemetry.track_remote_cache(api_client.base_url());
}
let _is_structured_output = opts.run_opts.graph.is_some()
|| matches!(opts.run_opts.dry_run, Some(DryRunMode::Json));

let is_single_package = opts.run_opts.single_package;
repo_telemetry.track_type(if is_single_package {
RepoType::SinglePackage
} else {
RepoType::Monorepo
});

let is_ci_or_not_tty = turborepo_ci::is_ci() || !std::io::stdout().is_terminal();
run_telemetry.track_ci(turborepo_ci::Vendor::get_name());

let daemon = match (is_ci_or_not_tty, opts.run_opts.daemon) {
(true, None) => {
run_telemetry.track_daemon_init(DaemonInitStatus::Skipped);
debug!("skipping turbod since we appear to be in a non-interactive context");
None
}
Expand All @@ -207,16 +225,19 @@ impl<'a> Run<'a> {

match connector.connect().await {
Ok(client) => {
run_telemetry.track_daemon_init(DaemonInitStatus::Started);
debug!("running in daemon mode");
Some(client)
}
Err(e) => {
run_telemetry.track_daemon_init(DaemonInitStatus::Failed);
debug!("failed to connect to daemon {e}");
None
}
}
}
(_, Some(false)) => {
run_telemetry.track_daemon_init(DaemonInitStatus::Disabled);
debug!("skipping turbod since --no-daemon was passed");
None
}
Expand All @@ -236,6 +257,10 @@ impl<'a> Run<'a> {
.build()
.await?;

repo_telemetry.track_package_manager(pkg_dep_graph.package_manager().to_string());
repo_telemetry.track_size(pkg_dep_graph.len());
run_telemetry.track_run_type(opts.run_opts.dry_run.is_some());

let root_turbo_json =
TurboJson::load(&self.base.repo_root, &root_package_json, is_single_package)?;

Expand Down Expand Up @@ -366,6 +391,7 @@ impl<'a> Run<'a> {
workspaces,
engine.task_definitions(),
&self.base.repo_root,
&run_telemetry,
)?;

if opts.run_opts.parallel {
Expand Down
1 change: 1 addition & 0 deletions crates/turborepo-lib/src/task_graph/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ impl<'a> Visitor<'a> {
EnvMode::Strict => ResolvedEnvMode::Strict,
EnvMode::Loose => ResolvedEnvMode::Loose,
};
package_task_event.track_env_mode(&task_env_mode.to_string());

let dependency_set = engine.dependencies(&info).ok_or(Error::MissingDefinition)?;

Expand Down
11 changes: 10 additions & 1 deletion crates/turborepo-lib/src/task_hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ use turborepo_cache::CacheHitMetadata;
use turborepo_env::{BySource, DetailedMap, EnvironmentVariableMap, ResolvedEnvMode};
use turborepo_repository::package_graph::{WorkspaceInfo, WorkspaceName};
use turborepo_scm::SCM;
use turborepo_telemetry::events::task::PackageTaskEventBuilder;
use turborepo_telemetry::events::{
generic::GenericEventBuilder, task::PackageTaskEventBuilder, EventBuilder,
};

use crate::{
engine::TaskNode,
Expand Down Expand Up @@ -71,6 +73,7 @@ impl PackageInputsHashes {
workspaces: HashMap<&WorkspaceName, &WorkspaceInfo>,
task_definitions: &HashMap<TaskId<'static>, TaskDefinition>,
repo_root: &AbsoluteSystemPath,
telemetry: &GenericEventBuilder,
) -> Result<PackageInputsHashes, Error> {
tracing::trace!(scm_manual=%scm.is_manual(), "scm running in {} mode", if scm.is_manual() { "manual" } else { "git" });

Expand All @@ -91,7 +94,11 @@ impl PackageInputsHashes {
Ok(def) => def,
Err(err) => return Some(Err(err)),
};
let package_task_event =
PackageTaskEventBuilder::new(task_id.package(), task_id.task())
.with_parent(telemetry);

package_task_event.track_scm_mode(if scm.is_manual() { "manual" } else { "git" });
let workspace_name = task_id.to_workspace_name();

let pkg = match workspaces
Expand All @@ -107,10 +114,12 @@ impl PackageInputsHashes {
.parent()
.unwrap_or_else(|| AnchoredSystemPath::new("").unwrap());

let scm_telemetry = package_task_event.child();
let mut hash_object = match scm.get_package_file_hashes(
repo_root,
package_path,
&task_definition.inputs,
Some(scm_telemetry),
) {
Ok(hash_object) => hash_object,
Err(err) => return Some(Err(err.into())),
Expand Down
1 change: 1 addition & 0 deletions crates/turborepo-scm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ sha1 = "0.10.5"
thiserror = { workspace = true }
tracing = { workspace = true }
turbopath = { workspace = true }
turborepo-telemetry = { path = "../turborepo-telemetry" }
wax = { workspace = true }
which = { workspace = true }

Expand Down
62 changes: 43 additions & 19 deletions crates/turborepo-scm/src/package_deps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::collections::HashMap;
use itertools::{Either, Itertools};
use tracing::debug;
use turbopath::{AbsoluteSystemPath, AnchoredSystemPath, PathError, RelativeUnixPathBuf};
use turborepo_telemetry::events::task::{FileHashMethod, PackageTaskEventBuilder};

use crate::{hash_object::hash_objects, Error, Git, SCM};

Expand All @@ -28,26 +29,44 @@ impl SCM {
turbo_root: &AbsoluteSystemPath,
package_path: &AnchoredSystemPath,
inputs: &[S],
telemetry: Option<PackageTaskEventBuilder>,
) -> Result<GitHashes, Error> {
match self {
SCM::Manual => crate::manual::get_package_file_hashes_from_processing_gitignore(
turbo_root,
package_path,
inputs,
),
SCM::Git(git) => git
.get_package_file_hashes(turbo_root, package_path, inputs)
.or_else(|e| {
debug!(
"failed to use git to hash files: {}. Falling back to manual",
e
);
crate::manual::get_package_file_hashes_from_processing_gitignore(
turbo_root,
package_path,
inputs,
)
}),
SCM::Manual => {
if let Some(telemetry) = telemetry {
telemetry.track_file_hash_method(FileHashMethod::Manual);
}
crate::manual::get_package_file_hashes_from_processing_gitignore(
turbo_root,
package_path,
inputs,
)
}
SCM::Git(git) => {
let result = git.get_package_file_hashes(turbo_root, package_path, inputs);
match result {
Ok(hashes) => {
if let Some(telemetry) = telemetry {
telemetry.track_file_hash_method(FileHashMethod::Git);
}
Ok(hashes)
}
Err(err) => {
debug!(
"failed to use git to hash files: {}. Falling back to manual",
err
);
if let Some(telemetry) = telemetry {
telemetry.track_file_hash_method(FileHashMethod::Manual);
}
crate::manual::get_package_file_hashes_from_processing_gitignore(
turbo_root,
package_path,
inputs,
)
}
}
}
}
}

Expand Down Expand Up @@ -272,7 +291,12 @@ mod tests {
repo_root.join_component(".git").remove_dir_all().unwrap();
let pkg_path = repo_root.anchor(&my_pkg_dir).unwrap();
let hashes = git
.get_package_file_hashes::<&str>(&repo_root, &pkg_path, &[])
.get_package_file_hashes::<&str>(
&repo_root,
&pkg_path,
&[],
Some(PackageTaskEventBuilder::new("my-pkg", "test")),
)
.unwrap();
let mut expected = GitHashes::new();
expected.insert(
Expand Down
65 changes: 65 additions & 0 deletions crates/turborepo-telemetry/src/events/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ use uuid::Uuid;
use super::{Event, EventBuilder, EventType, Identifiable};
use crate::{config::TelemetryConfig, telem};

pub enum DaemonInitStatus {
// skipped due to context (running in CI etc)
Skipped,
/// daemon was started
Started,
/// daemon failed to start
Failed,
/// daemon was manually disabled by user
Disabled,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenericEventBuilder {
id: String,
Expand Down Expand Up @@ -119,4 +130,58 @@ impl GenericEventBuilder {
});
self
}

// run data

pub fn track_is_linked(&self, is_linked: bool) -> &Self {
self.track(Event {
key: "is_linked".to_string(),
value: if is_linked { "true" } else { "false" }.to_string(),
is_sensitive: EventType::NonSensitive,
});
self
}

pub fn track_remote_cache(&self, cache_url: &str) -> &Self {
self.track(Event {
key: "remote_cache_url".to_string(),
value: cache_url.to_string(),
is_sensitive: EventType::NonSensitive,
});
self
}

pub fn track_ci(&self, ci: Option<&'static str>) -> &Self {
if let Some(ci) = ci {
self.track(Event {
key: "ci".to_string(),
value: ci.to_string(),
is_sensitive: EventType::NonSensitive,
});
}
self
}

pub fn track_run_type(&self, is_dry: bool) -> &Self {
self.track(Event {
key: "run_type".to_string(),
value: if is_dry { "dry" } else { "full" }.to_string(),
is_sensitive: EventType::NonSensitive,
});
self
}

pub fn track_daemon_init(&self, status: DaemonInitStatus) -> &Self {
self.track(Event {
key: "daemon_status".to_string(),
value: match status {
DaemonInitStatus::Skipped => "skipped".to_string(),
DaemonInitStatus::Started => "started".to_string(),
DaemonInitStatus::Failed => "failed".to_string(),
DaemonInitStatus::Disabled => "disabled".to_string(),
},
is_sensitive: EventType::NonSensitive,
});
self
}
}
Loading
Loading