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

feat: Host Metrics Generator #1556

Merged
merged 4 commits into from
Mar 19, 2024
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
113 changes: 105 additions & 8 deletions receiver/telemetrygeneratorreceiver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,22 @@ This receiver is used to generate synthetic telemetry for testing and configurat

## Minimum Agent Versions
- Introduced: [v1.46.0](https://github.com/observIQ/bindplane-agent/releases/tag/v1.46.0)
- Updated to include host_metrics: [v1.47.0](https://github.com/observIQ/bindplane-agent/releases/tag/v1.47.0)

## Supported Pipelines
- Logs
- Metrics
- Traces

## How It Works
1. The user configures this receiver in a pipeline.
2. The user configures a supported component to route telemetry from this receiver.

## Configuration
## Configuration for all generators
| Field | Type | Default | Required | Description |
|----------------------|-----------|-----------|----------|-------------|
| payloads_per_second | int | `1` | `false` | The number of payloads this receiver will generate per second.|
| generators | list | | `true` | A list of generators to use.|
### Generator Configuration
### Common Generator Configuration
| Field | Type | Default | Required | Description |
|----------------------|-----------|------------------|----------|--------------|
| type | string | | `true` | The type of generator to use. Currently only `logs` is supported. |
| type | string | | `true` | The type of generator to use. Currently `logs`, `otlp`, `metrics`, and `host_metrics` are supported. |
| resource_attributes | map | | `false` | A map of resource attributes to be included in the generated telemetry. Values can be `any`. |
| attributes | map | | `false` | A map of attributes to be included in the generated telemetry. Values can be `any`. |
| additional_config | map | | `false` | A map of additional configuration options to be included in the generated telemetry. Values can be `any`.|
Expand All @@ -32,7 +29,7 @@ This receiver is used to generate synthetic telemetry for testing and configurat
| body | string | | `false` | The body of the log, set in additional_config |
| severity | int | | `false` | The severity of the log message, set in additional_config |

### Example Configuration
#### Example Configuration
```yaml
telemetrygeneratorreceiver:
payloads_per_second: 1
Expand All @@ -58,3 +55,103 @@ telemetrygeneratorreceiver:
body: this is another body
severity: 10
```

### OTLP Replay Generator

The OTLP Replay Generator replays JSON-formatted telemetry from the variable `otlp_json`. It adjusts the timestamps of the telemetry relative the current time, with the most recent record moved to the current time, and the previous records the same relative duration in the past. The `otlp_json` variable should be valid OTLP, such as the JSON created by `plog.JSONMarshaler`,`ptrace.JSONMarshaler`, or `pmetric.JSONMarshaler`. The `otlp_json` variable is set in the `additional_config` section of the generator configuration.

#### additional_config:

| Field | Type | Default | Required | Description |
|----------------------|-----------|------------------|----------|--------------|
| telemetry_type | string | | `true` | The type of telemetry to replay: `logs`, `metrics`, or `traces`. |
| otlp_json | string | | `true` | A string of JSON encoded OTLP telemetry|

#### Example Configuration
```yaml
telemetrygeneratorreceiver:
payloads_per_second: 1
generators:
- type: otlp
additional_config:
telemetry_type: "metrics",
otlp_json: `{"resourceMetrics":[{"resource":{},"scopeMetrics":[{"scope":{},"metrics":[{"exponentialHistogram":{"dataPoints":[{"attributes":[{"key":"prod-machine","value":{"stringValue":"prod-1"}}],"count":"4","positive":{},"negative":{},"min":0,"max":100}]}}]}]}]}`,
```

### Metrics Generator

The metrics generator creates synthetic metrics. The generator can be configured to create metrics with arbitrary names, values, and attributes. The generator can be configured to create metrics with a random value between a minimum and maximum value, or a constant value by setting `value_max = value_min`. For `Sum` metrics with unit `s` and `Gauge` metrics, the generator will create a random `float` value. For all other `Sum` metrics, the generator will create a random `int` value.

#### additional_config:

| Field | Type | Default | Required | Description |
|----------------------|-----------|------------------|----------|--------------|
| telemetry_type | string | | `true` | The type of telemetry to replay: `logs`, `metrics`, or `traces`. |
| metrics | array | | `true` | A list of metrics to generate|

#### metrics:


| Field | Type | Default | Required | Description |
|----------------------|-----------|------------------|----------|--------------|
| name | string | | `true` | The metric name |
| value_min | int | | `true` | The metric's minimum value|
| value_max | int | | `true` | The metric's maximum value|
| type | string | | `true` | The metric type: `Gauge`, or `Sum`|
| unit | string | | `true` | The metric unit, either `By`, `by`, `1`, `s`, `{thread}`, `{errors}`, `{packets}`, `{entries}`, `{connections}`, `{faults}`, `{operations}`, or `{processes}`|
| attributes | map | | `false` | A map of attributes to be included in the generated telemetry record. Values can be `any`.|

#### Example Configuration
```yaml
telemetrygeneratorreceiver:
payloads_per_second: 1
generators:
- type: metrics
resource_attributes:
host.name: 2ed77de7e4c1
os.type: linux
additional_config:
metrics:
# memory metrics
- name: system.memory.usage
value_min: 100000
value_max: 1000000000
type: Sum
unit: By
attributes:
state: cached
# load metrics
- name: system.cpu.load_average.1m
value_min: 0
value_max: 1
type: Gauge
unit: "{thread}"
# file system metrics
- name: system.filesystem.usage
value_min: 0
value_max: 15616700416
type: Sum
unit: By
attributes:
device: "/dev/vda1"
mode: rw
mountpoint: "/etc/hosts"
state: reserved
type: ext4
```


### Host Metrics Generator

The host metrics generator creates synthetic host metrics, from a list of pre-defined metrics. The metrics resource attributes can be set in the `resource_attributes` section of the generator configuration.

#### Example Configuration
```yaml
telemetrygeneratorreceiver:
payloads_per_second: 1
generators:
- type: host_metrics
resource_attributes:
host.name: 2ed77de7e4c1
os.type: linux
```
72 changes: 71 additions & 1 deletion receiver/telemetrygeneratorreceiver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"errors"
"fmt"

"github.com/mitchellh/mapstructure"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/plog"
Expand Down Expand Up @@ -69,6 +70,8 @@ func (g *GeneratorConfig) Validate() error {
switch g.Type {
case generatorTypeLogs:
return validateLogGeneratorConfig(g)
case generatorTypeMetrics:
return validateMetricsGeneratorConfig(g)
case generatorTypeHostMetrics:
return validateHostMetricsGeneratorConfig(g)
case generatorTypeWindowsEvents:
Expand Down Expand Up @@ -180,7 +183,74 @@ func validateOTLPGenerator(cfg *GeneratorConfig) error {
return nil
}

func validateHostMetricsGeneratorConfig(_ *GeneratorConfig) error {
func validateMetricsGeneratorConfig(g *GeneratorConfig) error {
err := pcommon.NewMap().FromRaw(g.Attributes)
if err != nil {
return fmt.Errorf("error in attributes config: %w", err)
}

err = pcommon.NewMap().FromRaw(g.ResourceAttributes)
if err != nil {
return fmt.Errorf("error in resource_attributes config: %w", err)
}

// validate individual metrics
metrics, ok := g.AdditionalConfig["metrics"]
if !ok {
return errors.New("metrics must be set")
}

// check that the metricsArray is a valid array of maps[string]any
// Because of the way the config is unmarshaled, we have to use the `[]any` type
// and then cast to the correct type
metricsArray, ok := metrics.([]any)
if !ok {
return errors.New("metrics must be an array of maps")
}
for _, m := range metricsArray {
var metric metric
mapMetric, ok := m.(map[string]any)
if !ok {
return errors.New("each metric must be a map")
}

err := mapstructure.Decode(mapMetric, &metric)
if err != nil {
return fmt.Errorf("error decoding metric: %w", err)
}
if metric.Name == "" {
return errors.New("each metric must have a name")
}

// validate the metric type
switch metric.Type {
case "Gauge", "Sum":
case "":
return fmt.Errorf("metric %s missing type", metric.Name)
default:
return fmt.Errorf("metric %s has invalid metric type: %s", metric.Name, metric.Type)
}

// validate the unit
if metric.Unit == "" {
return fmt.Errorf("metric %s missing unit", metric.Name)
}

// attributes are optional
err = pcommon.NewMap().FromRaw(metric.Attributes)
if err != nil {
return fmt.Errorf("error in attributes config for metric %s: %w", metric.Name, err)
}

}
return nil
}

func validateHostMetricsGeneratorConfig(g *GeneratorConfig) error {
err := pcommon.NewMap().FromRaw(g.ResourceAttributes)
if err != nil {
return fmt.Errorf("error in resource_attributes config: %w", err)
}
return nil
}

Expand Down
Loading
Loading