Add /v3/metrics (get) endpoint to list all known metrics

This commit is contained in:
Ingo Oppermann
2022-09-08 13:50:53 +02:00
parent 2d754b4212
commit 285ef79716
17 changed files with 229 additions and 48 deletions

View File

@@ -839,6 +839,30 @@ const docTemplate = `{
}
},
"/api/v3/metrics": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "List all known metrics with their description and labels",
"produces": [
"application/json"
],
"summary": "List all known metrics with their description and labels",
"operationId": "metrics-3-describe",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/api.MetricsDescription"
}
}
}
}
},
"post": {
"security": [
{
@@ -2926,6 +2950,23 @@ const docTemplate = `{
}
}
},
"api.MetricsDescription": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"labels": {
"type": "array",
"items": {
"type": "string"
}
},
"name": {
"type": "string"
}
}
},
"api.MetricsQuery": {
"type": "object",
"properties": {

View File

@@ -831,6 +831,30 @@
}
},
"/api/v3/metrics": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "List all known metrics with their description and labels",
"produces": [
"application/json"
],
"summary": "List all known metrics with their description and labels",
"operationId": "metrics-3-describe",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/api.MetricsDescription"
}
}
}
}
},
"post": {
"security": [
{
@@ -2918,6 +2942,23 @@
}
}
},
"api.MetricsDescription": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"labels": {
"type": "array",
"items": {
"type": "string"
}
},
"name": {
"type": "string"
}
}
},
"api.MetricsQuery": {
"type": "object",
"properties": {

View File

@@ -462,6 +462,17 @@ definitions:
- password
- username
type: object
api.MetricsDescription:
properties:
description:
type: string
labels:
items:
type: string
type: array
name:
type: string
type: object
api.MetricsQuery:
properties:
interval_sec:
@@ -2264,6 +2275,21 @@ paths:
- ApiKeyAuth: []
summary: Add JSON metadata under the given key
/api/v3/metrics:
get:
description: List all known metrics with their description and labels
operationId: metrics-3-describe
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/api.MetricsDescription'
type: array
security:
- ApiKeyAuth: []
summary: List all known metrics with their description and labels
post:
consumes:
- application/json

View File

@@ -7,6 +7,12 @@ import (
"github.com/datarhei/core/v16/monitor"
)
type MetricsDescription struct {
Name string `json:"name"`
Description string `json:"description"`
Labels []string `json:"labels"`
}
type MetricsQueryMetric struct {
Name string `json:"name"`
Labels map[string]string `json:"labels"`

View File

@@ -2,6 +2,7 @@ package api
import (
"net/http"
"sort"
"time"
"github.com/datarhei/core/v16/http/api"
@@ -28,6 +29,34 @@ func NewMetrics(config MetricsConfig) *MetricsHandler {
}
}
// Describe the known metrics
// @Summary List all known metrics with their description and labels
// @Description List all known metrics with their description and labels
// @ID metrics-3-describe
// @Produce json
// @Success 200 {array} api.MetricsDescription
// @Security ApiKeyAuth
// @Router /api/v3/metrics [get]
func (r *MetricsHandler) Describe(c echo.Context) error {
response := []api.MetricsDescription{}
descriptors := r.metrics.Describe()
for _, d := range descriptors {
response = append(response, api.MetricsDescription{
Name: d.Name(),
Description: d.Description(),
Labels: d.Labels(),
})
}
sort.Slice(response, func(i, j int) bool {
return response[i].Name < response[j].Name
})
return c.JSON(http.StatusOK, response)
}
// Query the collected metrics
// @Summary Query the collected metrics
// @Description Query the collected metrics

View File

@@ -640,6 +640,7 @@ func (s *server) setRoutesV3(v3 *echo.Group) {
// v3 Log
v3.GET("/log", s.v3handler.log.Log)
// v3 Resources
// v3 Metrics
v3.GET("/metrics", s.v3handler.resources.Describe)
v3.POST("/metrics", s.v3handler.resources.Metrics)
}

View File

@@ -20,11 +20,11 @@ func NewCPUCollector() metric.Collector {
ncpu: 1,
}
c.ncpuDescr = metric.NewDesc("cpu_ncpu", "", nil)
c.systemDescr = metric.NewDesc("cpu_system", "", nil)
c.userDescr = metric.NewDesc("cpu_user", "", nil)
c.idleDescr = metric.NewDesc("cpu_idle", "", nil)
c.otherDescr = metric.NewDesc("cpu_other", "", nil)
c.ncpuDescr = metric.NewDesc("cpu_ncpu", "Number of logical CPUs in the system", nil)
c.systemDescr = metric.NewDesc("cpu_system", "Percentage of CPU used for the system", nil)
c.userDescr = metric.NewDesc("cpu_user", "Percentage of CPU used for the user", nil)
c.idleDescr = metric.NewDesc("cpu_idle", "Percentage of idle CPU", nil)
c.otherDescr = metric.NewDesc("cpu_other", "Percentage of CPU used for other subsystems", nil)
if ncpu, err := psutil.CPUCounts(true); err == nil {
c.ncpu = ncpu

View File

@@ -17,8 +17,8 @@ func NewDiskCollector(path string) metric.Collector {
path: path,
}
c.totalDescr = metric.NewDesc("disk_total", "", []string{"path"})
c.usageDescr = metric.NewDesc("disk_usage", "", []string{"path"})
c.totalDescr = metric.NewDesc("disk_total", "Total size of the disk in bytes", []string{"path"})
c.usageDescr = metric.NewDesc("disk_usage", "Number of used bytes on the disk", []string{"path"})
return c
}

View File

@@ -17,7 +17,7 @@ func NewFFmpegCollector(f ffmpeg.FFmpeg) metric.Collector {
ffmpeg: f,
}
c.processDescr = metric.NewDesc("ffmpeg_process", "", []string{"state"})
c.processDescr = metric.NewDesc("ffmpeg_process", "State of the ffmpeg process", []string{"state"})
return c
}

View File

@@ -19,9 +19,9 @@ func NewFilesystemCollector(name string, fs fs.Filesystem) metric.Collector {
name: name,
}
c.limitDescr = metric.NewDesc("filesystem_limit", "", []string{"name"})
c.usageDescr = metric.NewDesc("filesystem_usage", "", []string{"name"})
c.filesDescr = metric.NewDesc("filesystem_files", "", []string{"name"})
c.limitDescr = metric.NewDesc("filesystem_limit", "Total size of the filesystem in bytes, negative if unlimited", []string{"name"})
c.usageDescr = metric.NewDesc("filesystem_usage", "Number of used bytes on the filesystem", []string{"name"})
c.filesDescr = metric.NewDesc("filesystem_files", "Number of files on the filesystem (excluding directories)", []string{"name"})
return c
}

View File

@@ -13,8 +13,8 @@ type memCollector struct {
func NewMemCollector() metric.Collector {
c := &memCollector{}
c.totalDescr = metric.NewDesc("mem_total", "", nil)
c.freeDescr = metric.NewDesc("mem_free", "", nil)
c.totalDescr = metric.NewDesc("mem_total", "Total available memory in bytes", nil)
c.freeDescr = metric.NewDesc("mem_free", "Free memory in bytes", nil)
return c
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"regexp"
"sort"
"strings"
)
type Pattern interface {
@@ -304,6 +305,10 @@ func NewDesc(name, description string, labels []string) *Description {
}
}
func (d *Description) String() string {
return fmt.Sprintf("%s: %s (%s)", d.name, d.description, strings.Join(d.labels, ","))
}
func (d *Description) Name() string {
return d.name
}
@@ -312,6 +317,13 @@ func (d *Description) Description() string {
return d.description
}
func (d *Description) Labels() []string {
labels := make([]string, len(d.labels))
copy(labels, d.labels)
return labels
}
type Collector interface {
Prefix() string
Describe() []*Description

View File

@@ -10,9 +10,26 @@ import (
"github.com/datarhei/core/v16/monitor/metric"
)
type Monitor interface {
Register(c metric.Collector)
type Reader interface {
Collect(patterns []metric.Pattern) metric.Metrics
Describe() []*metric.Description
}
type Monitor interface {
Reader
Register(c metric.Collector)
UnregisterAll()
}
type HistoryReader interface {
Reader
History(timerange, interval time.Duration, patterns []metric.Pattern) []HistoryMetrics
Resolution() (timerange, interval time.Duration)
}
type HistoryMonitor interface {
HistoryReader
Register(c metric.Collector)
UnregisterAll()
}
@@ -75,6 +92,26 @@ func (m *monitor) Collect(patterns []metric.Pattern) metric.Metrics {
return metrics
}
func (m *monitor) Describe() []*metric.Description {
descriptors := []*metric.Description{}
collectors := map[metric.Collector]struct{}{}
m.lock.RLock()
defer m.lock.RUnlock()
for _, c := range m.collectors {
if _, ok := collectors[c]; ok {
continue
}
collectors[c] = struct{}{}
descriptors = append(descriptors, c.Describe()...)
}
return descriptors
}
func (m *monitor) UnregisterAll() {
m.lock.Lock()
defer m.lock.Unlock()
@@ -86,12 +123,6 @@ func (m *monitor) UnregisterAll() {
m.collectors = make(map[string]metric.Collector)
}
type HistoryMonitor interface {
Monitor
History(timerange, interval time.Duration, patterns []metric.Pattern) []HistoryMetrics
Resolution() (timerange, interval time.Duration)
}
type historyMonitor struct {
monitor Monitor
@@ -209,6 +240,10 @@ func (m *historyMonitor) Collect(patterns []metric.Pattern) metric.Metrics {
return m.monitor.Collect(patterns)
}
func (m *historyMonitor) Describe() []*metric.Description {
return m.monitor.Describe()
}
func (m *historyMonitor) UnregisterAll() {
m.monitor.UnregisterAll()
@@ -327,13 +362,3 @@ func (m *historyMonitor) resample(values []HistoryMetrics, timerange, interval t
return v
}
type Reader interface {
Collect(patterns []metric.Pattern) metric.Metrics
}
type HistoryReader interface {
Reader
History(timerange, interval time.Duration, patterns []metric.Pattern) []HistoryMetrics
Resolution() (timerange, interval time.Duration)
}

View File

@@ -13,8 +13,8 @@ type netCollector struct {
func NewNetCollector() metric.Collector {
c := &netCollector{}
c.rxDescr = metric.NewDesc("net_rx", "", []string{"interface"})
c.txDescr = metric.NewDesc("net_tx", "", []string{"interface"})
c.rxDescr = metric.NewDesc("net_rx", "Number of received bytes", []string{"interface"})
c.txDescr = metric.NewDesc("net_tx", "Number of transmitted bytes", []string{"interface"})
return c
}

View File

@@ -22,10 +22,10 @@ func NewRestreamCollector(r restream.Restreamer) metric.Collector {
r: r,
}
c.restreamProcessDescr = metric.NewDesc("restream_process", "", []string{"processid", "state", "order", "name"})
c.restreamProcessStatesDescr = metric.NewDesc("restream_process_states", "", []string{"processid", "state"})
c.restreamProcessIODescr = metric.NewDesc("restream_io", "", []string{"processid", "type", "id", "address", "index", "stream", "media", "name"})
c.restreamStatesDescr = metric.NewDesc("restream_state", "", []string{"state"})
c.restreamProcessDescr = metric.NewDesc("restream_process", "Current process values by name", []string{"processid", "state", "order", "name"})
c.restreamProcessStatesDescr = metric.NewDesc("restream_process_states", "Current process state", []string{"processid", "state"})
c.restreamProcessIODescr = metric.NewDesc("restream_io", "Current process IO values by name", []string{"processid", "type", "id", "address", "index", "stream", "media", "name"})
c.restreamStatesDescr = metric.NewDesc("restream_state", "Summarized process states", []string{"state"})
return c
}

View File

@@ -31,17 +31,17 @@ func NewSessionCollector(r session.RegistryReader, collectors []string) metric.C
c.collectors = r.Collectors()
}
c.totalDescr = metric.NewDesc("session_total", "", []string{"collector"})
c.limitDescr = metric.NewDesc("session_limit", "", []string{"collector"})
c.activeDescr = metric.NewDesc("session_active", "", []string{"collector"})
c.rxBytesDescr = metric.NewDesc("session_rxbytes", "", []string{"collector"})
c.txBytesDescr = metric.NewDesc("session_txbytes", "", []string{"collector"})
c.totalDescr = metric.NewDesc("session_total", "Total sessions", []string{"collector"})
c.limitDescr = metric.NewDesc("session_limit", "Max. number of concurrent sessions", []string{"collector"})
c.activeDescr = metric.NewDesc("session_active", "Number of current sessions", []string{"collector"})
c.rxBytesDescr = metric.NewDesc("session_rxbytes", "Number of received bytes", []string{"collector"})
c.txBytesDescr = metric.NewDesc("session_txbytes", "Number of transmitted bytes", []string{"collector"})
c.rxBitrateDescr = metric.NewDesc("session_rxbitrate", "", []string{"collector"})
c.txBitrateDescr = metric.NewDesc("session_txbitrate", "", []string{"collector"})
c.rxBitrateDescr = metric.NewDesc("session_rxbitrate", "Current receiving bitrate in bit per second", []string{"collector"})
c.txBitrateDescr = metric.NewDesc("session_txbitrate", "Current transmitting bitrate in bit per second", []string{"collector"})
c.maxTxBitrateDescr = metric.NewDesc("session_maxtxbitrate", "", []string{"collector"})
c.maxRxBitrateDescr = metric.NewDesc("session_maxrxbitrate", "", []string{"collector"})
c.maxRxBitrateDescr = metric.NewDesc("session_maxrxbitrate", "Max. allowed receiving bitrate in bit per second", []string{"collector"})
c.maxTxBitrateDescr = metric.NewDesc("session_maxtxbitrate", "Max. allowed transmitting bitrate in bit per second", []string{"collector"})
return c
}

View File

@@ -16,7 +16,7 @@ func NewUptimeCollector() metric.Collector {
t: time.Now(),
}
c.uptimeDescr = metric.NewDesc("uptime_uptime", "", nil)
c.uptimeDescr = metric.NewDesc("uptime_uptime", "Current uptime in seconds", nil)
return c
}