diff --git a/weed/stats/metrics.go b/weed/stats/metrics.go index 2723e253f..95491abf1 100644 --- a/weed/stats/metrics.go +++ b/weed/stats/metrics.go @@ -4,6 +4,7 @@ import ( "net" "net/http" "os" + "runtime" "strconv" "strings" "sync" @@ -16,6 +17,19 @@ import ( "github.com/seaweedfs/seaweedfs/weed/glog" ) +// SetVersionInfo sets the version information for the BuildInfo metric +// This is called by the version package during initialization. +// It uses sync.Once to ensure the build information is set only once, +// making it safe to call multiple times while ensuring immutability. +var SetVersionInfo = func() func(string, string, string) { + var once sync.Once + return func(version, commitHash, sizeLimit string) { + once.Do(func() { + BuildInfo.WithLabelValues(version, commitHash, sizeLimit, runtime.GOOS, runtime.GOARCH).Set(1) + }) + } +}() + // Readonly volume types const ( Namespace = "SeaweedFS" @@ -26,14 +40,20 @@ const ( bucketAtiveTTL = 10 * time.Minute ) -var readOnlyVolumeTypes = [4]string{IsReadOnly, NoWriteOrDelete, NoWriteCanDelete, IsDiskSpaceLow} - var bucketLastActiveTsNs map[string]int64 = map[string]int64{} var bucketLastActiveLock sync.Mutex var ( Gather = prometheus.NewRegistry() + BuildInfo = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: Namespace, + Subsystem: "build", + Name: "info", + Help: "A metric with a constant '1' value labeled by version, commit, sizelimit, goos, and goarch from which SeaweedFS was built.", + }, []string{"version", "commit", "sizelimit", "goos", "goarch"}) + MasterClientConnectCounter = prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: Namespace, @@ -385,6 +405,8 @@ var ( ) func init() { + Gather.MustRegister(BuildInfo) + Gather.MustRegister(MasterClientConnectCounter) Gather.MustRegister(MasterRaftIsleader) Gather.MustRegister(MasterAdminLock) diff --git a/weed/stats/metrics_buildinfo_test.go b/weed/stats/metrics_buildinfo_test.go new file mode 100644 index 000000000..1fc4a2b52 --- /dev/null +++ b/weed/stats/metrics_buildinfo_test.go @@ -0,0 +1,82 @@ +package stats_test + +import ( + "runtime" + "strings" + "testing" + + "github.com/prometheus/client_golang/prometheus/testutil" + "github.com/seaweedfs/seaweedfs/weed/stats" + _ "github.com/seaweedfs/seaweedfs/weed/util/version" // Import to trigger version init +) + +func TestBuildInfo(t *testing.T) { + // Verify that BuildInfo metric is registered and has the expected value + count := testutil.CollectAndCount(stats.BuildInfo) + if count != 1 { + t.Errorf("Expected 1 BuildInfo metric, got %d", count) + } + + // Verify the metric can be gathered + metrics, err := stats.Gather.Gather() + if err != nil { + t.Fatalf("Failed to gather metrics: %v", err) + } + + // Find the build_info metric + found := false + for _, mf := range metrics { + if mf.GetName() == "SeaweedFS_build_info" { + found = true + metric := mf.GetMetric()[0] + + // Verify the metric value is 1 + if metric.GetGauge().GetValue() != 1 { + t.Errorf("Expected BuildInfo value to be 1, got %f", metric.GetGauge().GetValue()) + } + + // Verify labels exist + labels := metric.GetLabel() + labelMap := make(map[string]string) + for _, label := range labels { + labelMap[label.GetName()] = label.GetValue() + } + + // Check required labels + if _, ok := labelMap["version"]; !ok { + t.Error("Missing 'version' label") + } + if _, ok := labelMap["commit"]; !ok { + t.Error("Missing 'commit' label") + } + if _, ok := labelMap["sizelimit"]; !ok { + t.Error("Missing 'sizelimit' label") + } + if labelMap["goos"] != runtime.GOOS { + t.Errorf("Expected goos='%s', got '%s'", runtime.GOOS, labelMap["goos"]) + } + if labelMap["goarch"] != runtime.GOARCH { + t.Errorf("Expected goarch='%s', got '%s'", runtime.GOARCH, labelMap["goarch"]) + } + + // Verify version format + if !strings.Contains(labelMap["version"], ".") { + t.Errorf("Version should contain a dot: %s", labelMap["version"]) + } + + // Verify sizelimit is either 30GB or 8000GB + if labelMap["sizelimit"] != "30GB" && labelMap["sizelimit"] != "8000GB" { + t.Errorf("Expected sizelimit to be '30GB' or '8000GB', got '%s'", labelMap["sizelimit"]) + } + + t.Logf("BuildInfo metric: version=%s, commit=%s, sizelimit=%s, goos=%s, goarch=%s", + labelMap["version"], labelMap["commit"], labelMap["sizelimit"], + labelMap["goos"], labelMap["goarch"]) + } + } + + if !found { + t.Error("BuildInfo metric not found in gathered metrics") + } +} + diff --git a/weed/util/version/constants.go b/weed/util/version/constants.go index 33b226202..8cbe2225a 100644 --- a/weed/util/version/constants.go +++ b/weed/util/version/constants.go @@ -3,6 +3,7 @@ package version import ( "fmt" + "github.com/seaweedfs/seaweedfs/weed/stats" "github.com/seaweedfs/seaweedfs/weed/util" ) @@ -14,6 +15,11 @@ var ( COMMIT = "" ) +func init() { + // Set version info in stats for Prometheus metrics + stats.SetVersionInfo(VERSION_NUMBER, COMMIT, util.SizeLimit) +} + func Version() string { return VERSION + " " + COMMIT }