feat: add FormatBigMetric, SetObjectFieldByPath from utils to support metric render

This commit is contained in:
tonysken 2026-04-08 23:10:10 +01:00
parent e758c35e3b
commit 96734cb3ef
No known key found for this signature in database
GPG key ID: 67D2B9EB6131A6CE
2 changed files with 235 additions and 0 deletions

View file

@ -410,3 +410,71 @@ func marshalIntoFormat(data interface{}, format string) ([]byte, error) {
return nil, errors.New(fmt.Sprintf("Unsupported detailization format: %s", format))
}
}
// FormatBigMetric takes a big metric and formats it into a human readable string with the appropriate unit.
// For example, if you give it 1000000 with a base unit of bytes, it will return "1.000 MB".
// If you give it 1000000000 with a base unit of nanoseconds, it will return "1.000 s"
func FormatBigMetric(number int64, baseUnitName string) string {
memoryUnits := []string{"KB", "MB", "GB", "TB"}
cpuUnits := []string{"µs", "ms", "s", "m", "h"}
const unit = 1000
if number < unit {
return fmt.Sprintf("%d", number)
}
div, exp := int64(unit), 0
for n := number / unit; n >= unit; n /= unit {
div *= unit
exp++
}
switch baseUnitName {
case "bytes":
return fmt.Sprintf("%.3f %s", float64(number)/float64(div), memoryUnits[exp-1])
case "nanoseconds":
return fmt.Sprintf("%.3f %s", float64(number)/float64(div), cpuUnits[exp-1])
default:
return fmt.Sprintf("%d", number)
}
}
// SetObjectFieldByPath takes an object, a path to a field in that object, and a value,
// and sets the field at that path to the value.
// For example, if you have an object {"a": {"b": {"c": 1}}}, a path of "a.b.c", and a value of 2,
// it will set the object to {"a": {"b": {"c": 2}}}
func SetObjectFieldByPath(object *map[string]interface{}, path string, value interface{}) error {
re := regexp.MustCompile(`^\.`)
if !re.MatchString(path) && path != "" {
return fmt.Errorf("Invalid path format %s, path should start with a dot", path)
}
targetObject := map[string]interface{}{}
currentObject := *object
targetObject = copyMapByPath(currentObject, targetObject, path, value)
*object = targetObject
return nil
}
// copyMapByPath is a helper function for SetObjectFieldByPath
// that recursively copies an object while setting the value at the right path
func copyMapByPath(currentObject map[string]interface{}, targetObject map[string]interface{}, path string, value interface{}) map[string]interface{} {
if path == "" {
return currentObject
}
keyPathSlice := strings.Split(path, ".")[1:]
currentKeys := make([]string, 0, len(currentObject))
for k := range currentObject {
currentKeys = append(currentKeys, k)
}
for _, key := range currentKeys {
if key != keyPathSlice[0] {
targetObject[key] = currentObject[key]
} else {
if len(keyPathSlice) == 1 {
targetObject[key] = value
} else {
nextObject := currentObject[key].(map[string]interface{})
targetObject[key] = copyMapByPath(nextObject, map[string]interface{}{}, "."+strings.Join(keyPathSlice[1:], "."), value)
}
}
}
return targetObject
}

View file

@ -319,3 +319,170 @@ quux:
}
}
}
func TestSetObjectFieldByPath(t *testing.T) {
type scenario struct {
object *map[string]interface{}
path string
value interface{}
expected map[string]interface{}
expectedErr error
}
scenarios := []scenario{
{
object: &map[string]interface{}{
"foo": "replacement",
},
path: ".foo",
value: "bar",
expected: map[string]interface{}{"foo": "bar"},
expectedErr: nil,
},
{
object: &map[string]interface{}{
"foo": map[string]interface{}{
"bar": "replacement",
},
},
path: ".foo.bar",
value: "bar_1",
expected: map[string]interface{}{"foo": map[string]interface{}{"bar": "bar_1"}},
expectedErr: nil,
},
{
object: &map[string]interface{}{
"foo": map[string]interface{}{
"foo_1": "foo_1_1",
"foo_2": map[string]interface{}{
"foo_2_1": "foo_2_1_1",
},
"foo_3": map[string]interface{}{
"foo_3_1": "foo_3_1_1",
"foo_3_2": "foo_3_2_1",
"foo_3_3": map[string]interface{}{
"foo_3_3_1": "foo_3_3_1_1",
"foo_3_3_2": "foo_3_3_2_1",
},
},
"bar": map[string]interface{}{
"bar_1": "replacement",
},
},
},
path: ".foo.bar.bar_1",
value: "bar_1_1",
expected: map[string]interface{}{
"foo": map[string]interface{}{
"foo_1": "foo_1_1",
"foo_2": map[string]interface{}{
"foo_2_1": "foo_2_1_1",
},
"foo_3": map[string]interface{}{
"foo_3_1": "foo_3_1_1",
"foo_3_2": "foo_3_2_1",
"foo_3_3": map[string]interface{}{
"foo_3_3_1": "foo_3_3_1_1",
"foo_3_3_2": "foo_3_3_2_1",
},
},
"bar": map[string]interface{}{
"bar_1": "bar_1_1",
},
},
},
expectedErr: nil,
},
{
object: &map[string]interface{}{
"foo": map[string]interface{}{
"foo_1": "foo_1_1",
"foo_2": map[string]interface{}{
"foo_2_1": "foo_2_1_1",
},
"foo_3": map[string]interface{}{
"foo_3_1": "foo_3_1_1",
"foo_3_2": "foo_3_2_1",
"foo_3_3": map[string]interface{}{
"foo_3_3_1": "foo_3_3_1_1",
"foo_3_3_2": "foo_3_3_2_1",
},
},
"bar": map[string]interface{}{
"bar_1": map[string]interface{}{
"bar_1_1": map[string]interface{}{
"bar_1_1_1": map[string]interface{}{
"bar_1_1_1_1": "replacement",
},
},
},
},
},
},
path: ".foo.bar.bar_1.bar_1_1.bar_1_1_1.bar_1_1_1_1",
value: "bar_1_1_1_1_1",
expected: map[string]interface{}{
"foo": map[string]interface{}{
"foo_1": "foo_1_1",
"foo_2": map[string]interface{}{
"foo_2_1": "foo_2_1_1",
},
"foo_3": map[string]interface{}{
"foo_3_1": "foo_3_1_1",
"foo_3_2": "foo_3_2_1",
"foo_3_3": map[string]interface{}{
"foo_3_3_1": "foo_3_3_1_1",
"foo_3_3_2": "foo_3_3_2_1",
},
},
"bar": map[string]interface{}{
"bar_1": map[string]interface{}{
"bar_1_1": map[string]interface{}{
"bar_1_1_1": map[string]interface{}{
"bar_1_1_1_1": "bar_1_1_1_1_1",
},
},
},
},
},
},
expectedErr: nil,
},
{
object: &map[string]interface{}{
"foo": map[string]interface{}{
"bar": "replacement",
},
},
path: "",
value: "bar_1",
expected: map[string]interface{}{"foo": map[string]interface{}{"bar": "replacement"}},
expectedErr: nil,
},
{
object: &map[string]interface{}{
"foo": map[string]interface{}{
"bar": "replacement",
},
},
path: "foo.bar",
value: "bar_1",
expected: map[string]interface{}{
"foo": map[string]interface{}{
"bar": "replacement",
},
},
expectedErr: errors.New("Invalid path format foo.bar, path should start with a dot"),
},
}
for _, s := range scenarios {
err := SetObjectFieldByPath(s.object, s.path, s.value)
assert.EqualValues(t, s.expected, *s.object)
if s.expectedErr != nil {
assert.EqualError(t, err, s.expectedErr.Error())
} else {
assert.NoError(t, err)
}
}
}