Skip to content
Open
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
6 changes: 3 additions & 3 deletions embed/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type Host struct {
// runtime-owned copy that no caller can race with; callers must still
// treat their own reference as frozen once they hand the Host off.
// See cloneResource in this package.
Resource map[string]string
Resource map[string]any

// Environment is the simulated datagen.Environment that generators draw
// their host identities (host.name / os.type) from (PIPE-1036). Nil means
Expand All @@ -56,11 +56,11 @@ type Host struct {
// worker goroutines reading the per-session resource attributes race
// neither with each other nor with a caller that retains a reference
// and later mutates its own copy.
func cloneResource(m map[string]string) map[string]string {
func cloneResource(m map[string]any) map[string]any {
if m == nil {
return nil
}
out := make(map[string]string, len(m))
out := make(map[string]any, len(m))
for k, v := range m {
out[k] = v
}
Expand Down
2 changes: 1 addition & 1 deletion embed/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type runner struct {

mu sync.Mutex
rt *runtime.Runtime
resource map[string]string // cloned from host.Resource at Start
resource map[string]any // cloned from host.Resource at Start
started bool
}

Expand Down
6 changes: 3 additions & 3 deletions embed/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type LogRecordMetadata struct {
Timestamp time.Time
Severity string
Attributes map[string]any
Resource map[string]string
Resource map[string]any
}

// MetricType represents the type of a metric data point.
Expand Down Expand Up @@ -101,7 +101,7 @@ type MetricPoint struct {
type MetricPointMetadata struct {
Timestamp time.Time
Attributes map[string]string
Resource map[string]string
Resource map[string]any
}

// SpanKind represents the kind of a trace span.
Expand Down Expand Up @@ -156,5 +156,5 @@ type Span struct {
// arrays).
type SpanMetadata struct {
Attributes map[string]any
Resource map[string]string
Resource map[string]any
}
2 changes: 1 addition & 1 deletion embed/record_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestLogRecordMetadataCarriesResourceAndAttributes(t *testing.T) {
Message: "GET / 200",
Metadata: embed.LogRecordMetadata{
Severity: "INFO",
Resource: map[string]string{
Resource: map[string]any{
"host.name": "web-01",
"telemetry.source": "apache",
"apache.format": "common",
Expand Down
4 changes: 2 additions & 2 deletions generator/fix/fix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
type captureConsumer struct {
mu sync.Mutex
got [][]byte
resources []map[string]string
resources []map[string]any
}

func (c *captureConsumer) ConsumeLogs(_ context.Context, records []embed.LogRecord) error {
Expand All @@ -42,7 +42,7 @@ func (c *captureConsumer) Snapshot() [][]byte {
return out
}

func (c *captureConsumer) ResourceAt(i int) map[string]string {
func (c *captureConsumer) ResourceAt(i int) map[string]any {
c.mu.Lock()
defer c.mu.Unlock()
if i < 0 || i >= len(c.resources) {
Expand Down
2 changes: 1 addition & 1 deletion generator/hostmetrics/cpu.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type cpuScraper struct{}

func (s *cpuScraper) Name() string { return "cpu" }

func (s *cpuScraper) Scrape(r *rand.Rand, hostname string, resource map[string]string) []output.MetricRecord {
func (s *cpuScraper) Scrape(r *rand.Rand, hostname string, resource map[string]any) []output.MetricRecord {
now := time.Now()
numCPUs := r.Intn(16) + 1 // #nosec G404

Expand Down
2 changes: 1 addition & 1 deletion generator/hostmetrics/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type diskScraper struct{}

func (s *diskScraper) Name() string { return "disk" }

func (s *diskScraper) Scrape(r *rand.Rand, _ string, resource map[string]string) []output.MetricRecord {
func (s *diskScraper) Scrape(r *rand.Rand, _ string, resource map[string]any) []output.MetricRecord {
now := time.Now()
devices := []string{"sda", "sdb", "nvme0n1"}
device := devices[r.Intn(len(devices))] // #nosec G404
Expand Down
2 changes: 1 addition & 1 deletion generator/hostmetrics/filesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type filesystemScraper struct{}

func (s *filesystemScraper) Name() string { return "filesystem" }

func (s *filesystemScraper) Scrape(r *rand.Rand, _ string, resource map[string]string) []output.MetricRecord {
func (s *filesystemScraper) Scrape(r *rand.Rand, _ string, resource map[string]any) []output.MetricRecord {
now := time.Now()

type mount struct {
Expand Down
2 changes: 1 addition & 1 deletion generator/hostmetrics/hostmetrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ func TestSeedDeterminism(t *testing.T) {
// Test individual scrapers produce non-empty records.
func TestScrapers(t *testing.T) {
r := rand.New(rand.NewSource(42)) // #nosec G404
resource := map[string]string{"host.name": "test", "os.type": "linux"}
resource := map[string]any{"host.name": "test", "os.type": "linux"}

scrapers := allScrapers()
for _, s := range scrapers {
Expand Down
2 changes: 1 addition & 1 deletion generator/hostmetrics/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type loadScraper struct{}

func (s *loadScraper) Name() string { return "load" }

func (s *loadScraper) Scrape(r *rand.Rand, _ string, resource map[string]string) []output.MetricRecord {
func (s *loadScraper) Scrape(r *rand.Rand, _ string, resource map[string]any) []output.MetricRecord {
now := time.Now()

// Load averages: 1m > 5m > 15m (typical pattern)
Expand Down
2 changes: 1 addition & 1 deletion generator/hostmetrics/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type memoryScraper struct{}

func (s *memoryScraper) Name() string { return "memory" }

func (s *memoryScraper) Scrape(r *rand.Rand, _ string, resource map[string]string) []output.MetricRecord {
func (s *memoryScraper) Scrape(r *rand.Rand, _ string, resource map[string]any) []output.MetricRecord {
now := time.Now()
totalGB := []int64{4, 8, 16, 32, 64}
total := totalGB[r.Intn(len(totalGB))] * 1024 * 1024 * 1024 // #nosec G404
Expand Down
2 changes: 1 addition & 1 deletion generator/hostmetrics/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type networkScraper struct{}

func (s *networkScraper) Name() string { return "network" }

func (s *networkScraper) Scrape(r *rand.Rand, _ string, resource map[string]string) []output.MetricRecord {
func (s *networkScraper) Scrape(r *rand.Rand, _ string, resource map[string]any) []output.MetricRecord {
now := time.Now()
ifaces := []string{"eth0", "eth1", "lo", "ens192", "bond0"}
iface := ifaces[r.Intn(len(ifaces))] // #nosec G404
Expand Down
2 changes: 1 addition & 1 deletion generator/hostmetrics/paging.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type pagingScraper struct{}

func (s *pagingScraper) Name() string { return "paging" }

func (s *pagingScraper) Scrape(r *rand.Rand, _ string, resource map[string]string) []output.MetricRecord {
func (s *pagingScraper) Scrape(r *rand.Rand, _ string, resource map[string]any) []output.MetricRecord {
now := time.Now()

swapTotal := int64(r.Intn(8)+1) * 1024 * 1024 * 1024 // 1-8 GB #nosec G404
Expand Down
2 changes: 1 addition & 1 deletion generator/hostmetrics/processes.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type processesScraper struct{}

func (s *processesScraper) Name() string { return "processes" }

func (s *processesScraper) Scrape(r *rand.Rand, _ string, resource map[string]string) []output.MetricRecord {
func (s *processesScraper) Scrape(r *rand.Rand, _ string, resource map[string]any) []output.MetricRecord {
now := time.Now()

running := int64(r.Intn(20)) + 1 // #nosec G404
Expand Down
2 changes: 1 addition & 1 deletion generator/hostmetrics/scraper.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ type Scraper interface {
// Name returns the scraper name.
Name() string
// Scrape generates metric records for the current scrape cycle.
Scrape(r *rand.Rand, hostname string, resource map[string]string) []output.MetricRecord
Scrape(r *rand.Rand, hostname string, resource map[string]any) []output.MetricRecord
}
16 changes: 8 additions & 8 deletions generator/resource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,16 @@ func Hostname() string {
//
// resource.Default("apache", "apache.format", "common")
// // → {"host.name": "<host>", "telemetry.source": "apache", "apache.format": "common"}
func Default(source string, extras ...string) map[string]string {
func Default(source string, extras ...string) map[string]any {
return WithHost(Hostname(), source, extras...)
}

// WithHost returns a Resource map like Default but with an explicit host.name,
// for generators whose hostname comes from a resolved datagen SystemIdentity
// (PIPE-1036) rather than the process's os.Hostname(). extras follow the same
// key/value convention as Default.
func WithHost(hostname, source string, extras ...string) map[string]string {
r := map[string]string{
func WithHost(hostname, source string, extras ...string) map[string]any {
r := map[string]any{
"host.name": hostname,
"telemetry.source": source,
}
Expand All @@ -78,14 +78,14 @@ func WithHost(hostname, source string, extras ...string) map[string]string {
// The model is: Static + Dynamic (per record) = Record. Static carries the
// fields that never change for the worker; Record merges in the few that do.
type StaticResources struct {
attrs map[string]string
attrs map[string]any
}

// NewStaticResources builds a StaticResources from a base attribute set. The
// map is copied, so a caller that retains and later mutates attrs does not
// affect the constructed value.
func NewStaticResources(attrs map[string]string) *StaticResources {
cp := make(map[string]string, len(attrs))
func NewStaticResources(attrs map[string]any) *StaticResources {
cp := make(map[string]any, len(attrs))
for k, v := range attrs {
cp[k] = v
}
Expand All @@ -102,11 +102,11 @@ func NewStaticResources(attrs map[string]string) *StaticResources {
// corrupts every other record and races concurrent workers. When dynamic pairs
// are supplied, Record returns a fresh merged map that is safe to mutate and
// leaves the static set untouched.
func (s *StaticResources) Record(dynamicKV ...string) map[string]string {
func (s *StaticResources) Record(dynamicKV ...string) map[string]any {
if len(dynamicKV) < 2 {
return s.attrs
}
out := make(map[string]string, len(s.attrs)+len(dynamicKV)/2)
out := make(map[string]any, len(s.attrs)+len(dynamicKV)/2)
for k, v := range s.attrs {
out[k] = v
}
Expand Down
10 changes: 5 additions & 5 deletions generator/resource/resource_static_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import (
"testing"
)

func mapPtr(m map[string]string) uintptr { return reflect.ValueOf(m).Pointer() }
func mapPtr(m map[string]any) uintptr { return reflect.ValueOf(m).Pointer() }

func TestStaticResourcesConstructorCopies(t *testing.T) {
base := map[string]string{"host.name": "thor-web-01", "telemetry.source": "apache"}
base := map[string]any{"host.name": "thor-web-01", "telemetry.source": "apache"}
s := NewStaticResources(base)

// Mutating the input after construction must not leak into the static set.
Expand All @@ -25,7 +25,7 @@ func TestStaticResourcesConstructorCopies(t *testing.T) {
}

func TestStaticResourcesRecordNoDynamicIsSharedAndReadOnly(t *testing.T) {
s := NewStaticResources(map[string]string{"host.name": "thor-web-01", "telemetry.source": "apache"})
s := NewStaticResources(map[string]any{"host.name": "thor-web-01", "telemetry.source": "apache"})

a := s.Record()
b := s.Record()
Expand All @@ -40,7 +40,7 @@ func TestStaticResourcesRecordNoDynamicIsSharedAndReadOnly(t *testing.T) {
}

func TestStaticResourcesRecordWithDynamicMergesWithoutMutatingStatic(t *testing.T) {
s := NewStaticResources(map[string]string{"host.name": "thor-web-01", "telemetry.source": "wel"})
s := NewStaticResources(map[string]any{"host.name": "thor-web-01", "telemetry.source": "wel"})

rec := s.Record("wel.channel", "Security", "wel.role", "dc")

Expand All @@ -59,7 +59,7 @@ func TestStaticResourcesRecordWithDynamicMergesWithoutMutatingStatic(t *testing.
}

func TestStaticResourcesRecordOddArgsIgnoresTrailing(t *testing.T) {
s := NewStaticResources(map[string]string{"telemetry.source": "json"})
s := NewStaticResources(map[string]any{"telemetry.source": "json"})

// A single unpaired arg is treated as "no complete dynamic pair": shared static.
if mapPtr(s.Record("dangling")) != mapPtr(s.Record()) {
Expand Down
6 changes: 3 additions & 3 deletions generator/traces/traces.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ func (g *Generator) startTrace(r *mathrand.Rand) {
}
}

func (g *Generator) buildChildSpan(r *mathrand.Rand, kind childKind, traceID, parentID string, earliestStart time.Time, res map[string]string) embed.Span {
func (g *Generator) buildChildSpan(r *mathrand.Rand, kind childKind, traceID, parentID string, earliestStart time.Time, res map[string]any) embed.Span {
spanID := generateSpanID()
startOffset := time.Duration(r.Intn(50)) * time.Millisecond // #nosec G404
start := earliestStart.Add(startOffset)
Expand Down Expand Up @@ -447,8 +447,8 @@ func (g *Generator) emitSpan(sp embed.Span) {

// cloneResource returns a defensive copy so per-span mutations (e.g. a
// future host-base merge in the runner) can't bleed across spans.
func cloneResource(src map[string]string) map[string]string {
out := make(map[string]string, len(src))
func cloneResource(src map[string]any) map[string]any {
out := make(map[string]any, len(src))
for k, v := range src {
out[k] = v
}
Expand Down
16 changes: 9 additions & 7 deletions output/otlp_grpc/metrics_traces.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,13 @@ func toAnyValueSimple(v any) *commonpb.AnyValue {
return &commonpb.AnyValue{Value: &commonpb.AnyValue_DoubleValue{DoubleValue: x}}
case bool:
return &commonpb.AnyValue{Value: &commonpb.AnyValue_BoolValue{BoolValue: x}}
case []string:
// Homogeneous string array (e.g. host.ip / host.mac) → OTLP ArrayValue.
values := make([]*commonpb.AnyValue, 0, len(x))
for _, s := range x {
values = append(values, &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: s}})
}
return &commonpb.AnyValue{Value: &commonpb.AnyValue_ArrayValue{ArrayValue: &commonpb.ArrayValue{Values: values}}}
default:
return &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: fmt.Sprintf("%v", x)}}
}
Expand Down Expand Up @@ -224,18 +231,13 @@ func hexByte(c byte) byte {
}

// buildMetricRequest builds an OTLP ExportMetricsServiceRequest from prepared metrics.
func buildMetricRequest(metrics []*metricspb.Metric, resource map[string]string) *metricspb.ResourceMetrics {
func buildMetricRequest(metrics []*metricspb.Metric, resource map[string]any) *metricspb.ResourceMetrics {
resourceAttrs := make([]*commonpb.KeyValue, 0, len(resource)+1)
resourceAttrs = append(resourceAttrs, &commonpb.KeyValue{
Key: "service.name",
Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: "blitz"}},
})
for k, v := range resource {
resourceAttrs = append(resourceAttrs, &commonpb.KeyValue{
Key: k,
Value: &commonpb.AnyValue{Value: &commonpb.AnyValue_StringValue{StringValue: v}},
})
}
resourceAttrs = append(resourceAttrs, anyMapToKeyValues(resource)...)

return &metricspb.ResourceMetrics{
Resource: &resourcepb.Resource{
Expand Down
45 changes: 45 additions & 0 deletions output/otlp_grpc/resource_array_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package otlpgrpc

import (
"testing"

commonpb "go.opentelemetry.io/proto/otlp/common/v1"
)

// A []string resource attribute (e.g. host.ip / host.mac) must serialize to an
// OTLP ArrayValue of StringValues, not a stringified slice (PIPE-1253).
func TestBuildMetricRequest_ArrayResourceAttribute(t *testing.T) {
rm := buildMetricRequest(nil, map[string]any{
"host.ip": []string{"10.0.0.1", "10.0.0.2"},
})

var hostIP *commonpb.AnyValue
for _, kv := range rm.Resource.Attributes {
if kv.Key == "host.ip" {
hostIP = kv.Value
}
}
if hostIP == nil {
t.Fatal("host.ip resource attribute not found")
}

arr, ok := hostIP.Value.(*commonpb.AnyValue_ArrayValue)
if !ok {
t.Fatalf("host.ip should serialize as an ArrayValue, got %T", hostIP.Value)
}
if len(arr.ArrayValue.Values) != 2 {
t.Fatalf("host.ip array should have 2 elements, got %d", len(arr.ArrayValue.Values))
}

got := make([]string, 0, 2)
for _, v := range arr.ArrayValue.Values {
sv, ok := v.Value.(*commonpb.AnyValue_StringValue)
if !ok {
t.Fatalf("array element should be a StringValue, got %T", v.Value)
}
got = append(got, sv.StringValue)
}
if got[0] != "10.0.0.1" || got[1] != "10.0.0.2" {
t.Errorf("array elements = %v, want [10.0.0.1 10.0.0.2]", got)
}
}
Loading