diff --git a/collector.go b/collector.go index 766eeda..01c6269 100644 --- a/collector.go +++ b/collector.go @@ -13,6 +13,11 @@ const ( subsystem = "" ) +type metricInfo struct { + Value float64 + Labels prometheus.Labels +} + type exporter struct { up prometheus.Gauge workingSet prometheus.Gauge @@ -29,6 +34,7 @@ type exporter struct { databaseIndexes *prometheus.GaugeVec databaseStaleIndexes *prometheus.GaugeVec databaseSize *prometheus.GaugeVec + databaseTasks *prometheus.GaugeVec databaseRequestTotal *prometheus.CounterVec databaseDocumentPutTotal *prometheus.CounterVec @@ -55,6 +61,7 @@ func newExporter() *exporter { databaseIndexes: createDatabaseGaugeVec("database_indexes", "Count of indexes in a database"), databaseStaleIndexes: createDatabaseGaugeVec("database_stale_indexes", "Count of stale indexes in a database"), databaseSize: createDatabaseGaugeVec("database_size_bytes", "Database size in bytes"), + databaseTasks: createDatabaseGaugeVec("database_tasks", "Tasks in a database", "type", "connection_status"), databaseRequestTotal: createDatabaseCounterVec("database_request_total", "Database request count"), databaseDocumentPutTotal: createDatabaseCounterVec("database_document_put_total", "Database document puts count"), @@ -81,6 +88,7 @@ func (e *exporter) Describe(ch chan<- *prometheus.Desc) { e.databaseIndexes.Describe(ch) e.databaseStaleIndexes.Describe(ch) e.databaseSize.Describe(ch) + e.databaseTasks.Describe(ch) e.databaseRequestTotal.Describe(ch) e.databaseDocumentPutTotal.Describe(ch) @@ -132,6 +140,7 @@ func (e *exporter) Collect(ch chan<- prometheus.Metric) { collectPerDatabaseGauge(stats, e.databaseIndexes, getDatabaseIndexes, ch) collectPerDatabaseGauge(stats, e.databaseStaleIndexes, getDatabaseStaleIndexes, ch) collectPerDatabaseGauge(stats, e.databaseSize, getDatabaseSize, ch) + collectPerDatabaseGauge(stats, e.databaseTasks, getDatabaseTasks, ch) collectPerDatabaseCounter(stats, e.databaseRequestTotal, getDatabaseRequestTotal, ch) collectPerDatabaseCounter(stats, e.databaseDocumentPutBytes, getDatabaseDocumentPutBytes, ch) @@ -144,16 +153,24 @@ func (e *exporter) Collect(ch chan<- prometheus.Metric) { } } -func collectPerDatabaseGauge(stats *stats, vec *prometheus.GaugeVec, collectFunc func(*dbStats) float64, ch chan<- prometheus.Metric) { +func collectPerDatabaseGauge(stats *stats, vec *prometheus.GaugeVec, collectFunc func(*dbStats) []metricInfo, ch chan<- prometheus.Metric) { for _, dbs := range stats.dbStats { - vec.WithLabelValues(dbs.database).Set(collectFunc(dbs)) + metricInfos := collectFunc(dbs) + + for _, metricInfo := range metricInfos { + vec.With(metricInfo.Labels).Set(metricInfo.Value) + } } vec.Collect(ch) } -func collectPerDatabaseCounter(stats *stats, vec *prometheus.CounterVec, collectFunc func(*dbStats) float64, ch chan<- prometheus.Metric) { +func collectPerDatabaseCounter(stats *stats, vec *prometheus.CounterVec, collectFunc func(*dbStats) []metricInfo, ch chan<- prometheus.Metric) { for _, dbs := range stats.dbStats { - vec.WithLabelValues(dbs.database).Set(collectFunc(dbs)) + metricInfos := collectFunc(dbs) + + for _, metricInfo := range metricInfos { + vec.With(metricInfo.Labels).Set(metricInfo.Value) + } } vec.Collect(ch) } @@ -210,17 +227,63 @@ func getMapReduceIndexReducedTotal(stats *stats) float64 { return value } -func getDatabaseDocuments(dbStats *dbStats) float64 { +func getDatabaseDocuments(dbStats *dbStats) []metricInfo { + var mi []metricInfo + value, _ := jp.GetFloat(dbStats.databaseStats, "CountOfDocuments") - return value + labels := generateDatabaseLabels(dbStats, nil) + mi = appendMetricInfo(mi, value, labels) + + return mi } -func getDatabaseIndexes(dbStats *dbStats) float64 { +func getDatabaseIndexes(dbStats *dbStats) []metricInfo { + var mi []metricInfo value, _ := jp.GetFloat(dbStats.databaseStats, "CountOfIndexes") - return value + labels := generateDatabaseLabels(dbStats, nil) + mi = appendMetricInfo(mi, value, labels) + + return mi } -func getDatabaseStaleIndexes(dbStats *dbStats) float64 { +func getDatabaseTasks(dbStats *dbStats) []metricInfo { + var mi []metricInfo + + // Required for compatibility with versions 5 and 6 + onGoingTasksKey := "OngoingTasks" + _, _, _, err := jp.Get(dbStats.tasks, onGoingTasksKey) + if err != nil { + onGoingTasksKey = "OngoingTasksList" + } + + type key struct { + taskType, connectionStatus string + } + + taskAggregate := make(map[key]float64) + + jp.ArrayEach(dbStats.tasks, func(value []byte, dataType jp.ValueType, offset int, err error) { + taskType, _ := jp.GetString(value, "TaskType") + connectionStatus, _ := jp.GetString(value, "TaskConnectionStatus") + taskAggregate[key{taskType, connectionStatus}] += 1 + }, onGoingTasksKey) + + for k, v := range taskAggregate { + labels := generateDatabaseLabels(dbStats, map[string]string{ + "type": k.taskType, + "connection_status": k.connectionStatus, + }) + + mi = appendMetricInfo(mi, v, labels) + } + + return mi +} + +func getDatabaseStaleIndexes(dbStats *dbStats) []metricInfo { + var mi []metricInfo + + labels := generateDatabaseLabels(dbStats, nil) count := 0 jp.ArrayEach(dbStats.databaseStats, func(value []byte, dataType jp.ValueType, offset int, err error) { if isStale, _ := jp.GetBoolean(value, "IsStale"); isStale { @@ -228,42 +291,78 @@ func getDatabaseStaleIndexes(dbStats *dbStats) float64 { } }, "Indexes") - return float64(count) + mi = appendMetricInfo(mi, float64(count), labels) + return mi } -func getDatabaseSize(dbStats *dbStats) float64 { +func getDatabaseSize(dbStats *dbStats) []metricInfo { + var mi []metricInfo + value, _ := jp.GetFloat(dbStats.databaseStats, "SizeOnDisk", "SizeInBytes") - return value + labels := generateDatabaseLabels(dbStats, nil) + mi = appendMetricInfo(mi, value, labels) + + return mi } -func getDatabaseRequestTotal(dbStats *dbStats) float64 { +func getDatabaseRequestTotal(dbStats *dbStats) []metricInfo { + var mi []metricInfo + value, _ := jp.GetFloat(dbStats.metrics, "Requests", "RequestsPerSec", "Count") - return value + labels := generateDatabaseLabels(dbStats, nil) + mi = appendMetricInfo(mi, value, labels) + + return mi } -func getDatabaseDocumentPutTotal(dbStats *dbStats) float64 { +func getDatabaseDocumentPutTotal(dbStats *dbStats) []metricInfo { + var mi []metricInfo + value, _ := jp.GetFloat(dbStats.metrics, "Docs", "PutsPerSec", "Count") - return value + labels := generateDatabaseLabels(dbStats, nil) + mi = appendMetricInfo(mi, value, labels) + + return mi } -func getDatabaseDocumentPutBytes(dbStats *dbStats) float64 { +func getDatabaseDocumentPutBytes(dbStats *dbStats) []metricInfo { + var mi []metricInfo + value, _ := jp.GetFloat(dbStats.metrics, "Docs", "BytesPutsPerSec", "Count") - return value + labels := generateDatabaseLabels(dbStats, nil) + mi = appendMetricInfo(mi, value, labels) + + return mi } -func getDatabaseMapIndexIndexedTotal(dbStats *dbStats) float64 { +func getDatabaseMapIndexIndexedTotal(dbStats *dbStats) []metricInfo { + var mi []metricInfo + value, _ := jp.GetFloat(dbStats.metrics, "MapIndexes", "IndexedPerSec", "Count") - return value + labels := generateDatabaseLabels(dbStats, nil) + mi = appendMetricInfo(mi, value, labels) + + return mi } -func getDatabaseMapReduceIndexMappedTotal(dbStats *dbStats) float64 { +func getDatabaseMapReduceIndexMappedTotal(dbStats *dbStats) []metricInfo { + var mi []metricInfo + value, _ := jp.GetFloat(dbStats.metrics, "MapIndexes", "MappedPerSec", "Count") - return value + labels := generateDatabaseLabels(dbStats, nil) + mi = appendMetricInfo(mi, value, labels) + + return mi } -func getDatabaseMapReduceIndexReducedTotal(dbStats *dbStats) float64 { +func getDatabaseMapReduceIndexReducedTotal(dbStats *dbStats) []metricInfo { + var mi []metricInfo + value, _ := jp.GetFloat(dbStats.metrics, "MapIndexes", "ReducedPerSec", "Count") - return value + labels := generateDatabaseLabels(dbStats, nil) + mi = appendMetricInfo(mi, value, labels) + + return mi } func createGauge(name string, help string) prometheus.Gauge { @@ -275,13 +374,13 @@ func createGauge(name string, help string) prometheus.Gauge { }) } -func createDatabaseGaugeVec(name string, help string) *prometheus.GaugeVec { +func createDatabaseGaugeVec(name string, help string, labels ...string) *prometheus.GaugeVec { return prometheus.NewGaugeVec(prometheus.GaugeOpts{ Namespace: namespace, Subsystem: subsystem, Name: name, Help: help, - }, []string{"database"}) + }, append([]string{"database"}, labels...)) } func createCounter(name string, help string) prometheus.Counter { @@ -293,13 +392,13 @@ func createCounter(name string, help string) prometheus.Counter { }) } -func createDatabaseCounterVec(name string, help string) *prometheus.CounterVec { +func createDatabaseCounterVec(name string, help string, labels ...string) *prometheus.CounterVec { return prometheus.NewCounterVec(prometheus.CounterOpts{ Namespace: namespace, Subsystem: subsystem, Name: name, Help: help, - }, []string{"database"}) + }, append([]string{"database"}, labels...)) } var timespanRegex = regexp.MustCompile(`((?P\d+)\.)?(?P\d{2}):(?P\d{2}):(?P\d{2})(\.(?P\d{7}))?`) @@ -344,3 +443,22 @@ func matchNamedGroups(regex *regexp.Regexp, text string) map[string]string { } return results } + +func generateDatabaseLabels(dbStats *dbStats, additionalLabels map[string]string) prometheus.Labels { + labels := prometheus.Labels{ + "database": dbStats.database, + } + + for key, value := range additionalLabels { + labels[key] = value + } + + return labels +} + +func appendMetricInfo(tasks []metricInfo, value float64, labels prometheus.Labels) []metricInfo { + return append(tasks, metricInfo{ + Value: value, + Labels: labels, + }) +} diff --git a/ravendb_client.go b/ravendb_client.go index e3f0b26..4b341d1 100644 --- a/ravendb_client.go +++ b/ravendb_client.go @@ -27,6 +27,7 @@ type dbStats struct { indexes []byte databaseStats []byte storage []byte + tasks []byte } func initializeClient() { @@ -84,6 +85,7 @@ func preparePaths(databases []string) []string { paths = append(paths, fmt.Sprintf("/databases/%s/metrics", database)) paths = append(paths, fmt.Sprintf("/databases/%s/stats", database)) paths = append(paths, fmt.Sprintf("/databases/%s/debug/storage/report", database)) + paths = append(paths, fmt.Sprintf("/databases/%s/tasks", database)) } return paths @@ -184,6 +186,7 @@ func organizeGetResults(results map[string]getResult, databases []string) (*stat metrics: results[fmt.Sprintf("/databases/%s/metrics", database)].result, databaseStats: results[fmt.Sprintf("/databases/%s/stats", database)].result, storage: results[fmt.Sprintf("/databases/%s//debug/storage/report", database)].result, + tasks: results[fmt.Sprintf("/databases/%s/tasks", database)].result, } stats.dbStats = append(stats.dbStats, dbs) diff --git a/readme.md b/readme.md index ff2d852..cc7cdd0 100644 --- a/readme.md +++ b/readme.md @@ -86,6 +86,11 @@ ravendb_database_size_bytes{database="Demo"} 6.35568128e+08 # HELP ravendb_database_stale_indexes Count of stale indexes in a database # TYPE ravendb_database_stale_indexes gauge ravendb_database_stale_indexes{database="Demo"} 0 +# HELP ravendb_database_tasks Tasks in a database +# TYPE ravendb_database_tasks gauge +ravendb_database_tasks{connection_status="Active",database="Demo",type="Backup"} 2 +ravendb_database_tasks{connection_status="NotActive",database="Demo",type="Backup"} 1 +ravendb_database_tasks{connection_status="NotActive",database="Demo",type="Subscription"} 2 # HELP ravendb_document_put_bytes_total Server-wide document put bytes # TYPE ravendb_document_put_bytes_total counter ravendb_document_put_bytes_total 0 @@ -117,7 +122,12 @@ ravendb_working_set_bytes 1.651195904e+09 ## Changelog +### 0.5.0 + +* Added database gauge metric: `ravendb_database_tasks` + ### 0.4.0 + * Updated golang version, some of the dependencies * Used `scratch` as base for the docker image