-
Notifications
You must be signed in to change notification settings - Fork 2.1k
feat: add valueType field for explicit duration parsing in custom resources #2797
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -173,6 +173,7 @@ func newCompiledMetric(m Metric) (compiledMetric, error) { | |
| ValueFrom: valueFromPath, | ||
| NilIsZero: m.Gauge.NilIsZero, | ||
| labelFromKey: m.Gauge.LabelFromKey, | ||
| valueType: m.Gauge.ValueType, | ||
| }, nil | ||
| case metric.Info: | ||
| if m.Info == nil { | ||
|
|
@@ -216,6 +217,7 @@ type compiledGauge struct { | |
| labelFromKey string | ||
| ValueFrom valuePath | ||
| NilIsZero bool | ||
| valueType ValueType | ||
| } | ||
|
|
||
| func (c *compiledGauge) Values(v interface{}) (result []eachValue, errs []error) { | ||
|
|
@@ -241,7 +243,21 @@ func (c *compiledGauge) Values(v interface{}) (result []eachValue, errs []error) | |
| len(sValueFrom) > 2 { | ||
| extractedValueFrom := sValueFrom[1 : len(sValueFrom)-1] | ||
| if key == extractedValueFrom { | ||
| gotFloat, err := toFloat64(it, c.NilIsZero) | ||
| // Check if explicit valueType is specified | ||
| var gotFloat float64 | ||
| var err error | ||
|
|
||
| switch c.valueType { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The value type switching logic is duplicated in two places func (c *compiledGauge) parseValue(value interface{}) (float64, error) {
switch c.valueType {
case ValueTypeDuration:
return parseDurationValue(value)
case ValueTypeQuantity, ValueTypeDefault:
return toFloat64(value, c.NilIsZero)
default:
return 0, fmt.Errorf("unknown valueType: %s", c.valueType)
}
}Then use it in both locations: |
||
| case ValueTypeDuration: | ||
| gotFloat, err = parseDurationValue(it) | ||
| case ValueTypeQuantity: | ||
| gotFloat, err = toFloat64(it, c.NilIsZero) | ||
| case ValueTypeDefault: | ||
| gotFloat, err = toFloat64(it, c.NilIsZero) | ||
| default: | ||
| err = fmt.Errorf("unknown valueType: %s", c.valueType) | ||
| } | ||
|
|
||
| if err != nil { | ||
| onError(fmt.Errorf("[%s]: %w", key, err)) | ||
| continue | ||
|
|
@@ -462,7 +478,23 @@ func (c compiledGauge) value(it interface{}) (*eachValue, error) { | |
| // Don't error if there was not a type-casting issue (`toFloat64`). | ||
| return nil, nil | ||
| } | ||
| value, err := toFloat64(got, c.NilIsZero) | ||
|
|
||
| // Check if explicit valueType is specified | ||
| var value float64 | ||
| var err error | ||
| switch c.valueType { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned above. |
||
| case ValueTypeDuration: | ||
| value, err = parseDurationValue(got) | ||
| case ValueTypeQuantity: | ||
| // Use existing quantity parsing from toFloat64 | ||
| value, err = toFloat64(got, c.NilIsZero) | ||
| case ValueTypeDefault: | ||
| // Fall through to auto-detection (existing logic) | ||
| value, err = toFloat64(got, c.NilIsZero) | ||
| default: | ||
| return nil, fmt.Errorf("unknown valueType: %s", c.valueType) | ||
| } | ||
|
|
||
| if err != nil { | ||
| return nil, fmt.Errorf("%s: %w", c.ValueFrom, err) | ||
| } | ||
|
|
@@ -710,6 +742,35 @@ func scrapeValuesFor(e compiledEach, obj map[string]interface{}) ([]eachValue, [ | |
| return result, errs | ||
| } | ||
|
|
||
| // parseDurationValue converts a duration string to seconds as float64. | ||
| // It uses time.ParseDuration to parse strings like "1h", "30m", "1h30m45s". | ||
| // The result is converted to seconds for Prometheus compatibility. | ||
| func parseDurationValue(value interface{}) (float64, error) { | ||
| var durationStr string | ||
|
|
||
| switch v := value.(type) { | ||
| case string: | ||
| durationStr = v | ||
| case nil: | ||
| return 0, fmt.Errorf("nil value cannot be parsed as duration") | ||
| default: | ||
| return 0, fmt.Errorf("value must be a string for duration parsing, got %T", value) | ||
| } | ||
|
|
||
| // Handle empty string | ||
| if durationStr == "" { | ||
| return 0, fmt.Errorf("empty string cannot be parsed as duration") | ||
| } | ||
|
|
||
| duration, err := time.ParseDuration(durationStr) | ||
| if err != nil { | ||
| return 0, fmt.Errorf("failed to parse duration '%s': %w", durationStr, err) | ||
| } | ||
|
|
||
| // Convert to seconds as float64 for Prometheus compatibility | ||
| return duration.Seconds(), nil | ||
| } | ||
|
|
||
| // toFloat64 converts the value to a float64 which is the value type for any metric. | ||
| func toFloat64(value interface{}, nilIsZero bool) (float64, error) { | ||
| var v float64 | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure the difference between
ValueTypeDefaultandValueTypeQuantity, Both cases call toFloat64(), which already includes automatic quantity detection.