Skip to content
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

[connector/count] Allow counting by attributes #19432

Merged
merged 5 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .chloggen/count-by-attributes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: countconnector

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add ability to count by attributes

# One or more tracking issues related to the change
issues: [19432]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
60 changes: 56 additions & 4 deletions connector/countconnector/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,15 @@ Optionally, emit custom counts by defining metrics under one or more of the foll
- `datapoints`
- `logs`

Under each custom metric name, specify one or more conditions using [OTTL Syntax].
Data that matches any one of the conditions will be counted. i.e. Conditions are ORed together.

Optionally, specify a description for the metric.

Note: If any custom metrics are defined for a data type, the default metric will not be emitted.

#### Conditions

Conditions may be specified for custom metrics. If specified, data that matches any one
of the conditions will be counted. i.e. Conditions are ORed together.

```yaml
receivers:
foo:
Expand All @@ -82,7 +86,29 @@ connectors:
- 'name == "prodevent"'
```

Note: If any custom metrics are defined for a data type, the default metric will not be emitted.
#### Attributes

`spans`, `spanevents`, `datapoints`, and `logs` may be counted according to attributes.

If attributes are specified for custom metrics, a separate count will be generated for each unique
set of attribute values. Each count will be emitted as a data point on the same metric.

Optionally, include a `default_value` for an attribute, to count data that does not contain the attribute.

```yaml
receivers:
foo:
exporters:
bar:
connectors:
count:
logs:
my.log.count:
description: The number of logs from each environment.
attributes:
- key: env
default_value: unspecified_environment
```

### Example Usage

Expand Down Expand Up @@ -177,6 +203,32 @@ service:
exporters: [bar]
```

Count logs with a severity of ERROR or higher. Maintain a separate count for each environment.

```yaml
receivers:
foo:
exporters:
bar:
connectors:
count:
logs:
my.error.log.count:
description: Error+ logs.
conditions:
- `severity_number >= SEVERITY_NUMBER_ERROR`
attributes:
- key: env
service:
pipelines:
logs:
receivers: [foo]
exporters: [count]
metrics:
receivers: [count]
exporters: [bar]
```

Count all spans and span events (default behavior). Count metrics and data points based on the `env` attribute.

```yaml
Expand Down
34 changes: 32 additions & 2 deletions connector/countconnector/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,14 @@ type Config struct {

// MetricInfo for a data type
type MetricInfo struct {
Description string `mapstructure:"description"`
Conditions []string `mapstructure:"conditions"`
Description string `mapstructure:"description"`
Conditions []string `mapstructure:"conditions"`
Attributes []AttributeConfig `mapstructure:"attributes"`
}

type AttributeConfig struct {
Key string `mapstructure:"key"`
DefaultValue string `mapstructure:"default_value"`
}

func (c *Config) Validate() error {
Expand All @@ -64,6 +70,9 @@ func (c *Config) Validate() error {
if _, err = parseConditions(parser, info.Conditions); err != nil {
return fmt.Errorf("spans condition: metric %q: %w", name, err)
}
if err := info.validateAttributes(); err != nil {
return fmt.Errorf("spans attributes: metric %q: %w", name, err)
}
}
for name, info := range c.SpanEvents {
if name == "" {
Expand All @@ -76,6 +85,9 @@ func (c *Config) Validate() error {
if _, err = parseConditions(parser, info.Conditions); err != nil {
return fmt.Errorf("spanevents condition: metric %q: %w", name, err)
}
if err := info.validateAttributes(); err != nil {
return fmt.Errorf("spanevents attributes: metric %q: %w", name, err)
}
}
for name, info := range c.Metrics {
if name == "" {
Expand All @@ -88,6 +100,9 @@ func (c *Config) Validate() error {
if _, err = parseConditions(parser, info.Conditions); err != nil {
return fmt.Errorf("metrics condition: metric %q: %w", name, err)
}
if len(info.Attributes) > 0 {
return fmt.Errorf("metrics attributes not supported: metric %q", name)
}
}

for name, info := range c.DataPoints {
Expand All @@ -101,6 +116,9 @@ func (c *Config) Validate() error {
if _, err = parseConditions(parser, info.Conditions); err != nil {
return fmt.Errorf("datapoints condition: metric %q: %w", name, err)
}
if err := info.validateAttributes(); err != nil {
return fmt.Errorf("spans attributes: metric %q: %w", name, err)
djaglowski marked this conversation as resolved.
Show resolved Hide resolved
}
}
for name, info := range c.Logs {
if name == "" {
Expand All @@ -113,6 +131,18 @@ func (c *Config) Validate() error {
if _, err = parseConditions(parser, info.Conditions); err != nil {
return fmt.Errorf("logs condition: metric %q: %w", name, err)
}
if err := info.validateAttributes(); err != nil {
return fmt.Errorf("logs attributes: metric %q: %w", name, err)
}
}
return nil
}

func (i *MetricInfo) validateAttributes() error {
for _, attr := range i.Attributes {
if attr.Key == "" {
return fmt.Errorf("attribute key missing")
}
}
return nil
}
Expand Down
84 changes: 82 additions & 2 deletions connector/countconnector/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,23 +205,85 @@ func TestLoadConfig(t *testing.T) {
},
},
},
{
name: "attribute",
expect: &Config{
Spans: map[string]MetricInfo{
"my.span.count": {
Description: "My span count by environment.",
Attributes: []AttributeConfig{
{Key: "env"},
},
},
},
SpanEvents: map[string]MetricInfo{
"my.spanevent.count": {
Description: "My span event count by environment.",
Attributes: []AttributeConfig{
{Key: "env"},
},
},
},
Metrics: map[string]MetricInfo{
"my.metric.count": {
Description: "My metric count.",
},
},
DataPoints: map[string]MetricInfo{
"my.datapoint.count": {
Description: "My data point count by environment.",
Attributes: []AttributeConfig{
{Key: "env"},
},
},
},
Logs: map[string]MetricInfo{
"my.logrecord.count": {
Description: "My log record count by environment.",
Attributes: []AttributeConfig{
{Key: "env"},
},
},
},
},
},
{
name: "multiple_metrics",
expect: &Config{
Spans: map[string]MetricInfo{
"my.span.count": {
Description: "My span count."},
Description: "My span count.",
},
"limited.span.count": {
Description: "Limited span count.",
Conditions: []string{`IsMatch(resource.attributes["host.name"], "pod-s") == true`},
Attributes: []AttributeConfig{
{
Key: "env",
},
{
Key: "component",
DefaultValue: "other",
},
},
},
},
SpanEvents: map[string]MetricInfo{
"my.spanevent.count": {
Description: "My span event count."},
Description: "My span event count.",
},
"limited.spanevent.count": {
Description: "Limited span event count.",
Conditions: []string{`IsMatch(resource.attributes["host.name"], "pod-e") == true`},
Attributes: []AttributeConfig{
{
Key: "env",
},
{
Key: "component",
DefaultValue: "other",
},
},
},
},
Metrics: map[string]MetricInfo{
Expand All @@ -239,6 +301,15 @@ func TestLoadConfig(t *testing.T) {
"limited.datapoint.count": {
Description: "Limited data point count.",
Conditions: []string{`IsMatch(resource.attributes["host.name"], "pod-d") == true`},
Attributes: []AttributeConfig{
{
Key: "env",
},
{
Key: "component",
DefaultValue: "other",
},
},
},
},
Logs: map[string]MetricInfo{
Expand All @@ -248,6 +319,15 @@ func TestLoadConfig(t *testing.T) {
"limited.logrecord.count": {
Description: "Limited log record count.",
Conditions: []string{`IsMatch(resource.attributes["host.name"], "pod-l") == true`},
Attributes: []AttributeConfig{
{
Key: "env",
},
{
Key: "component",
DefaultValue: "other",
},
},
},
},
},
Expand Down
Loading