Skip to content

Commit

Permalink
add database metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
alexopryshko committed Aug 24, 2021
1 parent c18e181 commit bb60352
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 68 deletions.
134 changes: 77 additions & 57 deletions pkg/exporter/balancecollector.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package exporter

import (
"fmt"
"github.com/ktsstudio/selectel-exporter/pkg/selapi"
"github.com/prometheus/client_golang/prometheus"
"log"
)

type primaryMetrics struct {
Expand All @@ -28,86 +28,106 @@ type vpcMetrics storageMetrics
type vmwareMetrics storageMetrics

type balanceCollector struct {
project selapi.Project

primary primaryMetrics
storage storageMetrics
vpc vpcMetrics
vmware vmwareMetrics
}

func registerGauge(name string, project selapi.Project) prometheus.Gauge {
func registerGauge(account, name string, project selapi.Project) prometheus.Gauge {
g := prometheus.NewGauge(prometheus.GaugeOpts{
Name: name,
ConstLabels: prometheus.Labels{"project": project.Name},
ConstLabels: prometheus.Labels{"project": project.Name, "account": account},
})
prometheus.MustRegister(g)
return g
}

func registerPrimaryGauge(name string, project selapi.Project) prometheus.Gauge {
return registerGauge("primary", name, project)
}

func registerStorageGauge(name string, project selapi.Project) prometheus.Gauge {
return registerGauge("storage", name, project)
}

func registerVpcGauge(name string, project selapi.Project) prometheus.Gauge {
return registerGauge("vpc", name, project)
}

func registerVmwareGauge(name string, project selapi.Project) prometheus.Gauge {
return registerGauge("vmware", name, project)
}

func NewBalanceCollector(project selapi.Project) *balanceCollector {
c := &balanceCollector{}

c.primary.main = registerGauge("selectel_billing_primary_main", project)
c.primary.bonus = registerGauge("selectel_billing_primary_bonus", project)
c.primary.vkRub = registerGauge("selectel_billing_primary_vk_rub", project)
c.primary.ref = registerGauge("selectel_billing_primary_ref", project)
c.primary.holdMain = registerGauge("selectel_billing_primary_hold_main", project)
c.primary.holdBonus = registerGauge("selectel_billing_primary_hold_bonus", project)
c.primary.holdVkRub = registerGauge("selectel_billing_primary_hold_vk_rub", project)

c.storage.main = registerGauge("selectel_billing_storage_main", project)
c.storage.bonus = registerGauge("selectel_billing_storage_bonus", project)
c.storage.vkRub = registerGauge("selectel_billing_storage_vk_rub", project)
c.storage.debt = registerGauge("selectel_billing_storage_debt", project)
c.storage.sum = registerGauge("selectel_billing_storage_sum", project)

c.vpc.main = registerGauge("selectel_billing_vpc_main", project)
c.vpc.bonus = registerGauge("selectel_billing_vpc_bonus", project)
c.vpc.vkRub = registerGauge("selectel_billing_vpc_vk_rub", project)
c.vpc.debt = registerGauge("selectel_billing_vpc_debt", project)
c.vpc.sum = registerGauge("selectel_billing_vpc_sum", project)

c.vmware.main = registerGauge("selectel_billing_vmware_main", project)
c.vmware.bonus = registerGauge("selectel_billing_vmware_bonus", project)
c.vmware.vkRub = registerGauge("selectel_billing_vmware_vk_rub", project)
c.vmware.debt = registerGauge("selectel_billing_vmware_debt", project)
c.vmware.sum = registerGauge("selectel_billing_vmware_sum", project)
c := &balanceCollector{project: project}

c.primary.main = registerPrimaryGauge("selectel_billing_main", project)
c.primary.bonus = registerPrimaryGauge("selectel_billing_bonus", project)
c.primary.vkRub = registerPrimaryGauge("selectel_billing_vk_rub", project)
c.primary.ref = registerPrimaryGauge("selectel_billing_ref", project)
c.primary.holdMain = registerPrimaryGauge("selectel_billing_hold_main", project)
c.primary.holdBonus = registerPrimaryGauge("selectel_billing_hold_bonus", project)
c.primary.holdVkRub = registerPrimaryGauge("selectel_billing_hold_vk_rub", project)

c.storage.main = registerStorageGauge("selectel_billing_main", project)
c.storage.bonus = registerStorageGauge("selectel_billing_bonus", project)
c.storage.vkRub = registerStorageGauge("selectel_billing_vk_rub", project)
c.storage.debt = registerStorageGauge("selectel_billing_debt", project)
c.storage.sum = registerStorageGauge("selectel_billing_sum", project)

c.vpc.main = registerVpcGauge("selectel_billing_main", project)
c.vpc.bonus = registerVpcGauge("selectel_billing_bonus", project)
c.vpc.vkRub = registerVpcGauge("selectel_billing_vk_rub", project)
c.vpc.debt = registerVpcGauge("selectel_billing_debt", project)
c.vpc.sum = registerVpcGauge("selectel_billing_sum", project)

c.vmware.main = registerVmwareGauge("selectel_billing_main", project)
c.vmware.bonus = registerVmwareGauge("selectel_billing_bonus", project)
c.vmware.vkRub = registerVmwareGauge("selectel_billing_vk_rub", project)
c.vmware.debt = registerVmwareGauge("selectel_billing_debt", project)
c.vmware.sum = registerVmwareGauge("selectel_billing_sum", project)

return c
}

func (c *balanceCollector) Collect(e *exporter) error {
log.Println("collect balance metrics")
func (col *balanceCollector) GetInfo() string {
return fmt.Sprintf("project: %s - collect balance metrics", col.project.Name)
}

func (col *balanceCollector) Collect(e *exporter) error {
res, err := selapi.FetchBalance(e.token)
if err != nil {
return err
}

c.primary.main.Set(float64(res.Data.Primary.Main))
c.primary.bonus.Set(float64(res.Data.Primary.Bonus))
c.primary.vkRub.Set(float64(res.Data.Primary.VkRub))
c.primary.ref.Set(float64(res.Data.Primary.Ref))
c.primary.holdMain.Set(float64(res.Data.Primary.Hold.Main))
c.primary.holdBonus.Set(float64(res.Data.Primary.Hold.Bonus))
c.primary.holdVkRub.Set(float64(res.Data.Primary.Hold.VkRub))

c.storage.main.Set(float64(res.Data.Storage.Main))
c.storage.bonus.Set(float64(res.Data.Storage.Bonus))
c.storage.vkRub.Set(float64(res.Data.Storage.VkRub))
c.storage.debt.Set(float64(res.Data.Storage.Debt))
c.storage.sum.Set(float64(res.Data.Storage.Sum))

c.vpc.main.Set(float64(res.Data.Vpc.Main))
c.vpc.bonus.Set(float64(res.Data.Vpc.Bonus))
c.vpc.vkRub.Set(float64(res.Data.Vpc.VkRub))
c.vpc.debt.Set(float64(res.Data.Vpc.Debt))
c.vpc.sum.Set(float64(res.Data.Vpc.Sum))

c.vmware.main.Set(float64(res.Data.Vmware.Main))
c.vmware.bonus.Set(float64(res.Data.Vmware.Bonus))
c.vmware.vkRub.Set(float64(res.Data.Vmware.VkRub))
c.vmware.debt.Set(float64(res.Data.Vmware.Debt))
c.vmware.sum.Set(float64(res.Data.Vmware.Sum))
col.primary.main.Set(float64(res.Data.Primary.Main))
col.primary.bonus.Set(float64(res.Data.Primary.Bonus))
col.primary.vkRub.Set(float64(res.Data.Primary.VkRub))
col.primary.ref.Set(float64(res.Data.Primary.Ref))
col.primary.holdMain.Set(float64(res.Data.Primary.Hold.Main))
col.primary.holdBonus.Set(float64(res.Data.Primary.Hold.Bonus))
col.primary.holdVkRub.Set(float64(res.Data.Primary.Hold.VkRub))

col.storage.main.Set(float64(res.Data.Storage.Main))
col.storage.bonus.Set(float64(res.Data.Storage.Bonus))
col.storage.vkRub.Set(float64(res.Data.Storage.VkRub))
col.storage.debt.Set(float64(res.Data.Storage.Debt))
col.storage.sum.Set(float64(res.Data.Storage.Sum))

col.vpc.main.Set(float64(res.Data.Vpc.Main))
col.vpc.bonus.Set(float64(res.Data.Vpc.Bonus))
col.vpc.vkRub.Set(float64(res.Data.Vpc.VkRub))
col.vpc.debt.Set(float64(res.Data.Vpc.Debt))
col.vpc.sum.Set(float64(res.Data.Vpc.Sum))

col.vmware.main.Set(float64(res.Data.Vmware.Main))
col.vmware.bonus.Set(float64(res.Data.Vmware.Bonus))
col.vmware.vkRub.Set(float64(res.Data.Vmware.VkRub))
col.vmware.debt.Set(float64(res.Data.Vmware.Debt))
col.vmware.sum.Set(float64(res.Data.Vmware.Sum))

return nil
}
79 changes: 79 additions & 0 deletions pkg/exporter/dbcollector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package exporter

import (
"fmt"
"github.com/ktsstudio/selectel-exporter/pkg/selapi"
"github.com/prometheus/client_golang/prometheus"
"time"
)

func buildGaugeKey(metricName, ip, dbName string) string {
return fmt.Sprintf("%s_%s_%s", metricName, ip, dbName)
}

type databaseCollector struct {
project selapi.Project
datastore selapi.Datastore
metrics map[string]prometheus.Gauge
}

func NewDatabaseCollector(project selapi.Project, datastore selapi.Datastore) *databaseCollector {
return &databaseCollector{
project: project,
datastore: datastore,
metrics: make(map[string]prometheus.Gauge),
}
}

func (col *databaseCollector) registerGauge(metricName string, metric selapi.DatabaseMetric) {
key := buildGaugeKey(metricName, metric.Ip, metric.DbName)
g, ok := col.metrics[key]
if !ok {
g = prometheus.NewGauge(prometheus.GaugeOpts{
Name: metricName,
ConstLabels: prometheus.Labels{
"project": col.project.Name,
"datastore": col.datastore.Name,
"ip": metric.Ip,
"database": metric.DbName,
},
})
prometheus.MustRegister(g)
col.metrics[key] = g
}
g.Set(metric.Last)
}

func (col *databaseCollector) loadMetrics(metricName string, metrics []selapi.DatabaseMetric) {
for _, m := range metrics {
col.registerGauge(metricName, m)
}
}

func (col *databaseCollector) GetInfo() string {
return fmt.Sprintf(
"project: %s, datastore: %s - collect database metrics", col.project.Name, col.datastore.Name)
}

func (col *databaseCollector) Collect(e *exporter) error {
start := time.Now().Add(-1 * time.Minute).Unix()
end := time.Now().Unix()
res, err := selapi.FetchDatabaseMetrics(e.openstackAccountToken, e.region, col.datastore.Id, start, end)
if err != nil {
return err
}
col.loadMetrics("selectel_database_locks", res.Metrics.Locks)
col.loadMetrics("selectel_database_deadlocks", res.Metrics.Deadlocks)
col.loadMetrics("selectel_database_cache_hit_ratio", res.Metrics.CacheHitRatio)
col.loadMetrics("selectel_database_tup_updated", res.Metrics.TupUpdated)
col.loadMetrics("selectel_database_tup_returned", res.Metrics.TupReturned)
col.loadMetrics("selectel_database_tup_inserted", res.Metrics.TupInserted)
col.loadMetrics("selectel_database_tup_fetched", res.Metrics.TupFetched)
col.loadMetrics("selectel_database_tup_deleted", res.Metrics.TupDeleted)
col.loadMetrics("selectel_database_xact_rollback", res.Metrics.XActRollback)
col.loadMetrics("selectel_database_xact_commit", res.Metrics.XActCommit)
col.loadMetrics("selectel_database_xact_commit_rollback", res.Metrics.XActCommitRollback)
col.loadMetrics("selectel_database_max_tx_duration", res.Metrics.MaxTxDuration)
col.loadMetrics("selectel_database_connections", res.Metrics.Connections)
return nil
}
8 changes: 6 additions & 2 deletions pkg/exporter/dscollector.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package exporter

import (
"fmt"
"github.com/ktsstudio/selectel-exporter/pkg/selapi"
"github.com/prometheus/client_golang/prometheus"
"log"
"time"
)

Expand Down Expand Up @@ -87,8 +87,12 @@ func (col *datastoreCollector) loadDiskBytes(data *selapi.DatastoreMetricsRespon
}
}

func (col *datastoreCollector) GetInfo() string {
return fmt.Sprintf(
"project: %s, datastore: %s - collect datastore metrics", col.project.Name, col.datastore.Name)
}

func (col *datastoreCollector) Collect(e *exporter) error {
log.Println("collect datastore metrics")
start := time.Now().Add(-1 * time.Minute).Unix()
end := time.Now().Unix()
res, err := selapi.FetchDatastoreMetrics(e.openstackAccountToken, e.region, col.datastore.Id, start, end)
Expand Down
19 changes: 10 additions & 9 deletions pkg/exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import (
"time"
)

type collectorFunc func(*exporter) error
type selectelCollector interface {
GetInfo() string
Collect(e *exporter) error
}

type exporter struct {
token string
Expand All @@ -21,9 +24,8 @@ type exporter struct {
refreshPeriod time.Duration
stopCh chan bool
wg sync.WaitGroup
collectors []collectorFunc
collectors []selectelCollector

databases []selapi.Database
datastores []selapi.Datastore
}

Expand Down Expand Up @@ -94,12 +96,10 @@ func (e *exporter) fetchDatastores() error {

func (e *exporter) loadCollectors() {
for _, ds := range e.datastores {
collector := NewDatastoreCollector(e.project, ds)
e.collectors = append(e.collectors, collector.Collect)
e.collectors = append(e.collectors, NewDatastoreCollector(e.project, ds))
e.collectors = append(e.collectors, NewDatabaseCollector(e.project, ds))
}

collector := NewBalanceCollector(e.project)
e.collectors = append(e.collectors, collector.Collect)
e.collectors = append(e.collectors, NewBalanceCollector(e.project))
}

func (e *exporter) runCollectors() {
Expand All @@ -109,7 +109,8 @@ func (e *exporter) runCollectors() {
col := col
go func() {
defer wg.Done()
err := col(e)
log.Println(col.GetInfo())
err := col.Collect(e)
if err != nil {
log.Println(err)
}
Expand Down
59 changes: 59 additions & 0 deletions pkg/selapi/dbmetrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package selapi

import (
"encoding/json"
"fmt"
"github.com/ktsstudio/selectel-exporter/pkg/apperrors"
"net/http"
"strconv"
)

type DatabaseMetric struct {
DatastoreMetric
DbName string `json:"db_name"`
}

type DatabaseMetricsResponses struct {
Metrics struct {
Connections []DatabaseMetric `json:"memory_percent"`
MaxTxDuration []DatabaseMetric `json:"max_tx_duration"`
XActCommitRollback []DatabaseMetric `json:"xact_commit_rollback"`
XActCommit []DatabaseMetric `json:"xact_commit"`
XActRollback []DatabaseMetric `json:"xact_rollback"`
TupDeleted []DatabaseMetric `json:"tup_deleted"`
TupFetched []DatabaseMetric `json:"tup_fetched"`
TupInserted []DatabaseMetric `json:"tup_inserted"`
TupReturned []DatabaseMetric `json:"tup_returned"`
TupUpdated []DatabaseMetric `json:"tup_updated"`
CacheHitRatio []DatabaseMetric `json:"cache_hit_ratio"`
Deadlocks []DatabaseMetric `json:"deadlocks"`
Locks []DatabaseMetric `json:"locks"`
} `json:"metrics"`
}

func FetchDatabaseMetrics(token, region, datastoreId string, start, end int64) (*DatabaseMetricsResponses, error) {
client := &http.Client{}
req, err := http.NewRequest(
"GET",
fmt.Sprintf("https://%s.dbaas.selcloud.ru/v1/datastores/%s/database-metrics", region, datastoreId),
nil)
if err != nil {
return nil, err
}
req.Header.Add("X-Auth-Token", token)
q := req.URL.Query()
q.Add("start", strconv.FormatInt(start, 10))
q.Add("end", strconv.FormatInt(end, 10))
req.URL.RawQuery = q.Encode()

data, err := fetch(client, req)
if err != nil {
return nil, err
}

resp := &DatabaseMetricsResponses{}
if err := json.Unmarshal(data, resp); err != nil {
return nil, apperrors.NewResponseFormatError("DatabaseMetricsResponses")
}
return resp, nil
}

0 comments on commit bb60352

Please sign in to comment.