diff --git a/pkg/dbsql/metrics.go b/pkg/dbsql/metrics.go new file mode 100644 index 0000000..85d6389 --- /dev/null +++ b/pkg/dbsql/metrics.go @@ -0,0 +1,56 @@ +// Copyright © 2026 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dbsql + +import ( + "context" + "database/sql" + + "github.com/hyperledger/firefly-common/pkg/metric" + "github.com/prometheus/client_golang/prometheus/collectors" +) + +type metricsOptions struct { + dbName string +} + +func (opts *metricsOptions) apply(options ...MetricsOption) { + for _, option := range options { + option(opts) + } +} + +type MetricsOption func(opts *metricsOptions) + +// WithDBName sets the name of the database label for the metrics collector. +func WithDBName(dbName string) MetricsOption { + return func(opts *metricsOptions) { + opts.dbName = dbName + } +} + +// EnableDBMetrics registers a prometheus collector for the given database connection. +func EnableDBMetrics(_ context.Context, metricsRegistry metric.MetricsRegistry, db *sql.DB, options ...MetricsOption) { + opts := &metricsOptions{} + opts.apply(options...) + + if opts.dbName == "" { + opts.dbName = "default" + } + + metricsRegistry.MustRegisterCollector(collectors.NewDBStatsCollector(db, opts.dbName)) +} diff --git a/pkg/ffapi/apiserver.go b/pkg/ffapi/apiserver.go index c800436..1e0d4cd 100644 --- a/pkg/ffapi/apiserver.go +++ b/pkg/ffapi/apiserver.go @@ -1,4 +1,4 @@ -// Copyright © 2025 Kaleido, Inc. +// Copyright © 2026 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -26,6 +26,7 @@ import ( "time" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/gorilla/mux" @@ -65,6 +66,7 @@ type apiServer[T any] struct { alwaysPaginate bool handleYAML bool monitoringEnabled bool + goProcessMetricsEnabled bool metricsPath string livenessPath string loggingPath string @@ -116,15 +118,17 @@ func NewAPIServer[T any](ctx context.Context, options APIServerOptions[T]) APISe } as := &apiServer[T]{ - defaultFilterLimit: options.APIConfig.GetUint64(ConfAPIDefaultFilterLimit), - maxFilterLimit: options.APIConfig.GetUint64(ConfAPIMaxFilterLimit), - maxFilterSkip: options.APIConfig.GetUint64(ConfAPIMaxFilterSkip), - requestTimeout: options.APIConfig.GetDuration(ConfAPIRequestTimeout), - requestMaxTimeout: options.APIConfig.GetDuration(ConfAPIRequestMaxTimeout), - monitoringEnabled: options.MonitoringConfig.GetBool(ConfMonitoringServerEnabled), - metricsPath: options.MonitoringConfig.GetString(ConfMonitoringServerMetricsPath), - livenessPath: options.MonitoringConfig.GetString(ConfMonitoringServerLivenessPath), - loggingPath: options.MonitoringConfig.GetString(ConfMonitoringServerLoggingPath), + defaultFilterLimit: options.APIConfig.GetUint64(ConfAPIDefaultFilterLimit), + maxFilterLimit: options.APIConfig.GetUint64(ConfAPIMaxFilterLimit), + maxFilterSkip: options.APIConfig.GetUint64(ConfAPIMaxFilterSkip), + requestTimeout: options.APIConfig.GetDuration(ConfAPIRequestTimeout), + requestMaxTimeout: options.APIConfig.GetDuration(ConfAPIRequestMaxTimeout), + monitoringEnabled: options.MonitoringConfig.GetBool(ConfMonitoringServerEnabled), + goProcessMetricsEnabled: options.MonitoringConfig.GetBool(ConfMonitoringGoProcessMetricsEnabled), + metricsPath: options.MonitoringConfig.GetString(ConfMonitoringServerMetricsPath), + livenessPath: options.MonitoringConfig.GetString(ConfMonitoringServerLivenessPath), + loggingPath: options.MonitoringConfig.GetString(ConfMonitoringServerLoggingPath), + alwaysPaginate: options.APIConfig.GetBool(ConfAPIAlwaysPaginate), handleYAML: options.HandleYAML, apiDynamicPublicURLHeader: options.APIConfig.GetString(ConfAPIDynamicPublicURLHeader), @@ -306,6 +310,11 @@ func (as *apiServer[T]) createMuxRouter(ctx context.Context) (*mux.Router, error } if as.monitoringEnabled { + if as.goProcessMetricsEnabled { + as.MetricsRegistry.MustRegisterCollector(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{})) + as.MetricsRegistry.MustRegisterCollector(collectors.NewGoCollector()) + } + as.MetricsRegistry.MustRegisterCollector(collectors.NewBuildInfoCollector()) h, _ := as.MetricsRegistry.GetHTTPMetricsInstrumentationsMiddlewareForSubsystem(ctx, as.metricsSubsystemName()) r.Use(h) } diff --git a/pkg/ffapi/apiserver_config.go b/pkg/ffapi/apiserver_config.go index 2d374c4..65ec1d7 100644 --- a/pkg/ffapi/apiserver_config.go +++ b/pkg/ffapi/apiserver_config.go @@ -22,10 +22,11 @@ import ( ) var ( - ConfMonitoringServerEnabled = "enabled" - ConfMonitoringServerMetricsPath = "metricsPath" - ConfMonitoringServerLivenessPath = "livenessPath" - ConfMonitoringServerLoggingPath = "loggingPath" + ConfMonitoringServerEnabled = "enabled" + ConfMonitoringServerMetricsPath = "metricsPath" + ConfMonitoringServerLivenessPath = "livenessPath" + ConfMonitoringServerLoggingPath = "loggingPath" + ConfMonitoringGoProcessMetricsEnabled = "goProcessMetricsEnabled" ConfAPIDefaultFilterLimit = "defaultFilterLimit" ConfAPIMaxFilterLimit = "maxFilterLimit" @@ -50,6 +51,7 @@ func InitAPIServerConfig(apiConfig, monitoringConfig, corsConfig config.Section) httpserver.InitHTTPConfig(monitoringConfig, 6000) monitoringConfig.AddKnownKey(ConfMonitoringServerEnabled, true) + monitoringConfig.AddKnownKey(ConfMonitoringGoProcessMetricsEnabled, false) monitoringConfig.AddKnownKey(ConfMonitoringServerMetricsPath, "/metrics") monitoringConfig.AddKnownKey(ConfMonitoringServerLivenessPath, "/livez") monitoringConfig.AddKnownKey(ConfMonitoringServerLoggingPath, "/logging")