diff --git a/plugins/serializers/carbon2/carbon2.go b/plugins/serializers/carbon2/carbon2.go index 0c7b4eca5..1b05d4cb2 100644 --- a/plugins/serializers/carbon2/carbon2.go +++ b/plugins/serializers/carbon2/carbon2.go @@ -61,7 +61,7 @@ func (s *Serializer) createObject(metric telegraf.Metric) []byte { metricsFormat := s.getMetricsFormat() for fieldName, fieldValue := range metric.Fields() { - if !isNumeric(fieldValue) { + if isString(fieldValue) { continue } @@ -88,7 +88,7 @@ func (s *Serializer) createObject(metric telegraf.Metric) []byte { m.WriteString(" ") } m.WriteString(" ") - m.WriteString(fmt.Sprintf("%v", fieldValue)) + m.WriteString(formatValue(fieldValue)) m.WriteString(" ") m.WriteString(strconv.FormatInt(metric.Time().Unix(), 10)) m.WriteString("\n") @@ -122,11 +122,33 @@ func serializeMetricIncludeField(name, fieldName string) string { ) } -func isNumeric(v interface{}) bool { - switch v.(type) { - case string: - return false +func formatValue(fieldValue interface{}) string { + switch v := fieldValue.(type) { + case bool: + // Print bools as 0s and 1s + return fmt.Sprintf("%d", bool2int(v)) default: - return true + return fmt.Sprintf("%v", v) } } + +func isString(v interface{}) bool { + switch v.(type) { + case string: + return true + default: + return false + } +} + +func bool2int(b bool) int { + // Slightly more optimized than a usual if ... return ... else return ... . + // See: https://0x0f.me/blog/golang-compiler-optimization/ + var i int + if b { + i = 1 + } else { + i = 0 + } + return i +} diff --git a/plugins/serializers/carbon2/carbon2_test.go b/plugins/serializers/carbon2/carbon2_test.go index 64f621875..45a0b7114 100644 --- a/plugins/serializers/carbon2/carbon2_test.go +++ b/plugins/serializers/carbon2/carbon2_test.go @@ -209,6 +209,63 @@ func TestSerializeMetricString(t *testing.T) { } } +func TestSerializeMetricBool(t *testing.T) { + requireMetric := func(t *testing.T, tim time.Time, value bool) telegraf.Metric { + tags := map[string]string{ + "tag_name": "tag_value", + } + fields := map[string]interface{}{ + "java_lang_GarbageCollector_Valid": value, + } + + m, err := metric.New("cpu", tags, fields, tim) + require.NoError(t, err) + + return m + } + + now := time.Now() + + testcases := []struct { + metric telegraf.Metric + format string + expected string + }{ + { + metric: requireMetric(t, now, false), + format: Carbon2FormatFieldSeparate, + expected: fmt.Sprintf("metric=cpu field=java_lang_GarbageCollector_Valid tag_name=tag_value 0 %d\n", now.Unix()), + }, + { + metric: requireMetric(t, now, false), + format: Carbon2FormatMetricIncludesField, + expected: fmt.Sprintf("metric=cpu_java_lang_GarbageCollector_Valid tag_name=tag_value 0 %d\n", now.Unix()), + }, + { + metric: requireMetric(t, now, true), + format: Carbon2FormatFieldSeparate, + expected: fmt.Sprintf("metric=cpu field=java_lang_GarbageCollector_Valid tag_name=tag_value 1 %d\n", now.Unix()), + }, + { + metric: requireMetric(t, now, true), + format: Carbon2FormatMetricIncludesField, + expected: fmt.Sprintf("metric=cpu_java_lang_GarbageCollector_Valid tag_name=tag_value 1 %d\n", now.Unix()), + }, + } + + for _, tc := range testcases { + t.Run(tc.format, func(t *testing.T) { + s, err := NewSerializer(tc.format) + require.NoError(t, err) + + buf, err := s.Serialize(tc.metric) + require.NoError(t, err) + + assert.Equal(t, tc.expected, string(buf)) + }) + } +} + func TestSerializeBatch(t *testing.T) { m := MustMetric( metric.New(