2021-02-12 20:37:30 +03:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
2021-02-13 01:06:48 +03:00
|
|
|
"errors"
|
2021-02-12 20:37:30 +03:00
|
|
|
"github.com/emvi/logbuch"
|
2023-03-03 22:40:50 +03:00
|
|
|
"github.com/go-chi/chi/v5"
|
2021-02-12 20:37:30 +03:00
|
|
|
conf "github.com/muety/wakapi/config"
|
2022-12-01 12:57:07 +03:00
|
|
|
"github.com/muety/wakapi/helpers"
|
2021-02-12 20:37:30 +03:00
|
|
|
"github.com/muety/wakapi/middlewares"
|
|
|
|
"github.com/muety/wakapi/models"
|
|
|
|
v1 "github.com/muety/wakapi/models/compat/wakatime/v1"
|
|
|
|
mm "github.com/muety/wakapi/models/metrics"
|
2022-03-18 20:20:13 +03:00
|
|
|
"github.com/muety/wakapi/repositories"
|
2021-02-12 20:37:30 +03:00
|
|
|
"github.com/muety/wakapi/services"
|
|
|
|
"net/http"
|
2021-10-14 11:22:59 +03:00
|
|
|
"runtime"
|
2021-02-12 20:37:30 +03:00
|
|
|
"sort"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
MetricsPrefix = "wakatime"
|
|
|
|
|
2021-02-13 01:06:48 +03:00
|
|
|
DescHeartbeats = "Total number of tracked heartbeats."
|
2021-02-12 20:37:30 +03:00
|
|
|
DescAllTime = "Total seconds (all time)."
|
|
|
|
DescTotal = "Total seconds."
|
|
|
|
DescEditors = "Total seconds for each editor."
|
|
|
|
DescProjects = "Total seconds for each project."
|
|
|
|
DescLanguages = "Total seconds for each language."
|
|
|
|
DescOperatingSystems = "Total seconds for each operating system."
|
|
|
|
DescMachines = "Total seconds for each machine."
|
2021-06-11 21:59:34 +03:00
|
|
|
DescLabels = "Total seconds for each project label."
|
2021-02-12 20:37:30 +03:00
|
|
|
|
2021-02-13 01:06:48 +03:00
|
|
|
DescAdminTotalTime = "Total seconds (all users, all time)."
|
2021-02-13 01:16:20 +03:00
|
|
|
DescAdminTotalHeartbeats = "Total number of tracked heartbeats (all users, all time)"
|
|
|
|
DescAdminUserHeartbeats = "Total number of tracked heartbeats by user (all time)."
|
2021-02-13 01:06:48 +03:00
|
|
|
DescAdminTotalUsers = "Total number of registered users."
|
|
|
|
DescAdminActiveUsers = "Number of active users."
|
2021-10-14 11:22:59 +03:00
|
|
|
|
2022-11-20 12:59:06 +03:00
|
|
|
DescJobQueueEnqueued = "Number of jobs currently enqueued"
|
|
|
|
DescJobQueueTotalFinished = "Total number of processed jobs"
|
|
|
|
|
2021-10-14 11:35:01 +03:00
|
|
|
DescMemAllocTotal = "Total number of bytes allocated for heap"
|
|
|
|
DescMemSysTotal = "Total number of bytes obtained from the OS"
|
|
|
|
DescGoroutines = "Total number of running goroutines"
|
2022-03-18 20:20:13 +03:00
|
|
|
DescDatabaseSize = "Total database size in bytes"
|
2021-02-12 20:37:30 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
type MetricsHandler struct {
|
|
|
|
config *conf.Config
|
|
|
|
userSrvc services.IUserService
|
|
|
|
summarySrvc services.ISummaryService
|
|
|
|
heartbeatSrvc services.IHeartbeatService
|
|
|
|
keyValueSrvc services.IKeyValueService
|
2022-03-18 20:20:13 +03:00
|
|
|
metricsRepo *repositories.MetricsRepository
|
2021-02-12 20:37:30 +03:00
|
|
|
}
|
|
|
|
|
2022-03-18 20:20:13 +03:00
|
|
|
func NewMetricsHandler(userService services.IUserService, summaryService services.ISummaryService, heartbeatService services.IHeartbeatService, keyValueService services.IKeyValueService, metricsRepo *repositories.MetricsRepository) *MetricsHandler {
|
2021-02-12 20:37:30 +03:00
|
|
|
return &MetricsHandler{
|
|
|
|
userSrvc: userService,
|
|
|
|
summarySrvc: summaryService,
|
|
|
|
heartbeatSrvc: heartbeatService,
|
|
|
|
keyValueSrvc: keyValueService,
|
2022-03-18 20:20:13 +03:00
|
|
|
metricsRepo: metricsRepo,
|
2021-02-12 20:37:30 +03:00
|
|
|
config: conf.Get(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-03 22:40:50 +03:00
|
|
|
func (h *MetricsHandler) RegisterRoutes(router chi.Router) {
|
2021-02-12 20:37:30 +03:00
|
|
|
if !h.config.Security.ExposeMetrics {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
logbuch.Info("exposing prometheus metrics under /api/metrics")
|
|
|
|
|
2023-03-03 22:40:50 +03:00
|
|
|
r := chi.NewRouter()
|
|
|
|
r.Use(middlewares.NewAuthenticateMiddleware(h.userSrvc).Handler)
|
|
|
|
r.Get("/", h.Get)
|
|
|
|
|
|
|
|
router.Mount("/metrics", r)
|
2021-02-12 20:37:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (h *MetricsHandler) Get(w http.ResponseWriter, r *http.Request) {
|
2021-03-26 15:10:10 +03:00
|
|
|
reqUser := middlewares.GetPrincipal(r)
|
2021-02-13 01:06:48 +03:00
|
|
|
if reqUser == nil {
|
2021-02-12 20:37:30 +03:00
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
2021-02-13 13:23:58 +03:00
|
|
|
w.Write([]byte(conf.ErrUnauthorized))
|
2021-02-12 20:37:30 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-02-13 01:06:48 +03:00
|
|
|
var metrics mm.Metrics
|
|
|
|
|
|
|
|
if userMetrics, err := h.getUserMetrics(reqUser); err != nil {
|
2021-04-16 16:59:39 +03:00
|
|
|
conf.Log().Request(r).Error("%v", err)
|
2021-02-13 01:06:48 +03:00
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
w.Write([]byte(conf.ErrInternalServerError))
|
2021-02-13 13:23:58 +03:00
|
|
|
return
|
2021-02-13 01:06:48 +03:00
|
|
|
} else {
|
|
|
|
for _, m := range *userMetrics {
|
|
|
|
metrics = append(metrics, m)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if reqUser.IsAdmin {
|
|
|
|
if adminMetrics, err := h.getAdminMetrics(reqUser); err != nil {
|
2021-04-16 16:59:39 +03:00
|
|
|
conf.Log().Request(r).Error("%v", err)
|
2021-02-13 01:06:48 +03:00
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
w.Write([]byte(conf.ErrInternalServerError))
|
2021-02-13 13:23:58 +03:00
|
|
|
return
|
2021-02-13 01:06:48 +03:00
|
|
|
} else {
|
|
|
|
for _, m := range *adminMetrics {
|
|
|
|
metrics = append(metrics, m)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Sort(metrics)
|
|
|
|
|
|
|
|
w.Header().Set("content-type", "text/plain; charset=utf-8")
|
|
|
|
w.Write([]byte(metrics.Print()))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *MetricsHandler) getUserMetrics(user *models.User) (*mm.Metrics, error) {
|
|
|
|
var metrics mm.Metrics
|
|
|
|
|
2021-12-26 19:02:14 +03:00
|
|
|
summaryAllTime, err := h.summarySrvc.Aliased(time.Time{}, time.Now(), user, h.summarySrvc.Retrieve, nil, false)
|
2021-02-12 20:37:30 +03:00
|
|
|
if err != nil {
|
2021-02-13 01:06:48 +03:00
|
|
|
logbuch.Error("failed to retrieve all time summary for user '%s' for metric", user.ID)
|
|
|
|
return nil, err
|
2021-02-12 20:37:30 +03:00
|
|
|
}
|
|
|
|
|
2022-12-01 12:57:07 +03:00
|
|
|
from, to := helpers.MustResolveIntervalRawTZ("today", user.TZ())
|
2021-02-12 20:37:30 +03:00
|
|
|
|
2021-12-26 19:02:14 +03:00
|
|
|
summaryToday, err := h.summarySrvc.Aliased(from, to, user, h.summarySrvc.Retrieve, nil, false)
|
2021-02-12 20:37:30 +03:00
|
|
|
if err != nil {
|
2021-02-13 01:06:48 +03:00
|
|
|
logbuch.Error("failed to retrieve today's summary for user '%s' for metric", user.ID)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
heartbeatCount, err := h.heartbeatSrvc.CountByUser(user)
|
|
|
|
if err != nil {
|
|
|
|
logbuch.Error("failed to count heartbeats for user '%s' for metric", user.ID)
|
|
|
|
return nil, err
|
2021-02-12 20:37:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// User Metrics
|
|
|
|
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-02-12 20:37:30 +03:00
|
|
|
Name: MetricsPrefix + "_cumulative_seconds_total",
|
|
|
|
Desc: DescAllTime,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: int64(v1.NewAllTimeFrom(summaryAllTime).Data.TotalSeconds),
|
2021-02-12 20:37:30 +03:00
|
|
|
Labels: []mm.Label{},
|
|
|
|
})
|
|
|
|
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-02-12 20:37:30 +03:00
|
|
|
Name: MetricsPrefix + "_seconds_total",
|
|
|
|
Desc: DescTotal,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: int64(summaryToday.TotalTime().Seconds()),
|
2021-02-12 20:37:30 +03:00
|
|
|
Labels: []mm.Label{},
|
|
|
|
})
|
|
|
|
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-02-13 01:06:48 +03:00
|
|
|
Name: MetricsPrefix + "_heartbeats_total",
|
|
|
|
Desc: DescHeartbeats,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: int64(heartbeatCount),
|
2021-02-13 01:06:48 +03:00
|
|
|
Labels: []mm.Label{},
|
|
|
|
})
|
|
|
|
|
2021-02-12 20:37:30 +03:00
|
|
|
for _, p := range summaryToday.Projects {
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-02-12 20:37:30 +03:00
|
|
|
Name: MetricsPrefix + "_project_seconds_total",
|
|
|
|
Desc: DescProjects,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: int64(summaryToday.TotalTimeByKey(models.SummaryProject, p.Key).Seconds()),
|
2021-02-12 20:37:30 +03:00
|
|
|
Labels: []mm.Label{{Key: "name", Value: p.Key}},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, l := range summaryToday.Languages {
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-02-12 20:37:30 +03:00
|
|
|
Name: MetricsPrefix + "_language_seconds_total",
|
|
|
|
Desc: DescLanguages,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: int64(summaryToday.TotalTimeByKey(models.SummaryLanguage, l.Key).Seconds()),
|
2021-02-12 20:37:30 +03:00
|
|
|
Labels: []mm.Label{{Key: "name", Value: l.Key}},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, e := range summaryToday.Editors {
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-02-12 20:37:30 +03:00
|
|
|
Name: MetricsPrefix + "_editor_seconds_total",
|
|
|
|
Desc: DescEditors,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: int64(summaryToday.TotalTimeByKey(models.SummaryEditor, e.Key).Seconds()),
|
2021-02-12 20:37:30 +03:00
|
|
|
Labels: []mm.Label{{Key: "name", Value: e.Key}},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, o := range summaryToday.OperatingSystems {
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-02-12 20:37:30 +03:00
|
|
|
Name: MetricsPrefix + "_operating_system_seconds_total",
|
|
|
|
Desc: DescOperatingSystems,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: int64(summaryToday.TotalTimeByKey(models.SummaryOS, o.Key).Seconds()),
|
2021-02-12 20:37:30 +03:00
|
|
|
Labels: []mm.Label{{Key: "name", Value: o.Key}},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, m := range summaryToday.Machines {
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-02-12 20:37:30 +03:00
|
|
|
Name: MetricsPrefix + "_machine_seconds_total",
|
|
|
|
Desc: DescMachines,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: int64(summaryToday.TotalTimeByKey(models.SummaryMachine, m.Key).Seconds()),
|
2021-02-12 20:37:30 +03:00
|
|
|
Labels: []mm.Label{{Key: "name", Value: m.Key}},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-06-11 21:59:34 +03:00
|
|
|
for _, m := range summaryToday.Labels {
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-06-11 21:59:34 +03:00
|
|
|
Name: MetricsPrefix + "_label_seconds_total",
|
|
|
|
Desc: DescLabels,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: int64(summaryToday.TotalTimeByKey(models.SummaryLabel, m.Key).Seconds()),
|
2021-06-11 21:59:34 +03:00
|
|
|
Labels: []mm.Label{{Key: "name", Value: m.Key}},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-10-14 11:35:01 +03:00
|
|
|
// Runtime metrics
|
|
|
|
var memStats runtime.MemStats
|
|
|
|
runtime.ReadMemStats(&memStats)
|
|
|
|
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-10-14 11:22:59 +03:00
|
|
|
Name: MetricsPrefix + "_goroutines_total",
|
|
|
|
Desc: DescGoroutines,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: int64(runtime.NumGoroutine()),
|
2021-10-14 11:22:59 +03:00
|
|
|
Labels: []mm.Label{},
|
|
|
|
})
|
|
|
|
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-10-14 11:35:01 +03:00
|
|
|
Name: MetricsPrefix + "_mem_alloc_total",
|
|
|
|
Desc: DescMemAllocTotal,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: int64(memStats.Alloc),
|
2021-10-14 11:35:01 +03:00
|
|
|
Labels: []mm.Label{},
|
|
|
|
})
|
|
|
|
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-10-14 11:35:01 +03:00
|
|
|
Name: MetricsPrefix + "_mem_sys_total",
|
|
|
|
Desc: DescMemSysTotal,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: int64(memStats.Sys),
|
|
|
|
Labels: []mm.Label{},
|
|
|
|
})
|
|
|
|
|
|
|
|
// Database metrics
|
|
|
|
dbSize, err := h.metricsRepo.GetDatabaseSize()
|
|
|
|
if err != nil {
|
|
|
|
logbuch.Warn("failed to get database size (%v)", err)
|
|
|
|
}
|
|
|
|
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2022-03-18 20:20:13 +03:00
|
|
|
Name: MetricsPrefix + "_db_total_bytes",
|
|
|
|
Desc: DescDatabaseSize,
|
|
|
|
Value: dbSize,
|
2021-10-14 11:35:01 +03:00
|
|
|
Labels: []mm.Label{},
|
|
|
|
})
|
|
|
|
|
2022-11-20 12:59:06 +03:00
|
|
|
// Miscellaneous
|
|
|
|
for _, qm := range conf.GetQueueMetrics() {
|
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
|
|
|
Name: MetricsPrefix + "_queue_jobs_enqueued",
|
|
|
|
Value: int64(qm.EnqueuedJobs),
|
|
|
|
Desc: DescJobQueueEnqueued,
|
|
|
|
Labels: []mm.Label{{Key: "queue", Value: qm.Queue}},
|
|
|
|
})
|
|
|
|
|
|
|
|
metrics = append(metrics, &mm.CounterMetric{
|
|
|
|
Name: MetricsPrefix + "_queue_jobs_total_finished",
|
|
|
|
Value: int64(qm.FinishedJobs),
|
|
|
|
Desc: DescJobQueueTotalFinished,
|
|
|
|
Labels: []mm.Label{{Key: "queue", Value: qm.Queue}},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-02-13 01:06:48 +03:00
|
|
|
return &metrics, nil
|
|
|
|
}
|
2021-02-12 20:37:30 +03:00
|
|
|
|
2021-02-13 01:06:48 +03:00
|
|
|
func (h *MetricsHandler) getAdminMetrics(user *models.User) (*mm.Metrics, error) {
|
|
|
|
var metrics mm.Metrics
|
2021-02-12 20:37:30 +03:00
|
|
|
|
2021-02-13 01:06:48 +03:00
|
|
|
if !user.IsAdmin {
|
|
|
|
return nil, errors.New("unauthorized")
|
|
|
|
}
|
2021-02-12 20:37:30 +03:00
|
|
|
|
2021-02-13 01:06:48 +03:00
|
|
|
var totalSeconds int
|
|
|
|
if t, err := h.keyValueSrvc.GetString(conf.KeyLatestTotalTime); err == nil && t != nil && t.Value != "" {
|
|
|
|
if d, err := time.ParseDuration(t.Value); err == nil {
|
|
|
|
totalSeconds = int(d.Seconds())
|
2021-02-12 20:37:30 +03:00
|
|
|
}
|
2021-02-13 01:06:48 +03:00
|
|
|
}
|
2021-02-12 20:37:30 +03:00
|
|
|
|
2021-02-13 01:06:48 +03:00
|
|
|
totalUsers, _ := h.userSrvc.Count()
|
2022-03-19 12:30:32 +03:00
|
|
|
totalHeartbeats, _ := h.heartbeatSrvc.Count(true)
|
2021-02-12 20:37:30 +03:00
|
|
|
|
2021-06-26 13:42:51 +03:00
|
|
|
activeUsers, err := h.userSrvc.GetActive(false)
|
2021-02-13 01:06:48 +03:00
|
|
|
if err != nil {
|
2022-02-17 14:20:22 +03:00
|
|
|
logbuch.Error("failed to retrieve active users for metric - %v", err)
|
2021-02-13 01:06:48 +03:00
|
|
|
return nil, err
|
|
|
|
}
|
2021-02-12 20:37:30 +03:00
|
|
|
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-02-13 01:06:48 +03:00
|
|
|
Name: MetricsPrefix + "_admin_seconds_total",
|
|
|
|
Desc: DescAdminTotalTime,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: int64(totalSeconds),
|
2021-02-13 01:06:48 +03:00
|
|
|
Labels: []mm.Label{},
|
|
|
|
})
|
2021-02-12 20:37:30 +03:00
|
|
|
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-02-13 01:16:20 +03:00
|
|
|
Name: MetricsPrefix + "_admin_heartbeats_total",
|
|
|
|
Desc: DescAdminTotalHeartbeats,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: totalHeartbeats,
|
2021-02-13 01:16:20 +03:00
|
|
|
Labels: []mm.Label{},
|
|
|
|
})
|
|
|
|
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-02-13 01:06:48 +03:00
|
|
|
Name: MetricsPrefix + "_admin_users_total",
|
|
|
|
Desc: DescAdminTotalUsers,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: totalUsers,
|
2021-02-13 01:06:48 +03:00
|
|
|
Labels: []mm.Label{},
|
|
|
|
})
|
|
|
|
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-02-13 01:06:48 +03:00
|
|
|
Name: MetricsPrefix + "_admin_users_active_total",
|
|
|
|
Desc: DescAdminActiveUsers,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: int64(len(activeUsers)),
|
2021-02-13 01:06:48 +03:00
|
|
|
Labels: []mm.Label{},
|
|
|
|
})
|
|
|
|
|
|
|
|
// Count per-user heartbeats
|
|
|
|
|
2021-02-13 13:23:58 +03:00
|
|
|
userCounts, err := h.heartbeatSrvc.CountByUsers(activeUsers)
|
|
|
|
if err != nil {
|
|
|
|
logbuch.Error("failed to count heartbeats for active users", err.Error())
|
|
|
|
return nil, err
|
2021-02-13 01:06:48 +03:00
|
|
|
}
|
|
|
|
|
2021-02-13 13:23:58 +03:00
|
|
|
for _, uc := range userCounts {
|
2022-11-20 12:59:06 +03:00
|
|
|
metrics = append(metrics, &mm.GaugeMetric{
|
2021-02-13 01:16:20 +03:00
|
|
|
Name: MetricsPrefix + "_admin_user_heartbeats_total",
|
|
|
|
Desc: DescAdminUserHeartbeats,
|
2022-03-18 20:20:13 +03:00
|
|
|
Value: uc.Count,
|
2021-02-13 13:23:58 +03:00
|
|
|
Labels: []mm.Label{{Key: "user", Value: uc.User}},
|
2021-02-12 20:37:30 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-02-13 01:06:48 +03:00
|
|
|
return &metrics, nil
|
2021-02-12 20:37:30 +03:00
|
|
|
}
|