diff --git a/README.md b/README.md index e69de29..6640b24 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,44 @@ +# Manifest Node Exporter + +[![CircleCI](https://dl.circleci.com/status-badge/img/gh/liftedinit/manifest-node-exporter/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/liftedinit/manifest-node-exporter/tree/main) + +A Prometheus exporter for collecting metrics from Manifest blockchain nodes. + +## Installation + +Download the latest release from the [releases page](https://github.com/liftedinit/manifest-node-exporter/releases) + +## Quick Start + +```bash +manifest-node-exporter serve grpc-address [flags] +``` + +where `grpc-address` is the address of the `manifest-ledger` gRPC server. + +The exporter will start a Prometheus metrics server on `0.0.0.0:2112` by default. + +## Global Flags + +| Flag | Description | +|---------------------|-------------------------------------------------------------------------------------------| +| `-h`, `--help` | help for manifest-node-exporter | +| `-l`, `--logLevel` | Set the log level. Available levels: `debug`, `info`, `warn`, `error`. Default is `info`. | + +## Serve Flags + +| Flag | Description | +|---------------------|-------------------------------------------------------------------------------------------| +| `-h`, `--help` | help for serve | +| `--insecure` | Skip TLS verification for gRPC connection. Default is `false`. | +| `--listen-address` | Address to listen on for Prometheus metrics. Default is `0.0.0.0:2112`. | + +## Metrics + +| Metric Name | Description | +|-------------------------------------|---------------------------------------------------------------------------| +| `manifest_tokenomics_denom_info` | Information about the token denominations (symbol, denom, name, display). | +| `manifest_tokenomics_total_supply` | Total supply for a given token. | +| `manifest_tokenomics_token_count` | The number of different tokens hosted on the Manifest blockchain. | +| `manifest_tokenomics_denom_grpc_up` | Whether the gRPC query for the token denomination was successful. | +| `manifest_tokenomics_count_grpc_up` | Whether the gRPC query for the token count was successful. | \ No newline at end of file diff --git a/cmd/serve.go b/cmd/serve.go index 325aa3c..f0cefc1 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -115,7 +115,6 @@ func validateGrpcAddress(grpcAddr string) error { // handleInterrupt handles interrupt signals for graceful shutdown. func handleInterrupt(cancel context.CancelFunc) { - // Handle interrupt signals for graceful shutdown c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { @@ -126,7 +125,7 @@ func handleInterrupt(cancel context.CancelFunc) { } func init() { - serveCmd.Flags().String("listen-address", ":2112", "Address to listen on") + serveCmd.Flags().String("listen-address", "0.0.0.0:2112", "Address to listen on") serveCmd.Flags().Bool("insecure", false, "Skip TLS certificate verification (INSECURE)") serveCmd.Flags().Uint("max-concurrency", 100, "Maximum request concurrency (advanced)") serveCmd.Flags().Uint("max-retries", 3, "Maximum number of retries for failed requests") diff --git a/pkg/collectors/grpc/registry.go b/pkg/collectors/grpc/registry.go index 655d5c1..b3c61cd 100644 --- a/pkg/collectors/grpc/registry.go +++ b/pkg/collectors/grpc/registry.go @@ -63,7 +63,6 @@ func RegisterCollectors(grpcClient *pkg.GRPCClient) ([]prometheus.Collector, err return nil, fmt.Errorf("cannot register collectors with a nil or unconnected gRPC client") } - // Use the DefaultGrpcRegistry defined in common.go to create collectors collectors, err := DefaultGrpcRegistry.CreateGrpcCollectors(grpcClient) if err != nil { // Error already logged by CreateGrpcCollectors if a factory failed @@ -79,17 +78,10 @@ func RegisterCollectors(grpcClient *pkg.GRPCClient) ([]prometheus.Collector, err if err := prometheus.DefaultRegisterer.Register(collector); err != nil { var alreadyRegistered prometheus.AlreadyRegisteredError if errors.As(err, &alreadyRegistered) { - // This is often benign during development or restarts, log as Info or Debug slog.Debug("Collector already registered with Prometheus, skipping registration.", "collector_type", collectorType) - // We might still want to include it in the returned list if it was successfully *created* - // Depending on desired behavior, you could fetch the existing collector: - // registeredCollectors = append(registeredCollectors, alreadyRegistered.ExistingCollector) skippedCount++ } else { - // This is a more serious registration error slog.Error("Failed to register collector with Prometheus", "collector_type", collectorType, "error", err) - // Decide if you want to fail entirely or just skip this collector - // Failing fast is often safer. return registeredCollectors, fmt.Errorf("failed to register collector type %s: %w", collectorType, err) } } else { diff --git a/pkg/collectors/grpc/token_count.go b/pkg/collectors/grpc/token_count.go index 297d548..09a4182 100644 --- a/pkg/collectors/grpc/token_count.go +++ b/pkg/collectors/grpc/token_count.go @@ -35,13 +35,13 @@ func NewTokenCountCollector(client *pkg.GRPCClient) *TokenCountCollector { grpcClient: client, initialError: initialError, tokenCountDesc: prometheus.NewDesc( - prometheus.BuildFQName("manifest", "tokenomics", "token_number"), + prometheus.BuildFQName("manifest", "tokenomics", "token_count"), "Total number of denominations, including native, IBC and factory tokens.", []string{}, prometheus.Labels{"source": "grpc"}, ), upDesc: prometheus.NewDesc( - prometheus.BuildFQName("manifest", "tokenomics", "supply_grpc_up"), + prometheus.BuildFQName("manifest", "tokenomics", "count_grpc_up"), "Whether the gRPC query was successful.", nil, prometheus.Labels{"source": "grpc", "queries": "DenomsMetadata"}, diff --git a/pkg/config.go b/pkg/config.go index d6708ba..a4768cc 100644 --- a/pkg/config.go +++ b/pkg/config.go @@ -9,20 +9,11 @@ import ( ) type ServeConfig struct { - ListenAddress string `mapstructure:"listen_address"` - Insecure bool `mapstructure:"insecure"` - MaxConcurrency uint `mapstructure:"max_concurrency"` - MaxRetries uint `mapstructure:"max_retries"` + ListenAddress string `mapstructure:"listen_address"` + Insecure bool `mapstructure:"insecure"` } func (c ServeConfig) Validate() error { - if c.MaxConcurrency == 0 { - return fmt.Errorf("max-concurrency must be greater than 0") - } - if c.MaxRetries == 0 { - return fmt.Errorf("max-retries must be greater than 0") - } - host, port, err := net.SplitHostPort(c.ListenAddress) if err != nil { return fmt.Errorf("invalid prometheus-addr format, expected host:port: %w", err) @@ -40,9 +31,7 @@ func (c ServeConfig) Validate() error { func LoadServeConfig() ServeConfig { return ServeConfig{ - ListenAddress: viper.GetString("listen-address"), - Insecure: viper.GetBool("insecure"), - MaxConcurrency: viper.GetUint("max-concurrency"), - MaxRetries: viper.GetUint("max-retries"), + ListenAddress: viper.GetString("listen-address"), + Insecure: viper.GetBool("insecure"), } } diff --git a/pkg/metrics_server.go b/pkg/metrics_server.go index 8f9dbe4..59e9e60 100644 --- a/pkg/metrics_server.go +++ b/pkg/metrics_server.go @@ -48,15 +48,10 @@ func (s *MetricsServer) Start() <-chan error { slog.Info("Starting Prometheus metrics server...", "address", s.listenAddr) go func() { - // ListenAndServe blocks until the server stops. err := s.httpServer.ListenAndServe() if err != nil && !errors.Is(err, http.ErrServerClosed) { - // Only send non-nil errors that aren't the expected ErrServerClosed errChan <- fmt.Errorf("prometheus metrics server failed: %w", err) } - // Close the channel only if an error occurred, otherwise leave it open. - // The caller uses context cancellation for shutdown signal, not channel closing. - // close(errChan) // Don't close here on normal shutdown }() // Give a brief moment to allow ListenAndServe to start or fail early @@ -77,5 +72,5 @@ func (s *MetricsServer) Shutdown(ctx context.Context) error { } else { slog.Error("Error during metrics server shutdown.", "error", err) } - return err // Return the error for the caller + return err } diff --git a/tests/e2e_serve_test.go b/tests/e2e_serve_test.go index 1047cd4..4f89eb7 100644 --- a/tests/e2e_serve_test.go +++ b/tests/e2e_serve_test.go @@ -53,7 +53,7 @@ func TestE2EServe(t *testing.T) { }{ {"manifest_tokenomics_denom_info", `{denom="udummy",display="DUMMY_DISPLAY",name="Dummy_Name",source="grpc",symbol="Dummy_Symbol"}`}, {"manifest_tokenomics_total_supply", `{denom="udummy",source="grpc"} 10`}, - {"manifest_tokenomics_token_number", `{source="grpc"} 102`}, + {"manifest_tokenomics_token_count", `{source="grpc"} 102`}, } for _, c := range cases { t.Run(c.Name, func(t *testing.T) {