mirror of
https://github.com/aaif-goose/goose.git
synced 2026-07-03 14:10:03 +02:00
fix(otel): use monotonic_counter prefix and support temporality env var (#7234)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -1059,7 +1059,7 @@ async fn handle_interactive_session(
|
||||
};
|
||||
|
||||
tracing::info!(
|
||||
counter.goose.session_starts = 1,
|
||||
monotonic_counter.goose.session_starts = 1,
|
||||
session_type,
|
||||
interactive = true,
|
||||
"Session started"
|
||||
@@ -1136,7 +1136,7 @@ async fn log_session_completion(
|
||||
.unwrap_or((0, 0));
|
||||
|
||||
tracing::info!(
|
||||
counter.goose.session_completions = 1,
|
||||
monotonic_counter.goose.session_completions = 1,
|
||||
session_type,
|
||||
exit_type,
|
||||
duration_ms = session_duration.as_millis() as u64,
|
||||
@@ -1146,14 +1146,14 @@ async fn log_session_completion(
|
||||
);
|
||||
|
||||
tracing::info!(
|
||||
counter.goose.session_duration_ms = session_duration.as_millis() as u64,
|
||||
monotonic_counter.goose.session_duration_ms = session_duration.as_millis() as u64,
|
||||
session_type,
|
||||
"Session duration"
|
||||
);
|
||||
|
||||
if total_tokens > 0 {
|
||||
tracing::info!(
|
||||
counter.goose.session_tokens = total_tokens,
|
||||
monotonic_counter.goose.session_tokens = total_tokens,
|
||||
session_type,
|
||||
"Session tokens"
|
||||
);
|
||||
@@ -1236,7 +1236,7 @@ fn parse_run_input(
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
counter.goose.recipe_runs = 1,
|
||||
monotonic_counter.goose.recipe_runs = 1,
|
||||
recipe_name = %recipe_display_name,
|
||||
recipe_version = %recipe_version,
|
||||
session_type = "recipe",
|
||||
@@ -1323,7 +1323,7 @@ async fn handle_run_command(
|
||||
let session_type = if recipe.is_some() { "recipe" } else { "run" };
|
||||
|
||||
tracing::info!(
|
||||
counter.goose.session_starts = 1,
|
||||
monotonic_counter.goose.session_starts = 1,
|
||||
session_type,
|
||||
interactive = false,
|
||||
"Headless session started"
|
||||
@@ -1437,7 +1437,7 @@ pub async fn cli() -> anyhow::Result<()> {
|
||||
|
||||
let command_name = get_command_name(&cli.command);
|
||||
tracing::info!(
|
||||
counter.goose.cli_commands = 1,
|
||||
monotonic_counter.goose.cli_commands = 1,
|
||||
command = command_name,
|
||||
"CLI command executed"
|
||||
);
|
||||
|
||||
@@ -1746,7 +1746,7 @@ fn log_tool_metrics(message: &Message, messages: &Conversation) {
|
||||
if let MessageContent::ToolRequest(tool_request) = content {
|
||||
if let Ok(tool_call) = &tool_request.tool_call {
|
||||
tracing::info!(
|
||||
counter.goose.tool_calls = 1,
|
||||
monotonic_counter.goose.tool_calls = 1,
|
||||
tool_name = %tool_call.name,
|
||||
"Tool call started"
|
||||
);
|
||||
@@ -1777,7 +1777,7 @@ fn log_tool_metrics(message: &Message, messages: &Conversation) {
|
||||
"error"
|
||||
};
|
||||
tracing::info!(
|
||||
counter.goose.tool_completions = 1,
|
||||
monotonic_counter.goose.tool_completions = 1,
|
||||
tool_name = %tool_name,
|
||||
result = %result_status,
|
||||
"Tool call completed"
|
||||
|
||||
@@ -66,7 +66,7 @@ fn track_tool_telemetry(content: &MessageContent, all_messages: &[Message]) {
|
||||
let result_status = if success { "success" } else { "error" };
|
||||
|
||||
tracing::info!(
|
||||
counter.goose.tool_completions = 1,
|
||||
monotonic_counter.goose.tool_completions = 1,
|
||||
tool_name = %tool_name,
|
||||
result = %result_status,
|
||||
"Tool call completed"
|
||||
@@ -209,7 +209,7 @@ pub async fn reply(
|
||||
let session_start = std::time::Instant::now();
|
||||
|
||||
tracing::info!(
|
||||
counter.goose.session_starts = 1,
|
||||
monotonic_counter.goose.session_starts = 1,
|
||||
session_type = "app",
|
||||
interface = "ui",
|
||||
"Session started"
|
||||
@@ -225,7 +225,7 @@ pub async fn reply(
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
|
||||
tracing::info!(
|
||||
counter.goose.recipe_runs = 1,
|
||||
monotonic_counter.goose.recipe_runs = 1,
|
||||
recipe_name = %recipe_name,
|
||||
recipe_version = %recipe_version,
|
||||
session_type = "app",
|
||||
@@ -396,7 +396,7 @@ pub async fn reply(
|
||||
if let Ok(session) = state.session_manager().get_session(&session_id, true).await {
|
||||
let total_tokens = session.total_tokens.unwrap_or(0);
|
||||
tracing::info!(
|
||||
counter.goose.session_completions = 1,
|
||||
monotonic_counter.goose.session_completions = 1,
|
||||
session_type = "app",
|
||||
interface = "ui",
|
||||
exit_type = "normal",
|
||||
@@ -407,7 +407,7 @@ pub async fn reply(
|
||||
);
|
||||
|
||||
tracing::info!(
|
||||
counter.goose.session_duration_ms = session_duration.as_millis() as u64,
|
||||
monotonic_counter.goose.session_duration_ms = session_duration.as_millis() as u64,
|
||||
session_type = "app",
|
||||
interface = "ui",
|
||||
"Session duration"
|
||||
@@ -415,7 +415,7 @@ pub async fn reply(
|
||||
|
||||
if total_tokens > 0 {
|
||||
tracing::info!(
|
||||
counter.goose.session_tokens = total_tokens,
|
||||
monotonic_counter.goose.session_tokens = total_tokens,
|
||||
session_type = "app",
|
||||
interface = "ui",
|
||||
"Session tokens"
|
||||
@@ -423,7 +423,7 @@ pub async fn reply(
|
||||
}
|
||||
} else {
|
||||
tracing::info!(
|
||||
counter.goose.session_completions = 1,
|
||||
monotonic_counter.goose.session_completions = 1,
|
||||
session_type = "app",
|
||||
interface = "ui",
|
||||
exit_type = "normal",
|
||||
@@ -434,7 +434,7 @@ pub async fn reply(
|
||||
);
|
||||
|
||||
tracing::info!(
|
||||
counter.goose.session_duration_ms = session_duration.as_millis() as u64,
|
||||
monotonic_counter.goose.session_duration_ms = session_duration.as_millis() as u64,
|
||||
session_type = "app",
|
||||
interface = "ui",
|
||||
"Session duration"
|
||||
|
||||
@@ -274,7 +274,7 @@ async fn run_now_handler(
|
||||
|
||||
let recipe_version_tag = recipe_version_opt.as_deref().unwrap_or("");
|
||||
tracing::info!(
|
||||
counter.goose.recipe_runs = 1,
|
||||
monotonic_counter.goose.recipe_runs = 1,
|
||||
recipe_name = %recipe_display_name,
|
||||
recipe_version = %recipe_version_tag,
|
||||
session_type = "schedule",
|
||||
|
||||
@@ -88,7 +88,7 @@ impl Agent {
|
||||
// Log user decision if this was a security alert
|
||||
if let Some(finding_id) = get_security_finding_id_from_results(&request.id, inspection_results) {
|
||||
tracing::info!(
|
||||
counter.goose.prompt_injection_user_decisions = 1,
|
||||
monotonic_counter.goose.prompt_injection_user_decisions = 1,
|
||||
decision = ?confirmation.permission,
|
||||
finding_id = %finding_id,
|
||||
tool_request_id = %request.id,
|
||||
|
||||
@@ -2,7 +2,7 @@ use opentelemetry::trace::TracerProvider;
|
||||
use opentelemetry::{global, KeyValue};
|
||||
use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
|
||||
use opentelemetry_sdk::logs::{SdkLogger, SdkLoggerProvider};
|
||||
use opentelemetry_sdk::metrics::SdkMeterProvider;
|
||||
use opentelemetry_sdk::metrics::{SdkMeterProvider, Temporality};
|
||||
use opentelemetry_sdk::propagation::TraceContextPropagator;
|
||||
use opentelemetry_sdk::resource::{EnvResourceDetector, TelemetryResourceDetector};
|
||||
use opentelemetry_sdk::trace::SdkTracerProvider;
|
||||
@@ -173,6 +173,19 @@ fn create_otlp_tracing_layer() -> OtlpResult<OtlpTracingLayer> {
|
||||
Ok(tracing_opentelemetry::layer().with_tracer(tracer))
|
||||
}
|
||||
|
||||
// TODO: remove once https://github.com/open-telemetry/opentelemetry-rust/pull/3351 is released.
|
||||
fn temporality_preference() -> Temporality {
|
||||
match env::var("OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE")
|
||||
.unwrap_or_default()
|
||||
.to_lowercase()
|
||||
.as_str()
|
||||
{
|
||||
"delta" => Temporality::Delta,
|
||||
"lowmemory" => Temporality::LowMemory,
|
||||
_ => Temporality::Cumulative,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_otlp_metrics_layer() -> OtlpResult<OtlpMetricsLayer> {
|
||||
let exporter = signal_exporter("metrics").ok_or("Metrics not enabled")?;
|
||||
let resource = create_resource();
|
||||
@@ -181,6 +194,7 @@ fn create_otlp_metrics_layer() -> OtlpResult<OtlpMetricsLayer> {
|
||||
ExporterType::Otlp => {
|
||||
let exporter = opentelemetry_otlp::MetricExporter::builder()
|
||||
.with_http()
|
||||
.with_temporality(temporality_preference())
|
||||
.build()?;
|
||||
SdkMeterProvider::builder()
|
||||
.with_resource(resource)
|
||||
@@ -332,6 +346,7 @@ mod tests {
|
||||
use super::*;
|
||||
use opentelemetry::metrics::{Meter, MeterProvider};
|
||||
use opentelemetry::InstrumentationScope;
|
||||
use opentelemetry_sdk::metrics::Temporality;
|
||||
use std::sync::Arc;
|
||||
use test_case::test_case;
|
||||
|
||||
@@ -371,6 +386,7 @@ mod tests {
|
||||
("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", None),
|
||||
("OTEL_EXPORTER_OTLP_TIMEOUT", None),
|
||||
("OTEL_SERVICE_NAME", None),
|
||||
("OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE", None),
|
||||
("OTEL_RESOURCE_ATTRIBUTES", None),
|
||||
]);
|
||||
for &(k, v) in overrides {
|
||||
@@ -548,4 +564,15 @@ mod tests {
|
||||
expect_timeout
|
||||
);
|
||||
}
|
||||
|
||||
#[test_case(&[], Temporality::Cumulative; "default is cumulative")]
|
||||
#[test_case(&[("OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE", "delta")], Temporality::Delta; "delta")]
|
||||
#[test_case(&[("OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE", "Delta")], Temporality::Delta; "Delta mixed case")]
|
||||
#[test_case(&[("OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE", "lowmemory")], Temporality::LowMemory; "lowmemory")]
|
||||
#[test_case(&[("OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE", "cumulative")], Temporality::Cumulative; "cumulative")]
|
||||
#[test_case(&[("OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE", "bogus")], Temporality::Cumulative; "unknown defaults to cumulative")]
|
||||
fn temporality_preference_from_env(env: &[(&str, &str)], expected: Temporality) {
|
||||
let _guard = clear_otel_env(env);
|
||||
assert_eq!(temporality_preference(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ impl SecurityManager {
|
||||
) -> Result<Vec<SecurityResult>> {
|
||||
if !self.is_prompt_injection_detection_enabled() {
|
||||
tracing::debug!(
|
||||
counter.goose.prompt_injection_scanner_disabled = 1,
|
||||
monotonic_counter.goose.prompt_injection_scanner_disabled = 1,
|
||||
"Security scanning disabled"
|
||||
);
|
||||
return Ok(vec![]);
|
||||
@@ -74,7 +74,7 @@ impl SecurityManager {
|
||||
match PromptInjectionScanner::with_ml_detection() {
|
||||
Ok(s) => {
|
||||
tracing::info!(
|
||||
counter.goose.prompt_injection_scanner_enabled = 1,
|
||||
monotonic_counter.goose.prompt_injection_scanner_enabled = 1,
|
||||
"Security scanner initialized with ML-based detection"
|
||||
);
|
||||
s
|
||||
@@ -90,7 +90,7 @@ impl SecurityManager {
|
||||
}
|
||||
} else {
|
||||
tracing::info!(
|
||||
counter.goose.prompt_injection_scanner_enabled = 1,
|
||||
monotonic_counter.goose.prompt_injection_scanner_enabled = 1,
|
||||
"Security scanner initialized with pattern-based detection only"
|
||||
);
|
||||
PromptInjectionScanner::new()
|
||||
@@ -124,7 +124,7 @@ impl SecurityManager {
|
||||
serde_json::to_string(&tool_call).unwrap_or_else(|_| "{}".to_string());
|
||||
|
||||
tracing::warn!(
|
||||
counter.goose.prompt_injection_finding = 1,
|
||||
monotonic_counter.goose.prompt_injection_finding = 1,
|
||||
threat_type = "command_injection",
|
||||
above_threshold = above_threshold,
|
||||
tool_name = %tool_call.name,
|
||||
@@ -164,7 +164,7 @@ impl SecurityManager {
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
counter.goose.prompt_injection_analysis_performed = 1,
|
||||
monotonic_counter.goose.prompt_injection_analysis_performed = 1,
|
||||
security_issues_found = results.len(),
|
||||
"Prompt injection detection: Security analysis complete"
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user