فهرست منبع

KCM-4268: Add log level to heartbeat data (#3222)

Ishaan Mittal 10 ماه پیش
والد
کامیت
f3b9bb352d

+ 81 - 0
core/pkg/heartbeat/exporter/metadataprovider.go

@@ -0,0 +1,81 @@
+package exporter
+
+import (
+	"maps"
+
+	"github.com/opencost/opencost/core/pkg/clusters"
+	"github.com/opencost/opencost/core/pkg/log"
+)
+
+// HeartbeatMetadataProvider is an interface that provides metadata for heartbeat instances. It can be used to inject
+// custom metadata into a generic `Heartbeat` payload.
+type HeartbeatMetadataProvider interface {
+	// GetMetadata returns the metadata for new heartbeat instances.
+	GetMetadata() map[string]any
+}
+
+// ClusterInfoMetadataProvider is a `HeartbeatMetadataProvider` implementation that provides metadata about the cluster
+// leveraging a `ClusterInfoProvider` implementation.
+type ClusterInfoMetadataProvider struct {
+	clusterInfoProvider clusters.ClusterInfoProvider
+}
+
+// NewClusterInfoMetadataProvider creates a new `ClusterInfoMetadataProvider` instance. The `provider` parameter is used to
+// inject custom metadata, but can be set to `nil` if no metadata is needed.
+func NewClusterInfoMetadataProvider(provider clusters.ClusterInfoProvider) *ClusterInfoMetadataProvider {
+	return &ClusterInfoMetadataProvider{
+		clusterInfoProvider: provider,
+	}
+}
+
+// GetMetadata returns the metadata for new heartbeat instances. It uses the `ClusterInfoProvider` to get the cluster
+// information and injects it into the metadata map.
+func (c *ClusterInfoMetadataProvider) GetMetadata() map[string]any {
+	m := c.clusterInfoProvider.GetClusterInfo()
+	metadata := make(map[string]any, len(m))
+
+	for k, v := range m {
+		metadata[k] = v
+	}
+
+	return metadata
+}
+
+// LogLevelMetadataProvider is a `HeartbeatMetadataProvider` implementation that provides the log level.
+type LogLevelMetadataProvider struct {}
+
+// NewLogLevelMetadataProvider creates a new `LogLevelMetadataProvider` instance.
+func NewLogLevelMetadataProvider() *LogLevelMetadataProvider {
+	return &LogLevelMetadataProvider{}
+}
+
+// GetMetadata returns the metadata for new heartbeat instances. It uses the log level from the global logger.
+func (l *LogLevelMetadataProvider) GetMetadata() map[string]any {
+	return map[string]any{
+		"logLevel": log.GetLogLevel(),
+	}
+}
+
+// MultiMetadataProvider is a `HeartbeatMetadataProvider` implementation that provides metadata from multiple providers.
+type MultiMetadataProvider struct {
+	providers []HeartbeatMetadataProvider
+}
+
+// NewMultiMetadataProvider creates a new `MultiMetadataProvider` instance.
+func NewMultiMetadataProvider(providers ...HeartbeatMetadataProvider) *MultiMetadataProvider {
+	return &MultiMetadataProvider{
+		providers: providers,
+	}
+}
+
+// GetMetadata returns the metadata for new heartbeat instances. 
+// It uses the `MultiMetadataProvider` to get the metadata from multiple providers and injects it into the metadata map.
+func (m *MultiMetadataProvider) GetMetadata() map[string]any {
+	metadata := make(map[string]any)
+
+	for _, provider := range m.providers {
+		maps.Copy(metadata, provider.GetMetadata())
+	}
+
+	return metadata
+}

+ 109 - 0
core/pkg/heartbeat/exporter/metadataprovider_test.go

@@ -0,0 +1,109 @@
+package exporter
+
+import (
+	"testing"
+	"time"
+
+	"github.com/opencost/opencost/core/pkg/clusters"
+)
+
+type MockClusterInfoProvider struct{}
+
+var (
+	logLevel = "trace"
+	ClusterInfo = map[string]string{
+		clusters.ClusterInfoIdKey:       "test-cluster-id",
+		clusters.ClusterInfoNameKey:     "test-cluster-name",
+		clusters.ClusterInfoVersionKey:  "test-cluster-version",
+		clusters.ClusterInfoRegionKey:   "test-cluster-region",
+		clusters.ClusterInfoProviderKey: "test-cluster-provider",
+	}
+)
+
+func NewMockClusterInfoProvider() clusters.ClusterInfoProvider {
+	return new(MockClusterInfoProvider)
+}
+func (m *MockClusterInfoProvider) GetClusterInfo() map[string]string {
+	return ClusterInfo
+}
+
+func TestClusterInfoProvider(t *testing.T) {
+	t.Parallel()
+
+	provider := NewMockClusterInfoProvider()
+	clusterInfoMetaDataProvider := NewClusterInfoMetadataProvider(provider)
+
+	heartbeatSrc := NewHeartbeatSource("test-app", "v0.0.1", clusterInfoMetaDataProvider)
+
+	hb := heartbeatSrc.Make(time.Now().UTC().Truncate(time.Second))
+
+	md := hb.Metadata
+	if md == nil {
+		t.Errorf("Expected metadata to be non-nil, got nil")
+	}
+
+	for k, v := range ClusterInfo {
+		if md[k] != v {
+			t.Errorf("Expected metadata key %s to be %s, got %s", k, v, md[k])
+		}
+	}
+
+	if heartbeatSrc.Name() != "heartbeat-source" {
+		t.Errorf("Expected source name to be 'heartbeat-source', got '%s'", heartbeatSrc.Name())
+	}
+}
+
+func TestLogLevelMetadataProvider(t *testing.T) {
+	t.Parallel()
+
+	logLevelMetaDataProvider := NewLogLevelMetadataProvider()
+
+	heartbeatSrc := NewHeartbeatSource("test-app", "v0.0.1", logLevelMetaDataProvider)
+
+	hb := heartbeatSrc.Make(time.Now().UTC().Truncate(time.Second))
+
+	md := hb.Metadata
+	if md == nil {
+		t.Errorf("Expected metadata to be non-nil, got nil")
+	}
+
+	if md["logLevel"] != logLevel {
+		t.Errorf("Expected log level to be '%s', got '%s'", logLevel, md["logLevel"])
+	}
+
+	if heartbeatSrc.Name() != "heartbeat-source" {
+		t.Errorf("Expected source name to be 'heartbeat-source', got '%s'", heartbeatSrc.Name())
+	}
+}
+
+func TestMultiMetadataProvider(t *testing.T) {
+	t.Parallel()
+
+	provider := NewMockClusterInfoProvider()
+	clusterInfoMetaDataProvider := NewClusterInfoMetadataProvider(provider)
+	logLevelMetaDataProvider := NewLogLevelMetadataProvider()
+	multiMetaDataProvider := NewMultiMetadataProvider(clusterInfoMetaDataProvider, logLevelMetaDataProvider)
+
+	heartbeatSrc := NewHeartbeatSource("test-app", "v0.0.1", multiMetaDataProvider)
+
+	hb := heartbeatSrc.Make(time.Now().UTC().Truncate(time.Second))
+
+	md := hb.Metadata
+	if md == nil {
+		t.Errorf("Expected metadata to be non-nil, got nil")
+	}
+
+	for k, v := range ClusterInfo {
+		if md[k] != v {
+			t.Errorf("Expected metadata key %s to be %s, got %s", k, v, md[k])
+		}
+	}
+
+	if md["logLevel"] != logLevel {
+		t.Errorf("Expected log level to be '%s', got '%s'", logLevel, md["logLevel"])
+	}
+
+	if heartbeatSrc.Name() != "heartbeat-source" {
+		t.Errorf("Expected source name to be 'heartbeat-source', got '%s'", heartbeatSrc.Name())
+	}
+}

+ 0 - 35
core/pkg/heartbeat/exporter/source.go

@@ -4,44 +4,9 @@ import (
 	"time"
 
 	"github.com/google/uuid"
-	"github.com/opencost/opencost/core/pkg/clusters"
 	"github.com/opencost/opencost/core/pkg/heartbeat"
 )
 
-// HeartbeatMetadataProvider is an interface that provides metadata for heartbeat instances. It can be used to inject
-// custom metadata into a generic `Heartbeat` payload.
-type HeartbeatMetadataProvider interface {
-	// GetMetadata returns the metadata for new heartbeat instances.
-	GetMetadata() map[string]any
-}
-
-// ClusterInfoMetadataProvider is a `HeartbeatMetadataProvider` implementation that provides metadata about the cluster
-// leveraging a `ClusterInfoProvider` implementation.
-type ClusterInfoMetadataProvider struct {
-	clusterInfoProvider clusters.ClusterInfoProvider
-}
-
-// NewClusterInfoMetadataProvider creates a new `ClusterInfoMetadataProvider` instance. The `provider` parameter is used to
-// inject custom metadata, but can be set to `nil` if no metadata is needed.
-func NewClusterInfoMetadataProvider(provider clusters.ClusterInfoProvider) *ClusterInfoMetadataProvider {
-	return &ClusterInfoMetadataProvider{
-		clusterInfoProvider: provider,
-	}
-}
-
-// GetMetadata returns the metadata for new heartbeat instances. It uses the `ClusterInfoProvider` to get the cluster
-// information and injects it into the metadata map.
-func (c *ClusterInfoMetadataProvider) GetMetadata() map[string]any {
-	m := c.clusterInfoProvider.GetClusterInfo()
-	metadata := make(map[string]any, len(m))
-
-	for k, v := range m {
-		metadata[k] = v
-	}
-
-	return metadata
-}
-
 // HeartbeatSource is an `export.ExportSource` implementation that provides the basic data for a `Heartbeat` payload, and
 // leverages a `HeartbeatMetadataProvider` to inject custom metadata.
 type HeartbeatSource struct {

+ 0 - 59
core/pkg/heartbeat/exporter/source_test.go

@@ -1,59 +0,0 @@
-package exporter
-
-import (
-	"testing"
-	"time"
-
-	"github.com/opencost/opencost/core/pkg/clusters"
-)
-
-type MockClusterInfoProvider struct{}
-
-func NewMockClusterInfoProvider() clusters.ClusterInfoProvider {
-	return new(MockClusterInfoProvider)
-}
-func (m *MockClusterInfoProvider) GetClusterInfo() map[string]string {
-	return map[string]string{
-		clusters.ClusterInfoIdKey:       "test-cluster-id",
-		clusters.ClusterInfoNameKey:     "test-cluster-name",
-		clusters.ClusterInfoVersionKey:  "test-cluster-version",
-		clusters.ClusterInfoRegionKey:   "test-cluster-region",
-		clusters.ClusterInfoProviderKey: "test-cluster-provider",
-	}
-}
-
-func TestClusterInfoProvider(t *testing.T) {
-	t.Parallel()
-
-	provider := NewMockClusterInfoProvider()
-	clusterInfoMetaDataProvider := NewClusterInfoMetadataProvider(provider)
-
-	heartbeatSrc := NewHeartbeatSource("test-app", "v0.0.1", clusterInfoMetaDataProvider)
-
-	hb := heartbeatSrc.Make(time.Now().UTC().Truncate(time.Second))
-
-	md := hb.Metadata
-	if md == nil {
-		t.Errorf("Expected metadata to be non-nil, got nil")
-	}
-
-	if md[clusters.ClusterInfoIdKey] != "test-cluster-id" {
-		t.Errorf("Expected cluster ID to be 'test-cluster-id', got '%s'", md[clusters.ClusterInfoIdKey])
-	}
-	if md[clusters.ClusterInfoNameKey] != "test-cluster-name" {
-		t.Errorf("Expected cluster name to be 'test-cluster-name', got '%s'", md[clusters.ClusterInfoNameKey])
-	}
-	if md[clusters.ClusterInfoVersionKey] != "test-cluster-version" {
-		t.Errorf("Expected cluster version to be 'test-cluster-version', got '%s'", md[clusters.ClusterInfoVersionKey])
-	}
-	if md[clusters.ClusterInfoRegionKey] != "test-cluster-region" {
-		t.Errorf("Expected cluster region to be 'test-cluster-region', got '%s'", md[clusters.ClusterInfoRegionKey])
-	}
-	if md[clusters.ClusterInfoProviderKey] != "test-cluster-provider" {
-		t.Errorf("Expected cluster provider to be 'test-cluster-provider', got '%s'", md[clusters.ClusterInfoProviderKey])
-	}
-
-	if heartbeatSrc.Name() != "heartbeat-source" {
-		t.Errorf("Expected source name to be 'heartbeat-source', got '%s'", heartbeatSrc.Name())
-	}
-}