mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
fix(telemetry): introduce staging and prod PostHog projects (#8647)
As per PostHog's recommendation [0], we now use different projects to manage the feature-flags. This allows us to turn feature flags in staging or production on / off without affecting the other. [0]: https://posthog.com/tutorials/multiple-environments
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
use std::{borrow::Cow, sync::LazyLock, time::Duration};
|
||||
use std::{sync::LazyLock, time::Duration};
|
||||
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use parking_lot::RwLock;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
const POSTHOG_API_KEY: &str = "phc_uXXl56plyvIBHj81WwXBLtdPElIRbm7keRTdUCmk8ll";
|
||||
const POSTHOG_API_KEY_PROD: &str = "phc_uXXl56plyvIBHj81WwXBLtdPElIRbm7keRTdUCmk8ll";
|
||||
const POSTHOG_API_KEY_STAGING: &str = "phc_tHOVtq183RpfKmzadJb4bxNpLM5jzeeb1Gu8YSH3nsK";
|
||||
const RE_EVAL_DURATION: Duration = Duration::from_secs(5 * 60);
|
||||
|
||||
static RUNTIME: LazyLock<Runtime> = LazyLock::new(init_runtime);
|
||||
@@ -23,9 +24,15 @@ pub fn drop_llmnr_nxdomain_responses() -> bool {
|
||||
FEATURE_FLAGS.read().drop_llmnr_nxdomain_responses
|
||||
}
|
||||
|
||||
pub(crate) fn reevaluate(user_id: String, env: Option<Cow<'static, str>>) {
|
||||
pub(crate) fn reevaluate(user_id: String, env: &str) {
|
||||
let api_key = match env {
|
||||
crate::env::PRODUCTION => POSTHOG_API_KEY_PROD,
|
||||
crate::env::STAGING => POSTHOG_API_KEY_STAGING,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
RUNTIME.spawn(async move {
|
||||
let flags = decide(user_id, env)
|
||||
let flags = decide(user_id, api_key.to_owned())
|
||||
.await
|
||||
.inspect_err(|e| tracing::debug!("Failed to evaluate feature flags: {e:#}"))
|
||||
.unwrap_or_default();
|
||||
@@ -58,31 +65,31 @@ fn init_runtime() -> Runtime {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(env) = client.options().environment.as_ref() else {
|
||||
continue; // Nothing to do if we don't have an environment set.
|
||||
};
|
||||
|
||||
let Some(user_id) = sentry::Hub::main()
|
||||
.configure_scope(|scope| scope.user().and_then(|u| u.id.clone()))
|
||||
else {
|
||||
continue; // Nothing to do if we don't have a user-id set.
|
||||
};
|
||||
|
||||
reevaluate(user_id, client.options().environment.to_owned());
|
||||
reevaluate(user_id, env);
|
||||
}
|
||||
});
|
||||
|
||||
runtime
|
||||
}
|
||||
|
||||
async fn decide(
|
||||
distinct_id: String,
|
||||
environment: Option<Cow<'static, str>>,
|
||||
) -> Result<FeatureFlags> {
|
||||
async fn decide(distinct_id: String, api_key: String) -> Result<FeatureFlags> {
|
||||
let response = reqwest::ClientBuilder::new()
|
||||
.connection_verbose(true)
|
||||
.build()?
|
||||
.post("https://us.i.posthog.com/decide?v=3")
|
||||
.json(&DecideRequest {
|
||||
api_key: POSTHOG_API_KEY.to_string(),
|
||||
api_key,
|
||||
distinct_id,
|
||||
groups: Groups { environment },
|
||||
})
|
||||
.send()
|
||||
.await
|
||||
@@ -108,13 +115,6 @@ async fn decide(
|
||||
struct DecideRequest {
|
||||
api_key: String,
|
||||
distinct_id: String,
|
||||
groups: Groups,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct Groups {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
environment: Option<Cow<'static, str>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![cfg_attr(test, allow(clippy::unwrap_used))]
|
||||
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use std::{borrow::Cow, sync::Arc, time::Duration};
|
||||
|
||||
use env::ON_PREM;
|
||||
use sentry::protocol::SessionStatus;
|
||||
@@ -37,11 +37,9 @@ pub const TESTING: Dsn = Dsn(
|
||||
);
|
||||
|
||||
mod env {
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub const PRODUCTION: Cow<'static, str> = Cow::Borrowed("production");
|
||||
pub const STAGING: Cow<'static, str> = Cow::Borrowed("staging");
|
||||
pub const ON_PREM: Cow<'static, str> = Cow::Borrowed("on-prem");
|
||||
pub const PRODUCTION: &str = "production";
|
||||
pub const STAGING: &str = "staging";
|
||||
pub const ON_PREM: &str = "on-prem";
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -74,7 +72,7 @@ impl Telemetry {
|
||||
.inner
|
||||
.as_ref()
|
||||
.and_then(|i| i.options().environment.as_ref())
|
||||
.is_some_and(|env| env == &environment)
|
||||
.is_some_and(|env| env == environment)
|
||||
{
|
||||
tracing::debug!(%environment, "Telemetry already initialised");
|
||||
|
||||
@@ -101,7 +99,7 @@ impl Telemetry {
|
||||
let inner = sentry::init((
|
||||
dsn.0,
|
||||
sentry::ClientOptions {
|
||||
environment: Some(environment),
|
||||
environment: Some(Cow::Borrowed(environment)),
|
||||
// We can't get the release number ourselves because we don't know if we're embedded in a GUI Client or a Headless Client.
|
||||
release: Some(release.to_owned().into()),
|
||||
traces_sampler: Some(Arc::new(|tx| {
|
||||
@@ -173,7 +171,11 @@ impl Telemetry {
|
||||
return;
|
||||
};
|
||||
|
||||
feature_flags::reevaluate(id, client.options().environment.to_owned());
|
||||
let Some(env) = client.options().environment.as_ref() else {
|
||||
return; // Nothing to do if we don't have an environment set.
|
||||
};
|
||||
|
||||
feature_flags::reevaluate(id, env);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user