diff --git a/cmd/hl-exporter/main.go b/cmd/hl-exporter/main.go index 0961532..0938259 100644 --- a/cmd/hl-exporter/main.go +++ b/cmd/hl-exporter/main.go @@ -12,6 +12,7 @@ import ( "github.com/validaoxyz/hyperliquid-exporter/internal/exporter" "github.com/validaoxyz/hyperliquid-exporter/internal/logger" "github.com/validaoxyz/hyperliquid-exporter/internal/metrics" + "github.com/validaoxyz/hyperliquid-exporter/internal/monitors" ) func main() { @@ -76,7 +77,13 @@ func main() { } } - // Initialize metrics + ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer stop() + + // After loading config, before metrics initialization + validatorAddress, isValidator := monitors.GetValidatorStatus(cfg.NodeHome) + + // Initialize metrics configuration metricsConfig := metrics.MetricsConfig{ EnablePrometheus: !*disableProm && *enableProm, EnableOTLP: *enableOTLP, @@ -84,11 +91,11 @@ func main() { OTLPInsecure: *otlpInsecure, Alias: *alias, Chain: *chain, + NodeHome: cfg.NodeHome, + ValidatorAddress: validatorAddress, + IsValidator: isValidator, } - ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) - defer stop() - if err := metrics.InitMetrics(ctx, metricsConfig); err != nil { logger.Error("Failed to initialize metrics: %v", err) os.Exit(1) diff --git a/go.mod b/go.mod index 097173f..8f1711c 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,6 @@ module github.com/validaoxyz/hyperliquid-exporter go 1.22 - require ( github.com/joho/godotenv v1.5.1 github.com/prometheus/client_golang v1.20.4 diff --git a/internal/metrics/config.go b/internal/metrics/config.go index edd3b03..61be2e9 100644 --- a/internal/metrics/config.go +++ b/internal/metrics/config.go @@ -7,4 +7,7 @@ type MetricsConfig struct { OTLPInsecure bool Alias string Chain string + NodeHome string + ValidatorAddress string + IsValidator bool } diff --git a/internal/metrics/getters.go b/internal/metrics/getters.go index f4103b9..d3fc30d 100644 --- a/internal/metrics/getters.go +++ b/internal/metrics/getters.go @@ -3,5 +3,5 @@ package metrics func IsValidator() bool { metricsMutex.Lock() defer metricsMutex.Unlock() - return nodeAlias.IsValidator + return nodeIdentity.IsValidator } diff --git a/internal/metrics/identity.go b/internal/metrics/identity.go index 82b7433..66bfa41 100644 --- a/internal/metrics/identity.go +++ b/internal/metrics/identity.go @@ -21,7 +21,7 @@ func getPublicIP() (string, error) { return string(ip), nil } -func InitializeNodeAlias(cfg MetricsConfig) error { +func InitializeNodeIdentity(cfg MetricsConfig) error { ip, err := getPublicIP() if err != nil { return fmt.Errorf("failed to get public IP: %w", err) @@ -30,10 +30,12 @@ func InitializeNodeAlias(cfg MetricsConfig) error { metricsMutex.Lock() defer metricsMutex.Unlock() - nodeAlias = NodeAlias{ - ServerIP: ip, - Alias: cfg.Alias, - Chain: cfg.Chain, + nodeIdentity = NodeIdentity{ + ServerIP: ip, + Alias: cfg.Alias, + Chain: cfg.Chain, + ValidatorAddress: cfg.ValidatorAddress, + IsValidator: cfg.IsValidator, } return nil diff --git a/internal/metrics/init.go b/internal/metrics/init.go index ebeede9..716694d 100644 --- a/internal/metrics/init.go +++ b/internal/metrics/init.go @@ -17,10 +17,12 @@ import ( // InitMetrics initializes the metrics system with the given configuration func InitMetrics(ctx context.Context, cfg MetricsConfig) error { - if err := InitializeNodeAlias(cfg); err != nil { - return fmt.Errorf("failed to initialize node alias: %w", err) + // Initialize node identity with values from config + if err := InitializeNodeIdentity(cfg); err != nil { + return fmt.Errorf("failed to initialize node identity: %w", err) } + // Initialize the provider if err := InitProvider(ctx, cfg); err != nil { return fmt.Errorf("failed to initialize provider: %w", err) } @@ -29,8 +31,6 @@ func InitMetrics(ctx context.Context, cfg MetricsConfig) error { return fmt.Errorf("failed to initialize instruments: %w", err) } - // SetNodeInfo() - if cfg.EnablePrometheus { if err := StartPrometheusServer(ctx, 8086); err != nil { return fmt.Errorf("failed to start Prometheus server: %w", err) @@ -57,9 +57,9 @@ func sanitizeEndpoint(endpoint string) string { func InitProvider(ctx context.Context, cfg MetricsConfig) error { metricsMutex.RLock() - serverIP := nodeAlias.ServerIP - isValidator := nodeAlias.IsValidator - validatorAddress := nodeAlias.ValidatorAddress + serverIP := nodeIdentity.ServerIP + isValidator := nodeIdentity.IsValidator + validatorAddress := nodeIdentity.ValidatorAddress metricsMutex.RUnlock() res := resource.NewWithAttributes( diff --git a/internal/metrics/setters.go b/internal/metrics/setters.go index 7043e76..208351c 100644 --- a/internal/metrics/setters.go +++ b/internal/metrics/setters.go @@ -166,13 +166,13 @@ func IncrementEVMTransactionsCounter() { func SetIsValidator(isValidator bool) { metricsMutex.Lock() defer metricsMutex.Unlock() - nodeAlias.IsValidator = isValidator + nodeIdentity.IsValidator = isValidator } func SetValidatorAddress(address string) { metricsMutex.Lock() defer metricsMutex.Unlock() - nodeAlias.ValidatorAddress = address + nodeIdentity.ValidatorAddress = address } func SetActiveStake(stake float64) { diff --git a/internal/metrics/types.go b/internal/metrics/types.go index 55316ac..0f9934c 100644 --- a/internal/metrics/types.go +++ b/internal/metrics/types.go @@ -12,7 +12,7 @@ type labeledValue struct { labels []attribute.KeyValue } -type NodeAlias struct { +type NodeIdentity struct { ValidatorAddress string ServerIP string Alias string @@ -22,11 +22,11 @@ type NodeAlias struct { // Global variables for metric state management var ( + nodeIdentity NodeIdentity + metricsMutex sync.RWMutex currentValues = make(map[api.Observable]interface{}) labeledValues = make(map[api.Observable]map[string]labeledValue) - metricsMutex sync.RWMutex callbacks []api.Registration - nodeAlias NodeAlias ) // TODO CommonLabels holds the common labels to be added to all metrics diff --git a/internal/monitors/validator_status_monitor.go b/internal/monitors/validator_status_monitor.go index 35097a3..77be6c6 100644 --- a/internal/monitors/validator_status_monitor.go +++ b/internal/monitors/validator_status_monitor.go @@ -136,3 +136,50 @@ func ReadLastLine(filePath string) (string, error) { } return lastLine, nil } + +func GetValidatorStatus(nodeHome string) (string, bool) { + statusDir := filepath.Join(nodeHome, "data/node_logs/status/hourly") + latestFile, err := utils.GetLatestFile(statusDir) + if err != nil { + logger.Warning("Error finding latest status file: %v", err) + return "", false + } + + fileInfo, err := os.Stat(latestFile) + if err != nil { + logger.Warning("Error getting status file info: %v", err) + return "", false + } + + if time.Since(fileInfo.ModTime()) > 24*time.Hour { + return "", false + } + + lastLine, err := ReadLastLine(latestFile) + if err != nil { + logger.Warning("Error reading last line of status file: %v", err) + return "", false + } + + var rawData []json.RawMessage + if err := json.Unmarshal([]byte(lastLine), &rawData); err != nil { + logger.Warning("Failed to parse status array: %v", err) + return "", false + } + + if len(rawData) != 2 { + logger.Warning("Unexpected validator status data format") + return "", false + } + + var data struct { + HomeValidator string `json:"home_validator"` + } + + if err := json.Unmarshal(rawData[1], &data); err != nil { + logger.Warning("Failed to parse validator data: %v", err) + return "", false + } + + return data.HomeValidator, data.HomeValidator != "" +}