How to expose prometheus metrics on instance level

Hi,
I have a plugin which runs under multiple instances in the grafana env. I want to expose some prometheus metrics on instance level. Eg: I would like to reuse the *sql.db.stats in a custom collector by calling the collect function. This is working like a charm if only one instance of the plugin is running. When i have a second one, I receive “duplicate metrics collector registration attempted” errors. Is there a way to get a registry by instance? with its own endpoint? like:metrics/plugins/myplugin/myinstance? or how to handle this situation?

Hey. I’m not sure I understand what “reuse the *sql.db.stats in a custom collector by calling the collect function” means.

Given you have a backend plugin and you want to register metrics, either register the metrics when plugin starts, e.g. grafana-plugin-examples/examples/datasource-basic/pkg/main.go at main · grafana/grafana-plugin-examples · GitHub, or either use sync.Once in datasource instance to guarantee the metrics are only registered once. To track per instance you would have to use metric labels.

Let me know if I completely misunderstood the question and/or provide more details for us to be able to understand and help.

Thanks for your detailed reply. It helps a bit but probably my problem is more prometheus generic. I’m not really an expert on that.
What would I like to do: I would like to generate metrics for my datasource plugin. There is the possibility to use the gauge or counter interfaces (with promauto) like described in the grafana-doc.
On the other hand I have internal counters or state objects from subsequent libraries. There I thought it would be possible to just copy the actual state of this object during the prometheus collect. But for that I need a reference to that object in my collect function which seems not to be possible with a global collect and multiple instances.

type LocalPrometheusCollector struct {
	db                        *sql.DB
	setting                   *backend.DataSourceInstanceSettings
	db_in_use_connections     *prometheus.Desc
	db_wait_count             *prometheus.Desc
}
func (c *LocalPrometheusCollector) Describe(ch chan<- *prometheus.Desc) {
	ch <- c.db_in_use_connections
	ch <- c.db_wait_count
}

func (c *LocalPrometheusCollector) Collect(ch chan<- prometheus.Metric) {
	ch <- prometheus.MustNewConstMetric(c.db_in_use_connections, prometheus.GaugeValue, float64(c.db.Stats().InUse), c.setting.Name)
    ch <- prometheus.MustNewConstMetric(c.db_wait_count, prometheus.CounterValue, float64(c.db.Stats().WaitCount), c.setting.Name)
}

func NewLocalPrometheusCollector(db *sql.DB, setting *backend.DataSourceInstanceSettings) *LocalPrometheusCollector {
	return &LocalPrometheusCollector{
		db:                        db,
		setting:                   setting,
		db_in_use_connections:     prometheus.NewDesc("grafana_plugin_sql_pool_in_use_connections", "SQL Pool - The number of connections currently in use.", []string{"instance_name"}, nil),
        db_wait_count:             prometheus.NewDesc("grafana_plugin_sql_pool_count_total", "SQL Pool - Connections total wait count", []string{"instance_name"}, nil),
}

//in:
func newDataSourceInstance(ctx context.Context, setting backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
...
prom := NewLocalPrometheusCollector(db, &setting)
prometheus.DefaultRegisterer.MustRegister(prom)
...

With that I will receive a

duplicate metrics collector registration attempted

by shifting the mustRegister() into main i will not be able to access the objects on instance level.

All my googeling :wink: brougth me to the solution to register endpoints per instance…

In the example above the problem is the counter metric. I haven’t found a way to use the library based counter by using the promauto approach. Since you have to inc it somehow by having a library counter value only. (got it?)

Any available library for tracking db stats seems to support only one database, e.g. GitHub - dlmiddlecote/sqlstats: A Go library for collecting sql.DBStats in Prometheus format. I’m afraid we cannot help with this specific use case with using custom collectors, but can provide some pointers/ideas.

by shifting the mustRegister() into main i will not be able to access the objects on instance level.

You can probably work around it, but you’re on your own and would need to manage concurrency/locks when new instances are created and old ones disposed.

In general, if your collector is available to each instance you could add/remove your instance with the collector, something like

func newDataSourceInstance(collector *LocalPrometheusCollector) datasource.InstanceFactoryFunc {
  return datasource.InstanceFactoryFunc(func(ctx context.Context, setting backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
    ...
    instance := &MyDatasource{
      collector: collector,
    }
    collector.Add("<name>", <instance/db/stats>)
    ...
  })
}

func (d *MyDatasource) Dispose() {
  d.collector.Remove("<name>")
}

// main
prom := NewLocalPrometheusCollector()
prometheus.DefaultRegisterer.MustRegister(prom)
err := datasource.Manage("myds-plugin-id", newDataSourceInstance(prom), datasource.ManageOpts{})
...

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.