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:
Adrian Cole
2026-02-16 16:45:15 -05:00
committed by GitHub
parent dbf57c81fd
commit 4a5848ea76
7 changed files with 52 additions and 25 deletions
+1 -1
View File
@@ -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,
+28 -1
View File
@@ -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);
}
}
+5 -5
View File
@@ -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"
);