feat: gate telemetry behind Cargo feature flags (#8119)

Signed-off-by: Rodolfo Olivieri <rolivier@redhat.com>
Signed-off-by: Douwe Osinga <douwe@squareup.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Rodolfo Olivieri
2026-03-26 13:33:43 -03:00
committed by GitHub
parent 8c8bb1051d
commit adc0e221fc
20 changed files with 111 additions and 38 deletions
+16 -7
View File
@@ -8,7 +8,16 @@ repository.workspace = true
description.workspace = true
[features]
default = ["code-mode", "local-inference", "aws-providers", "rustls-tls"]
default = ["code-mode", "local-inference", "aws-providers", "telemetry", "otel", "rustls-tls"]
telemetry = []
otel = [
"dep:tracing-opentelemetry",
"dep:opentelemetry",
"dep:opentelemetry_sdk",
"dep:opentelemetry-appender-tracing",
"dep:opentelemetry-otlp",
"dep:opentelemetry-stdout",
]
code-mode = ["dep:pctx_code_mode"]
local-inference = [
"dep:candle-core",
@@ -90,12 +99,12 @@ lazy_static = "1.5.0"
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
tracing-futures = { workspace = true }
tracing-opentelemetry = { workspace = true }
opentelemetry = { workspace = true }
opentelemetry_sdk = { workspace = true }
opentelemetry-appender-tracing = { workspace = true }
opentelemetry-otlp = { workspace = true }
opentelemetry-stdout = { workspace = true }
tracing-opentelemetry = { workspace = true, optional = true }
opentelemetry = { workspace = true, optional = true }
opentelemetry_sdk = { workspace = true, optional = true }
opentelemetry-appender-tracing = { workspace = true, optional = true }
opentelemetry-otlp = { workspace = true, optional = true }
opentelemetry-stdout = { workspace = true, optional = true }
keyring = { version = "3.6.2", features = [
"apple-native",
"windows-native",
+7
View File
@@ -583,6 +583,7 @@ impl Agent {
)
.await;
result.unwrap_or_else(|e| {
#[cfg(feature = "telemetry")]
crate::posthog::emit_error(
"tool_execution_failed",
&format!("{}: {}", tool_call.name, e),
@@ -943,6 +944,7 @@ impl Agent {
let command = message_text.split_whitespace().next();
if let Some(cmd) = command {
if crate::slash_commands::get_recipe_for_command(cmd).is_some() {
#[cfg(feature = "telemetry")]
crate::posthog::emit_custom_slash_command_used();
}
}
@@ -1487,6 +1489,7 @@ impl Agent {
}
}
Err(ref provider_err @ ProviderError::ContextLengthExceeded(_)) => {
#[cfg(feature = "telemetry")]
crate::posthog::emit_error(provider_err.telemetry_type(), &provider_err.to_string());
compaction_attempts += 1;
@@ -1531,6 +1534,7 @@ impl Agent {
break;
}
Err(e) => {
#[cfg(feature = "telemetry")]
crate::posthog::emit_error("compaction_failed", &e.to_string());
error!("Compaction failed: {}", e);
yield AgentEvent::Message(
@@ -1543,6 +1547,7 @@ impl Agent {
}
}
Err(ref provider_err @ ProviderError::CreditsExhausted { details: _, ref top_up_url }) => {
#[cfg(feature = "telemetry")]
crate::posthog::emit_error(provider_err.telemetry_type(), &provider_err.to_string());
error!("Error: {}", provider_err);
@@ -1566,6 +1571,7 @@ impl Agent {
break;
}
Err(ref provider_err @ ProviderError::NetworkError(_)) => {
#[cfg(feature = "telemetry")]
crate::posthog::emit_error(provider_err.telemetry_type(), &provider_err.to_string());
error!("Error: {}", provider_err);
yield AgentEvent::Message(
@@ -1576,6 +1582,7 @@ impl Agent {
break;
}
Err(ref provider_err) => {
#[cfg(feature = "telemetry")]
crate::posthog::emit_error(provider_err.telemetry_type(), &provider_err.to_string());
error!("Error: {}", provider_err);
yield AgentEvent::Message(
+1
View File
@@ -140,6 +140,7 @@ impl RetryManager {
"Maximum retry attempts ({}) exceeded",
retry_config.max_retries
);
#[cfg(feature = "telemetry")]
crate::posthog::emit_error(
"retry_max_exceeded",
&format!("Max retries ({}) exceeded", retry_config.max_retries),
+2
View File
@@ -21,8 +21,10 @@ pub mod logging;
pub mod mcp_utils;
pub mod model;
pub mod oauth;
#[cfg(feature = "otel")]
pub mod otel;
pub mod permission;
#[cfg(feature = "telemetry")]
pub mod posthog;
pub mod prompt_template;
pub mod providers;
+25 -19
View File
@@ -18,6 +18,7 @@ use crate::config::paths::Paths;
use crate::config::{resolve_extensions_for_new_session, Config};
use crate::conversation::message::Message;
use crate::conversation::Conversation;
#[cfg(feature = "telemetry")]
use crate::posthog;
use crate::providers::create;
use crate::recipe::Recipe;
@@ -267,6 +268,7 @@ impl Scheduler {
Ok(_) => tracing::info!("Job '{}' completed", task_job_id),
Err(ref e) => {
tracing::error!("Job '{}' failed: {}", task_job_id, e);
#[cfg(feature = "telemetry")]
crate::posthog::emit_error("scheduler_job_failed", &e.to_string());
}
}
@@ -838,6 +840,7 @@ async fn execute_job(
drop(jobs_guard);
let start_time = std::time::Instant::now();
#[cfg(feature = "telemetry")]
tokio::spawn(async move {
let mut props = HashMap::new();
props.insert(
@@ -907,25 +910,28 @@ async fn execute_job(
.apply()
.await?;
let duration_secs = start_time.elapsed().as_secs();
tokio::spawn(async move {
let mut props = HashMap::new();
props.insert(
"trigger".to_string(),
serde_json::Value::String("automated".to_string()),
);
props.insert(
"status".to_string(),
serde_json::Value::String("completed".to_string()),
);
props.insert(
"duration_seconds".to_string(),
serde_json::Value::Number(serde_json::Number::from(duration_secs)),
);
if let Err(e) = posthog::emit_event("schedule_job_completed", props).await {
tracing::debug!("Failed to send schedule telemetry: {}", e);
}
});
#[cfg(feature = "telemetry")]
{
let duration_secs = start_time.elapsed().as_secs();
tokio::spawn(async move {
let mut props = HashMap::new();
props.insert(
"trigger".to_string(),
serde_json::Value::String("automated".to_string()),
);
props.insert(
"status".to_string(),
serde_json::Value::String("completed".to_string()),
);
props.insert(
"duration_seconds".to_string(),
serde_json::Value::Number(serde_json::Number::from(duration_secs)),
);
if let Err(e) = posthog::emit_event("schedule_job_completed", props).await {
tracing::debug!("Failed to send schedule telemetry: {}", e);
}
});
}
Ok(session.id)
}
@@ -967,6 +967,7 @@ impl SessionStorage {
.await?;
tx.commit().await?;
#[cfg(feature = "telemetry")]
crate::posthog::emit_session_started();
Ok(session)
}
@@ -4,14 +4,9 @@ use goose::providers::api_client::{ApiClient, AuthMethod};
use goose::providers::base::Provider;
use goose::providers::openai::OpenAiProvider;
use goose::session_context::SESSION_ID_HEADER;
use opentelemetry::logs::AnyValue;
use opentelemetry::Key;
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use opentelemetry_sdk::logs::{InMemoryLogExporterBuilder, SdkLoggerProvider};
use serde_json::json;
use std::sync::Arc;
use std::sync::Mutex;
use tracing_subscriber::prelude::*;
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, Request, ResponseTemplate};
@@ -110,7 +105,14 @@ async fn make_request(provider: &dyn Provider, session_id: &str) {
}
#[tokio::test]
#[cfg(feature = "otel")]
async fn test_session_id_propagates_to_log_records() {
use opentelemetry::logs::AnyValue;
use opentelemetry::Key;
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
use opentelemetry_sdk::logs::{InMemoryLogExporterBuilder, SdkLoggerProvider};
use tracing_subscriber::prelude::*;
let exporter = InMemoryLogExporterBuilder::default().build();
let provider = SdkLoggerProvider::builder()
.with_simple_exporter(exporter.clone())