Skip to content

Commit f1dd213

Browse files
authored
Add Prometheus and OTLP histogram generators (#375)
1 parent c7dcf1f commit f1dd213

File tree

20 files changed

+2835
-0
lines changed

20 files changed

+2835
-0
lines changed

.checkapi.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
ignored_paths:
2+
- cmd/otlphistgen
3+
- cmd/promgen
24
- cmd/telemetrygen
35
- cmd/opampsupervisor
46
- exporter/datadogexporter/integrationtest

cmd/otlphistgen/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include ../../Makefile.Common

cmd/otlphistgen/README.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# OTEL Histograms Metrics Generator
2+
3+
A command-line tool that generates and exports OpenTelemetry histogram metrics using predefined test cases. This generator is designed for testing histogram processing in OpenTelemetry collectors and exporters.
4+
5+
## Overview
6+
7+
This tool continuously generates histogram metrics with various data patterns and exports them via OTLP HTTP. It includes both valid and invalid histogram test cases to verify proper handling of edge cases and error conditions.
8+
9+
## Features
10+
11+
- **Continuous Metric Generation**: Exports metrics every 10 seconds
12+
- **Comprehensive Test Cases**: 25+ valid histogram scenarios covering various data patterns
13+
- **Invalid Test Cases**: 12 invalid histogram scenarios for error handling validation
14+
- **OTLP HTTP Export**: Configurable endpoint with insecure connections supported
15+
- **Realistic Data**: Test cases include real-world scenarios like payment services, API gateways, and batch processors
16+
17+
## Usage
18+
19+
### Basic Usage
20+
21+
```bash
22+
go run main.go
23+
```
24+
25+
The generator will start exporting metrics to `localhost:4318` (default OTLP HTTP endpoint) every 10 seconds.
26+
27+
### Configuration
28+
29+
The tool is currently configured with hardcoded settings but can be modified in `main.go`:
30+
31+
```go
32+
exporter, err := createExporter(&Config{
33+
UseHTTP: true,
34+
Insecure: true,
35+
})
36+
```
37+
38+
## Test Cases
39+
40+
### Valid Histogram Scenarios
41+
42+
The generator includes diverse histogram patterns:
43+
44+
- **Basic Histogram**: Standard distribution with 101 samples
45+
- **Tail Heavy**: Distribution with heavy concentration in upper buckets
46+
- **Single/Two Buckets**: Minimal bucket configurations
47+
- **Large Numbers**: High-value measurements (up to 1B)
48+
- **Small Numbers**: Micro-precision measurements
49+
- **Many Buckets**: Up to 325 buckets for testing scalability
50+
- **Negative Values**: Histograms with negative boundaries
51+
- **Missing Min/Max**: Various combinations of undefined extrema
52+
- **Unbounded**: Histograms without explicit boundaries
53+
54+
### Invalid Test Cases
55+
56+
Error scenarios for validation:
57+
58+
- Non-ascending boundaries
59+
- Count/bucket mismatches
60+
- Invalid min/max relationships
61+
- NaN/Inf values
62+
- Inconsistent sum calculations
63+
64+
## Metric Structure
65+
66+
Each generated metric includes:
67+
68+
```go
69+
type HistogramInput struct {
70+
Count uint64 // Total sample count
71+
Sum float64 // Sum of all samples
72+
Min *float64 // Minimum value (optional)
73+
Max *float64 // Maximum value (optional)
74+
Boundaries []float64 // Bucket boundaries
75+
Counts []uint64 // Counts per bucket
76+
Attributes map[string]string // Metric attributes
77+
}
78+
```
79+
80+
## Export Format
81+
82+
Metrics are exported as OTLP histogram data points with:
83+
- Delta temporality
84+
- Configurable attributes (service.name, etc.)
85+
- Proper bucket counts and boundaries
86+
- Min/max extrema when available
87+
88+
## Development
89+
90+
### Adding New Test Cases
91+
92+
1. Add new cases to `TestCases()` or `InvalidTestCases()` in the histograms package
93+
2. Define the input histogram structure
94+
3. Specify expected metrics for validation
95+
96+
### Modifying Export Configuration
97+
98+
Update the `Config` struct in `main.go` to customize:
99+
- Endpoint URLs
100+
- Security settings
101+
- Headers
102+
- Export intervals
103+
104+
## Use Cases
105+
106+
- **Testing Histogram Processing**: Validate collector histogram handling
107+
- **Performance Testing**: Generate high-volume histogram data
108+
- **Edge Case Validation**: Test error handling with invalid histograms
109+
- **Integration Testing**: End-to-end OTLP histogram pipeline testing
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Histogram Generator
2+
3+
A Go package for generating realistic histogram data for OpenTelemetry metrics testing. This package provides statistical distribution functions and can optionally publish generated metrics to OTLP endpoints.
4+
5+
## Architecture
6+
7+
The package is organized into focused modules for maintainability and scalability:
8+
9+
```
10+
cmd/generator/
11+
├── generator.go # Package documentation and overview
12+
├── types.go # Core data structures and types
13+
├── histogram_generator.go # Main histogram generation logic
14+
├── distributions.go # Statistical distribution functions
15+
├── otlp_publisher.go # OTLP endpoint publishing functionality
16+
└── example_test.go # Usage examples
17+
```
18+
19+
### Key Components
20+
21+
- **HistogramGenerator**: Main generator that creates histogram data from statistical distributions
22+
- **OTLPPublisher**: Handles publishing metrics to OpenTelemetry Protocol endpoints using both telemetrygen and custom OTLP
23+
- **Distribution Functions**: Various statistical distributions (Normal, Exponential, Gamma, etc.)
24+
- **Types**: Shared data structures for histogram inputs, outputs, and configuration
25+
26+
### Publishing Approach
27+
28+
The package uses the official OpenTelemetry telemetrygen package for all metric publishing, ensuring:
29+
30+
- **Standard Compliance**: Full compatibility with OTLP endpoints
31+
- **Reliability**: Uses the same code as the official telemetrygen tool
32+
- **Simplicity**: Clean API with `metrics.Start(cfg)` under the hood
33+
- **Flexibility**: Supports Gauge, Sum, and Histogram metric types
34+
35+
The histogram generator creates realistic data distributions, while telemetrygen handles the actual OTLP publishing.
36+
37+
## Usage
38+
39+
### Basic Generation
40+
41+
```go
42+
generator := NewHistogramGenerator(GenerationOptions{
43+
Seed: 12345, // For reproducible results
44+
})
45+
46+
input := HistogramInput{
47+
Count: 1000,
48+
Min: ptr(10.0),
49+
Max: ptr(200.0),
50+
Boundaries: []float64{25, 50, 75, 100, 150},
51+
Attributes: map[string]string{"service.name": "test-service"},
52+
}
53+
54+
result, err := generator.GenerateHistogram(input, func(rnd *rand.Rand, t time.Time) float64 {
55+
return NormalRandom(rnd, 75, 25) // mean=75, stddev=25
56+
})
57+
```
58+
59+
### Generation with Publishing
60+
61+
```go
62+
generator := NewHistogramGenerator(GenerationOptions{
63+
Seed: time.Now().UnixNano(),
64+
Endpoint: "localhost:4318", // OTLP HTTP endpoint
65+
})
66+
67+
result, err := generator.GenerateAndPublishHistograms(input, valueFunc)
68+
```
69+
70+
### Direct Publishing with Telemetrygen
71+
72+
You can also use telemetrygen directly for different metric types:
73+
74+
```go
75+
publisher := NewOTLPPublisher("localhost:4318")
76+
77+
// Send different types of metrics using telemetrygen
78+
err := publisher.SendSumMetric("requests_total", 100)
79+
err = publisher.SendGaugeMetric("cpu_usage", 75.5)
80+
err = publisher.SendHistogramMetricSimple("response_time")
81+
```
82+
83+
This approach uses the official OpenTelemetry telemetrygen package under the hood, ensuring compatibility with standard OTLP endpoints.
84+
85+
## Available Distributions
86+
87+
- **NormalRandom**: Normal (Gaussian) distribution
88+
- **ExponentialRandom**: Exponential distribution
89+
- **GammaRandom**: Gamma distribution
90+
- **LogNormalRandom**: Log-normal distribution
91+
- **WeibullRandom**: Weibull distribution
92+
- **BetaRandom**: Beta distribution
93+
94+
### Time-based Functions
95+
96+
- **SinusoidalValue**: Sinusoidal patterns with noise
97+
- **SpikyValue**: Baseline with occasional spikes
98+
- **TrendingValue**: Linear trend with noise
99+
100+
## Design Principles
101+
102+
### Single Responsibility
103+
Each file has a focused purpose:
104+
- `types.go`: Data structures only
105+
- `distributions.go`: Statistical functions only
106+
- `histogram_generator.go`: Core generation logic only
107+
- `otlp_publisher.go`: Publishing logic only
108+
109+
### Dependency Injection
110+
The generator accepts value functions, allowing for flexible distribution selection and custom patterns.
111+
112+
### Testability
113+
All components are designed for easy unit testing with deterministic seeds and dependency injection.
114+
115+
### Extensibility
116+
New distributions can be added to `distributions.go` without affecting other components.
117+
118+
## Features
119+
120+
-**Statistical Distributions**: Multiple distribution functions for realistic data
121+
-**OTLP Publishing**: Direct integration with OpenTelemetry Protocol endpoints
122+
-**Flexible Generation**: Custom value functions and deterministic seeds
123+
-**Multiple Metric Types**: Support for Gauge, Sum, and Histogram metrics
124+
125+
## Future Enhancements
126+
127+
1. **Additional Publishers**: Support for Prometheus, StatsD, etc.
128+
2. **More Distributions**: Poisson, Binomial, etc.
129+
3. **Validation**: Input validation for histogram consistency
130+
4. **Batch Generation**: Generate multiple histograms efficiently
131+
5. **Configuration Files**: YAML/JSON configuration support
132+
133+
## Testing
134+
135+
The package includes comprehensive examples in `example_test.go` and integrates with the test cases in `share/testdata/histograms/`.
136+
137+
Run tests:
138+
```bash
139+
go test ./cmd/generator/...
140+
```
141+
142+
## Integration
143+
144+
This generator is used by:
145+
- `share/testdata/histograms/histograms.go`: Test case generation
146+
- Various metric exporters for testing realistic data patterns
147+
- Performance testing tools for load generation
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package generator
5+
6+
import (
7+
"math"
8+
rand "math/rand/v2"
9+
"time"
10+
)
11+
12+
// Distribution functions for generating statistical data
13+
14+
func ExponentialRandom(rnd *rand.Rand, rate float64) float64 {
15+
return -math.Log(1.0-rnd.Float64()) / rate
16+
}
17+
18+
func NormalRandom(rnd *rand.Rand, mean, stddev float64) float64 {
19+
return rnd.NormFloat64()*stddev + mean
20+
}
21+
22+
func LogNormalRandom(rnd *rand.Rand, mu, sigma float64) float64 {
23+
return math.Exp(NormalRandom(rnd, mu, sigma))
24+
}
25+
26+
func WeibullRandom(rnd *rand.Rand, shape, scale float64) float64 {
27+
return scale * math.Pow(-math.Log(1.0-rnd.Float64()), 1.0/shape)
28+
}
29+
30+
func BetaRandom(rnd *rand.Rand, alpha, beta float64) float64 {
31+
x := GammaRandom(rnd, alpha, 1.0)
32+
y := GammaRandom(rnd, beta, 1.0)
33+
return x / (x + y)
34+
}
35+
36+
func GammaRandom(rnd *rand.Rand, alpha, beta float64) float64 {
37+
if alpha < 1.0 {
38+
// Use Johnk's generator for alpha < 1
39+
for {
40+
u := rnd.Float64()
41+
v := rnd.Float64()
42+
x := math.Pow(u, 1.0/alpha)
43+
y := math.Pow(v, 1.0/(1.0-alpha))
44+
if x+y <= 1.0 {
45+
if x+y > 0 {
46+
return beta * x / (x + y) * (-math.Log(rnd.Float64()))
47+
}
48+
}
49+
}
50+
}
51+
52+
// Marsaglia and Tsang's method for alpha >= 1
53+
d := alpha - 1.0/3.0
54+
c := 1.0 / math.Sqrt(9.0*d)
55+
56+
for {
57+
x := rnd.NormFloat64()
58+
v := 1.0 + c*x
59+
if v <= 0 {
60+
continue
61+
}
62+
v = v * v * v
63+
u := rnd.Float64()
64+
if u < 1.0-0.0331*(x*x)*(x*x) {
65+
return beta * d * v
66+
}
67+
if math.Log(u) < 0.5*x*x+d*(1.0-v+math.Log(v)) {
68+
return beta * d * v
69+
}
70+
}
71+
}
72+
73+
// Time-based value functions
74+
75+
func SinusoidalValue(rnd *rand.Rand, timestamp time.Time, amplitude, period, phase, baseline float64) float64 {
76+
t := float64(timestamp.Unix())
77+
noise := rnd.NormFloat64() * amplitude * 0.1 // 10% noise
78+
return baseline + amplitude*math.Sin(2*math.Pi*t/period+phase) + noise
79+
}
80+
81+
func SpikyValue(rnd *rand.Rand, baseline, spikeHeight, spikeProb float64) float64 {
82+
if rnd.Float64() < spikeProb {
83+
return baseline + spikeHeight*rnd.Float64()
84+
}
85+
return baseline + rnd.NormFloat64()*baseline*0.1
86+
}
87+
88+
func TrendingValue(rnd *rand.Rand, timestamp time.Time, startValue, trendRate, noise float64) float64 {
89+
t := float64(timestamp.Unix())
90+
trend := startValue + trendRate*t
91+
return trend + rnd.NormFloat64()*noise
92+
}

0 commit comments

Comments
 (0)