diff --git a/pkg/app/processor.go b/pkg/app/processor.go index 8b072f99..4331d39d 100644 --- a/pkg/app/processor.go +++ b/pkg/app/processor.go @@ -135,11 +135,12 @@ func (a *App) promFormat(rrevs [][]*formatters.EventMsg, outName string) ([]byte return nil, fmt.Errorf("output %q must be of type 'prometheus' or 'remote_write'", outName) } mb := &promcom.MetricBuilder{ - Prefix: a.Config.FileConfig.GetString(outputPath + "/metric-prefix"), - AppendSubscriptionName: a.Config.FileConfig.GetBool(outputPath + "/append-subscription-name"), - StringsAsLabels: a.Config.FileConfig.GetBool(outputPath + "/strings-as-labels"), - OverrideTimestamps: a.Config.FileConfig.GetBool(outputPath + "/override-timestamps"), - ExportTimestamps: a.Config.FileConfig.GetBool(outputPath + "/export-timestamps"), + Prefix: a.Config.FileConfig.GetString(outputPath + "/metric-prefix"), + AppendSubscriptionName: a.Config.FileConfig.GetBool(outputPath + "/append-subscription-name"), + StringsAsLabels: a.Config.FileConfig.GetBool(outputPath + "/strings-as-labels"), + StringsAsSingleMetricLabels: a.Config.FileConfig.GetBool(outputPath + "/strings-as-single-metric-labels"), + OverrideTimestamps: a.Config.FileConfig.GetBool(outputPath + "/override-timestamps"), + ExportTimestamps: a.Config.FileConfig.GetBool(outputPath + "/export-timestamps"), } b := new(bytes.Buffer) diff --git a/pkg/outputs/prometheus_output/prometheus_common.go b/pkg/outputs/prometheus_output/prometheus_common.go index ffb3f1e2..58895669 100644 --- a/pkg/outputs/prometheus_output/prometheus_common.go +++ b/pkg/outputs/prometheus_output/prometheus_common.go @@ -146,15 +146,24 @@ func (mb *MetricBuilder) MetricsFromEvent(ev *formatters.EventMsg, now time.Time labels := mb.GetLabels(ev) for vName, val := range ev.Values { v, err := toFloat(val) + metricLabels := labels if err != nil { - if !mb.StringsAsLabels { + if !mb.StringsAsLabels && !mb.StringsAsSingleMetricLabels { continue } v = 1.0 + if mb.StringsAsSingleMetricLabels { + if s, ok := val.(string); ok { + labelName := MetricNameRegex.ReplaceAllString(path.Base(vName), "_") + metricLabels = make([]prompb.Label, 0, len(labels)+1) + metricLabels = append(metricLabels, labels...) + metricLabels = append(metricLabels, prompb.Label{Name: labelName, Value: s}) + } + } } pm := &PromMetric{ Name: mb.MetricName(ev.Name, vName), - labels: labels, + labels: metricLabels, value: v, AddedAt: now, } @@ -171,11 +180,12 @@ func (mb *MetricBuilder) MetricsFromEvent(ev *formatters.EventMsg, now time.Time } type MetricBuilder struct { - Prefix string - AppendSubscriptionName bool - StringsAsLabels bool - OverrideTimestamps bool - ExportTimestamps bool + Prefix string + AppendSubscriptionName bool + StringsAsLabels bool + StringsAsSingleMetricLabels bool + OverrideTimestamps bool + ExportTimestamps bool } func (m *MetricBuilder) GetLabels(ev *formatters.EventMsg) []prompb.Label { diff --git a/pkg/outputs/prometheus_output/prometheus_common_test.go b/pkg/outputs/prometheus_output/prometheus_common_test.go index 7f0e125c..05bfd106 100644 --- a/pkg/outputs/prometheus_output/prometheus_common_test.go +++ b/pkg/outputs/prometheus_output/prometheus_common_test.go @@ -292,9 +292,10 @@ func TestMetricBuilder_MetricsFromEvent(t *testing.T) { tests := []struct { name string // description of this test case // Named input parameters for target function. - ev *formatters.EventMsg - now time.Time - want []*PromMetric + ev *formatters.EventMsg + now time.Time + stringsAsSingleMetricLabels bool + want []*PromMetric }{ { name: "no_duplicates", @@ -385,11 +386,58 @@ func TestMetricBuilder_MetricsFromEvent(t *testing.T) { }, }, }, + { + name: "string_value_label_only_on_own_metric", + stringsAsSingleMetricLabels: true, + ev: &formatters.EventMsg{ + Name: "sub1", + Timestamp: 1777889507942770536, + Tags: map[string]string{ + "interface_name": "onu 1/1/95", + "source": "lab-olt", + }, + Values: map[string]any{ + "/interfaces-state/interface/admin-status": "1", + "/interfaces-state/interface/name": "onu 1/1/95", + "/interfaces-state/interface/onu/clei-code": "CLEI-1234", + }, + }, + now: time.Unix(0, 1777889507942770536), + want: []*PromMetric{ + { + Name: "interfaces_state_interface_admin_status", + value: 1, + labels: []prompb.Label{ + {Name: "interface_name", Value: "onu 1/1/95"}, + {Name: "source", Value: "lab-olt"}, + }, + }, + { + Name: "interfaces_state_interface_name", + value: 1, + labels: []prompb.Label{ + {Name: "interface_name", Value: "onu 1/1/95"}, + {Name: "source", Value: "lab-olt"}, + {Name: "name", Value: "onu 1/1/95"}, + }, + }, + { + Name: "interfaces_state_interface_onu_clei_code", + value: 1, + labels: []prompb.Label{ + {Name: "interface_name", Value: "onu 1/1/95"}, + {Name: "source", Value: "lab-olt"}, + {Name: "clei_code", Value: "CLEI-1234"}, + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mb := &MetricBuilder{ - StringsAsLabels: true, + StringsAsLabels: !tt.stringsAsSingleMetricLabels, + StringsAsSingleMetricLabels: tt.stringsAsSingleMetricLabels, } got := mb.MetricsFromEvent(tt.ev, tt.now) if len(got) != len(tt.want) { diff --git a/pkg/outputs/prometheus_output/prometheus_output/prometheus_output.go b/pkg/outputs/prometheus_output/prometheus_output/prometheus_output.go index f2fab012..7deec769 100644 --- a/pkg/outputs/prometheus_output/prometheus_output/prometheus_output.go +++ b/pkg/outputs/prometheus_output/prometheus_output/prometheus_output.go @@ -106,7 +106,8 @@ type config struct { OverrideTimestamps bool `mapstructure:"override-timestamps,omitempty" json:"override-timestamps,omitempty"` AddTarget string `mapstructure:"add-target,omitempty" json:"add-target,omitempty"` TargetTemplate string `mapstructure:"target-template,omitempty" json:"target-template,omitempty"` - StringsAsLabels bool `mapstructure:"strings-as-labels,omitempty" json:"strings-as-labels,omitempty"` + StringsAsLabels bool `mapstructure:"strings-as-labels,omitempty" json:"strings-as-labels,omitempty"` + StringsAsSingleMetricLabels bool `mapstructure:"strings-as-single-metric-labels,omitempty" json:"strings-as-single-metric-labels,omitempty"` Debug bool `mapstructure:"debug,omitempty" json:"debug,omitempty"` EventProcessors []string `mapstructure:"event-processors,omitempty" json:"event-processors,omitempty"` ServiceRegistration *serviceRegistration `mapstructure:"service-registration,omitempty" json:"service-registration,omitempty"` @@ -219,11 +220,12 @@ func (p *prometheusOutput) Init(ctx context.Context, name string, cfg map[string } dc.mb = &promcom.MetricBuilder{ - Prefix: newCfg.MetricPrefix, - AppendSubscriptionName: newCfg.AppendSubscriptionName, - StringsAsLabels: newCfg.StringsAsLabels, - OverrideTimestamps: newCfg.OverrideTimestamps, - ExportTimestamps: newCfg.ExportTimestamps, + Prefix: newCfg.MetricPrefix, + AppendSubscriptionName: newCfg.AppendSubscriptionName, + StringsAsLabels: newCfg.StringsAsLabels, + StringsAsSingleMetricLabels: newCfg.StringsAsSingleMetricLabels, + OverrideTimestamps: newCfg.OverrideTimestamps, + ExportTimestamps: newCfg.ExportTimestamps, } p.dynCfg.Store(dc) @@ -361,11 +363,12 @@ func (p *prometheusOutput) Update(ctx context.Context, cfg map[string]any) error // metric builder dc.mb = &promcom.MetricBuilder{ - Prefix: tmp.MetricPrefix, - AppendSubscriptionName: tmp.AppendSubscriptionName, - StringsAsLabels: tmp.StringsAsLabels, - OverrideTimestamps: tmp.OverrideTimestamps, - ExportTimestamps: tmp.ExportTimestamps, + Prefix: tmp.MetricPrefix, + AppendSubscriptionName: tmp.AppendSubscriptionName, + StringsAsLabels: tmp.StringsAsLabels, + StringsAsSingleMetricLabels: tmp.StringsAsSingleMetricLabels, + OverrideTimestamps: tmp.OverrideTimestamps, + ExportTimestamps: tmp.ExportTimestamps, } p.dynCfg.Store(dc) diff --git a/pkg/outputs/prometheus_output/prometheus_write_output/prometheus_write_output.go b/pkg/outputs/prometheus_output/prometheus_write_output/prometheus_write_output.go index 689c8034..9d6890e1 100644 --- a/pkg/outputs/prometheus_output/prometheus_write_output/prometheus_write_output.go +++ b/pkg/outputs/prometheus_output/prometheus_write_output/prometheus_write_output.go @@ -103,7 +103,8 @@ type config struct { AppendSubscriptionName bool `mapstructure:"append-subscription-name,omitempty" json:"append-subscription-name,omitempty"` AddTarget string `mapstructure:"add-target,omitempty" json:"add-target,omitempty"` TargetTemplate string `mapstructure:"target-template,omitempty" json:"target-template,omitempty"` - StringsAsLabels bool `mapstructure:"strings-as-labels,omitempty" json:"strings-as-labels,omitempty"` + StringsAsLabels bool `mapstructure:"strings-as-labels,omitempty" json:"strings-as-labels,omitempty"` + StringsAsSingleMetricLabels bool `mapstructure:"strings-as-single-metric-labels,omitempty" json:"strings-as-single-metric-labels,omitempty"` EventProcessors []string `mapstructure:"event-processors,omitempty" json:"event-processors,omitempty"` NumWorkers int `mapstructure:"num-workers,omitempty" json:"num-workers,omitempty"` NumWriters int `mapstructure:"num-writers,omitempty" json:"num-writers,omitempty"` @@ -226,9 +227,10 @@ func (p *promWriteOutput) Init(ctx context.Context, name string, cfg map[string] } dc.mb = &promcom.MetricBuilder{ - Prefix: ncfg.MetricPrefix, - AppendSubscriptionName: ncfg.AppendSubscriptionName, - StringsAsLabels: ncfg.StringsAsLabels, + Prefix: ncfg.MetricPrefix, + AppendSubscriptionName: ncfg.AppendSubscriptionName, + StringsAsLabels: ncfg.StringsAsLabels, + StringsAsSingleMetricLabels: ncfg.StringsAsSingleMetricLabels, } p.dynCfg.Store(dc) @@ -304,9 +306,10 @@ func (p *promWriteOutput) Update(ctx context.Context, cfg map[string]any) error // metric builder dc.mb = &promcom.MetricBuilder{ - Prefix: newCfg.MetricPrefix, - AppendSubscriptionName: newCfg.AppendSubscriptionName, - StringsAsLabels: newCfg.StringsAsLabels, + Prefix: newCfg.MetricPrefix, + AppendSubscriptionName: newCfg.AppendSubscriptionName, + StringsAsLabels: newCfg.StringsAsLabels, + StringsAsSingleMetricLabels: newCfg.StringsAsSingleMetricLabels, } // rebuild processors ?