diff --git a/CHANGELOG.md b/CHANGELOG.md index 97f2cfa644..cb5aacf64b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,8 +18,11 @@ Main (unreleased) ### Features - Add the function `path_join` to the stdlib. (@wildum) + - Add support to `loki.source.syslog` for the RFC3164 format ("BSD syslog"). (@sushain97) + - Add support to `loki.source.api` to be able to extract the tenant from the HTTP `X-Scope-OrgID` header (@QuentinBisson) +- - (_Experimental_) Add a `loki.secretfilter` component to redact secrets from collected logs. ### Enhancements @@ -31,6 +34,9 @@ Main (unreleased) - Fix an issue where some `faro.receiver` would drop multiple fields defined in `payload.meta.browser`, as fields were defined in the struct. +- SNMP exporter now supports labels in both `target` and `targets` parameters. (@mattdurham) + + ### Bugfixes - Fixed a bug in `import.git` which caused a `"non-fast-forward update"` error message. (@ptodev) diff --git a/docs/sources/reference/components/prometheus/prometheus.exporter.snmp.md b/docs/sources/reference/components/prometheus/prometheus.exporter.snmp.md index ed21bd0423..fec15ddb3c 100644 --- a/docs/sources/reference/components/prometheus/prometheus.exporter.snmp.md +++ b/docs/sources/reference/components/prometheus/prometheus.exporter.snmp.md @@ -65,6 +65,8 @@ The following labels can be set to a target: * `auth`: The SNMP authentication profile to use. * `walk_params`: The config to use for this target. +Any other labels defined are added to the scraped metrics. + ## Blocks The following blocks are supported inside the definition of @@ -83,13 +85,15 @@ The following blocks are supported inside the definition of The `target` block defines an individual SNMP target. The `target` block may be specified multiple times to define multiple targets. The label of the block is required and will be used in the target's `job` label. -| Name | Type | Description | Default | Required | -| -------------- | -------- | --------------------------------------------------------------------- | ------- | -------- | -| `address` | `string` | The address of SNMP device. | | yes | -| `module` | `string` | SNMP module to use for polling. | `""` | no | -| `auth` | `string` | SNMP authentication profile to use. | `""` | no | -| `walk_params` | `string` | Config to use for this target. | `""` | no | -| `snmp_context` | `string` | Override the `context_name` parameter in the SNMP configuration file. | `""` | no | +| Name | Type | Description | Default | Required | +|----------------|---------------|-----------------------------------------------------------------------| ------- | -------- | +| `address` | `string` | The address of SNMP device. | | yes | +| `module` | `string` | SNMP module to use for polling. | `""` | no | +| `auth` | `string` | SNMP authentication profile to use. | `""` | no | +| `walk_params` | `string` | Config to use for this target. | `""` | no | +| `snmp_context` | `string` | Override the `context_name` parameter in the SNMP configuration file. | `""` | no | +| `labels` | `map(string)` | Map of labels to apply to all metrics captured from the target. | `""` | no | + ### walk_param block @@ -136,6 +140,9 @@ prometheus.exporter.snmp "example" { address = "192.168.1.2" module = "if_mib" walk_params = "public" + labels = { + "env" = "dev", + } } target "network_router_2" { @@ -227,6 +234,7 @@ prometheus.exporter.snmp "example" { "address" = "192.168.1.2", "module" = "if_mib", "walk_params" = "public", + "env" = "dev", }, { "name" = "network_router_2", diff --git a/internal/component/prometheus/exporter/snmp/snmp.go b/internal/component/prometheus/exporter/snmp/snmp.go index f3eed3ec1a..556c7229b3 100644 --- a/internal/component/prometheus/exporter/snmp/snmp.go +++ b/internal/component/prometheus/exporter/snmp/snmp.go @@ -3,6 +3,7 @@ package snmp import ( "errors" "fmt" + "slices" "time" "github.com/grafana/alloy/internal/component" @@ -44,12 +45,17 @@ func buildSNMPTargets(baseTarget discovery.Target, args component.Arguments) []d for _, tgt := range snmpTargets { target := make(discovery.Target) + // Set extra labels first, meaning that any other labels will override + for k, v := range tgt.Labels { + target[k] = v + } for k, v := range baseTarget { target[k] = v } target["job"] = target["job"] + "/" + tgt.Name target["__param_target"] = tgt.Target + target["__param_name"] = tgt.Name if tgt.Module != "" { target["__param_module"] = tgt.Module } @@ -71,12 +77,13 @@ func buildSNMPTargets(baseTarget discovery.Target, args component.Arguments) []d // SNMPTarget defines a target to be used by the exporter. type SNMPTarget struct { - Name string `alloy:",label"` - Target string `alloy:"address,attr"` - Module string `alloy:"module,attr,optional"` - Auth string `alloy:"auth,attr,optional"` - WalkParams string `alloy:"walk_params,attr,optional"` - SNMPContext string `alloy:"snmp_context,attr,optional"` + Name string `alloy:",label"` + Target string `alloy:"address,attr"` + Module string `alloy:"module,attr,optional"` + Auth string `alloy:"auth,attr,optional"` + WalkParams string `alloy:"walk_params,attr,optional"` + SNMPContext string `alloy:"snmp_context,attr,optional"` + Labels map[string]string `alloy:"labels,attr,optional"` } type TargetBlock []SNMPTarget @@ -92,6 +99,7 @@ func (t TargetBlock) Convert() []snmp_exporter.SNMPTarget { Auth: target.Auth, WalkParams: target.WalkParams, SNMPContext: target.SNMPContext, + Labels: target.Labels, }) } return targets @@ -134,6 +142,22 @@ type Arguments struct { type TargetsList []map[string]string +// target technically isnt required but its so overloaded within snmp I dont want it leaking. +var ignoredLabels = []string{"name", "module", "auth", "walk_params", "snmp_context", "address", "__address__", "target"} + +func createUserLabels(t map[string]string) map[string]string { + // Need to create labels. + userLabels := make(map[string]string) + for k, v := range t { + // ignore the special labels + if slices.Contains(ignoredLabels, k) { + continue + } + userLabels[k] = v + } + return userLabels +} + func (t TargetsList) Convert() []snmp_exporter.SNMPTarget { targets := make([]snmp_exporter.SNMPTarget, 0, len(t)) for _, target := range t { @@ -145,6 +169,7 @@ func (t TargetsList) Convert() []snmp_exporter.SNMPTarget { Auth: target["auth"], WalkParams: target["walk_params"], SNMPContext: target["snmp_context"], + Labels: createUserLabels(target), }) } return targets @@ -161,6 +186,7 @@ func (t TargetsList) convert() []SNMPTarget { Auth: target["auth"], WalkParams: target["walk_params"], SNMPContext: target["snmp_context"], + Labels: createUserLabels(target), }) } return targets diff --git a/internal/component/prometheus/exporter/snmp/snmp_test.go b/internal/component/prometheus/exporter/snmp/snmp_test.go index b79e8a3412..677d27e813 100644 --- a/internal/component/prometheus/exporter/snmp/snmp_test.go +++ b/internal/component/prometheus/exporter/snmp/snmp_test.go @@ -126,6 +126,21 @@ func TestConvertConfig(t *testing.T) { require.Equal(t, "network_switch_1", res.SnmpTargets[0].Name) } +func TestConfigLabels(t *testing.T) { + lbls := map[string]string{"env": "dev"} + args := Arguments{ + ConfigFile: "modules.yml", + Targets: TargetBlock{{Name: "network_switch_1", Target: "192.168.1.2", Module: "if_mib", Labels: lbls}}, + WalkParams: WalkParams{{Name: "public", Retries: 2}}, + } + + res := args.Convert() + require.Equal(t, "modules.yml", res.SnmpConfigFile) + require.Equal(t, 1, len(res.SnmpTargets)) + require.Equal(t, "network_switch_1", res.SnmpTargets[0].Name) + require.Equal(t, res.SnmpTargets[0].Labels["env"], "dev") +} + func TestConvertConfigWithInlineConfig(t *testing.T) { args := Arguments{ ConfigStruct: config.Config{Modules: map[string]*config.Module{"if_mib": {Walk: []string{"1.3.6.1.2.1.2"}}}}, @@ -177,6 +192,32 @@ func TestConvertTargetsList(t *testing.T) { require.Equal(t, "1.3.6.1.2.1.2", res[0].WalkParams) } +func TestConvertTargetsListWithLabels(t *testing.T) { + targets := TargetsList{ + { + "name": "network_switch_1", + "address": "192.168.1.2", + "module": "if_mib", + "auth": "public_v2", + "walk_params": "1.3.6.1.2.1.2", + "env": "dev", + "app": "falcon", + }, + } + + res := targets.Convert() + require.Equal(t, 1, len(res)) + require.Equal(t, "network_switch_1", res[0].Name) + require.Equal(t, "192.168.1.2", res[0].Target) + require.Equal(t, "if_mib", res[0].Module) + require.Equal(t, "public_v2", res[0].Auth) + require.Equal(t, "1.3.6.1.2.1.2", res[0].WalkParams) + require.Len(t, res[0].Labels, 2) + require.Equal(t, "dev", res[0].Labels["env"]) + require.Equal(t, "falcon", res[0].Labels["app"]) + +} + func TestConvertTargetsListAlternativeAddress(t *testing.T) { targets := TargetsList{ { diff --git a/internal/converter/internal/staticconvert/internal/build/snmp_exporter.go b/internal/converter/internal/staticconvert/internal/build/snmp_exporter.go index 44557042e6..b6250cfce5 100644 --- a/internal/converter/internal/staticconvert/internal/build/snmp_exporter.go +++ b/internal/converter/internal/staticconvert/internal/build/snmp_exporter.go @@ -25,6 +25,9 @@ func toSnmpExporter(config *snmp_exporter.Config) *snmp.Arguments { target["auth"] = t.Auth target["walk_params"] = t.WalkParams target["snmp_context"] = t.SNMPContext + for k, v := range t.Labels { + target[k] = v + } targets = append(targets, target) } diff --git a/internal/static/integrations/snmp_exporter/snmp_exporter.go b/internal/static/integrations/snmp_exporter/snmp_exporter.go index 0a449ed60d..841604d0b7 100644 --- a/internal/static/integrations/snmp_exporter/snmp_exporter.go +++ b/internal/static/integrations/snmp_exporter/snmp_exporter.go @@ -33,6 +33,7 @@ type SNMPTarget struct { Auth string `yaml:"auth"` WalkParams string `yaml:"walk_params,omitempty"` SNMPContext string `yaml:"snmp_context,omitempty"` + Labels map[string]string } // Config configures the SNMP integration.