Skip to content

fix(go-adk): explicitly set timeout for traces and logs export#1638

Closed
supreme-gg-gg wants to merge 2 commits into
kagent-dev:mainfrom
supreme-gg-gg:fix/go-tracing-2
Closed

fix(go-adk): explicitly set timeout for traces and logs export#1638
supreme-gg-gg wants to merge 2 commits into
kagent-dev:mainfrom
supreme-gg-gg:fix/go-tracing-2

Conversation

@supreme-gg-gg

Copy link
Copy Markdown
Contributor

Go OTLP gRPC exporter parses OTEL_EXPORTER_OTLP_TIMEOUT as milliseconds by default but Python parses it as seconds. So our current value set via the controller chart will be parsed incorrectly, causing intermittent traces export issues like traces export: exporter export timeout: rpc error: code = DeadlineExceeded desc = context deadline exceeded

Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Copilot AI review requested due to automatic review settings April 7, 2026 16:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adjusts Go ADK OTLP gRPC exporter configuration to avoid mis-parsing OTEL_EXPORTER_OTLP_TIMEOUT (Go treating numeric values as ms vs Python treating them as seconds), which has been causing intermittent trace export timeouts.

Changes:

  • Adds a shared timeoutFromEnv(...) helper to parse OTLP timeout env vars (duration strings or integers) with a fallback default.
  • Explicitly applies WithTimeout(...) to both OTLP trace and log gRPC exporters, using signal-specific env vars with a global fallback.
  • Introduces a package-level default OTLP export timeout constant.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +171 to +193
// timeoutFromEnv reads OTLP timeout env vars and returns a duration.
// Supported formats:
// - duration strings (e.g. "15s", "15000ms")
// - plain integers:
// - <1000 treated as seconds (backward-compatible with chart values like 15)
// - >=1000 treated as milliseconds (OTEL default convention)
func timeoutFromEnv(keys ...string) time.Duration {
for _, key := range keys {
raw := strings.TrimSpace(os.Getenv(key))
if raw == "" {
continue
}

if d, err := time.ParseDuration(raw); err == nil {
return d
}

if n, err := strconv.Atoi(raw); err == nil {
if n < 1000 {
return time.Duration(n) * time.Second
}
return time.Duration(n) * time.Millisecond
}

Copilot AI Apr 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

timeoutFromEnv changes the meaning of numeric-only OTLP timeout values: for values <1000 it treats them as seconds, whereas upstream OpenTelemetry Go treats numeric-only values as milliseconds. This divergence can surprise users who set OTEL_EXPORTER_OTLP_TIMEOUT=500 expecting 500ms and instead get 500s. Consider requiring explicit units (e.g. "15s") and parsing plain integers as milliseconds, or scoping the seconds-compat behavior to a kagent-specific legacy env var / chart-only knob.

Copilot uses AI. Check for mistakes.
Comment on lines +184 to +193
if d, err := time.ParseDuration(raw); err == nil {
return d
}

if n, err := strconv.Atoi(raw); err == nil {
if n < 1000 {
return time.Duration(n) * time.Second
}
return time.Duration(n) * time.Millisecond
}

Copilot AI Apr 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

timeoutFromEnv accepts negative numeric values (e.g. "-1"), which will produce a negative time.Duration and can lead to immediate failures or undefined exporter behavior. Consider treating non-positive parsed durations/integers as invalid and falling back to the next key (or the default) instead of returning them.

Copilot uses AI. Check for mistakes.
Comment on lines +171 to +197
// timeoutFromEnv reads OTLP timeout env vars and returns a duration.
// Supported formats:
// - duration strings (e.g. "15s", "15000ms")
// - plain integers:
// - <1000 treated as seconds (backward-compatible with chart values like 15)
// - >=1000 treated as milliseconds (OTEL default convention)
func timeoutFromEnv(keys ...string) time.Duration {
for _, key := range keys {
raw := strings.TrimSpace(os.Getenv(key))
if raw == "" {
continue
}

if d, err := time.ParseDuration(raw); err == nil {
return d
}

if n, err := strconv.Atoi(raw); err == nil {
if n < 1000 {
return time.Duration(n) * time.Second
}
return time.Duration(n) * time.Millisecond
}
}

return defaultOTLPExportTimeout
}

Copilot AI Apr 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change introduces new timeout parsing/precedence logic (timeoutFromEnv) and applies it to both trace and log exporters, but there are no unit tests covering the parsing rules (duration strings vs integers, <1000 seconds behavior, key precedence, invalid inputs). Since this package already has tests, add focused tests for timeoutFromEnv using t.Setenv to prevent regressions.

Copilot uses AI. Check for mistakes.
Comment on lines +110 to +113
otlptracegrpc.WithTimeout(timeoutFromEnv(
"OTEL_EXPORTER_OTLP_TRACES_TIMEOUT",
"OTEL_EXPORTER_OTLP_TIMEOUT",
)),

Copilot AI Apr 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WithTimeout(timeoutFromEnv(...)) now always sets a timeout even when no OTLP timeout env vars are provided (due to the hardcoded defaultOTLPExportTimeout). That changes behavior vs relying on the exporter’s built-in default, and can affect deployments that don’t set these env vars. Consider only adding WithTimeout when a relevant env var is present, or explicitly document/align the chosen default with the upstream default.

Suggested change
otlptracegrpc.WithTimeout(timeoutFromEnv(
"OTEL_EXPORTER_OTLP_TRACES_TIMEOUT",
"OTEL_EXPORTER_OTLP_TIMEOUT",
)),
}
if strings.TrimSpace(os.Getenv("OTEL_EXPORTER_OTLP_TRACES_TIMEOUT")) != "" ||
strings.TrimSpace(os.Getenv("OTEL_EXPORTER_OTLP_TIMEOUT")) != "" {
opts = append(opts, otlptracegrpc.WithTimeout(timeoutFromEnv(
"OTEL_EXPORTER_OTLP_TRACES_TIMEOUT",
"OTEL_EXPORTER_OTLP_TIMEOUT",
)))

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants