diff --git a/main.go b/main.go index 96f7cbd..6b00d69 100644 --- a/main.go +++ b/main.go @@ -58,6 +58,7 @@ var ( summaryRepository repositories.ISummaryRepository keyValueRepository repositories.IKeyValueRepository diagnosticsRepository repositories.IDiagnosticsRepository + metricsRepository *repositories.MetricsRepository ) var ( @@ -149,6 +150,7 @@ func main() { summaryRepository = repositories.NewSummaryRepository(db) keyValueRepository = repositories.NewKeyValueRepository(db) diagnosticsRepository = repositories.NewDiagnosticsRepository(db) + metricsRepository = repositories.NewMetricsRepository(db) // Services mailService = mail.NewMailService() @@ -178,7 +180,7 @@ func main() { healthApiHandler := api.NewHealthApiHandler(db) heartbeatApiHandler := api.NewHeartbeatApiHandler(userService, heartbeatService, languageMappingService) summaryApiHandler := api.NewSummaryApiHandler(userService, summaryService) - metricsHandler := api.NewMetricsHandler(userService, summaryService, heartbeatService, keyValueService) + metricsHandler := api.NewMetricsHandler(userService, summaryService, heartbeatService, keyValueService, metricsRepository) diagnosticsHandler := api.NewDiagnosticsApiHandler(userService, diagnosticsService) avatarHandler := api.NewAvatarHandler() diff --git a/models/metrics/counter_metric.go b/models/metrics/counter_metric.go index 7c9f217..def7531 100644 --- a/models/metrics/counter_metric.go +++ b/models/metrics/counter_metric.go @@ -4,7 +4,7 @@ import "fmt" type CounterMetric struct { Name string - Value int + Value int64 Desc string Labels Labels } diff --git a/repositories/metrics.go b/repositories/metrics.go new file mode 100644 index 0000000..8c57c07 --- /dev/null +++ b/repositories/metrics.go @@ -0,0 +1,43 @@ +package repositories + +import ( + "github.com/muety/wakapi/config" + "gorm.io/gorm" +) + +type MetricsRepository struct { + config *config.Config + db *gorm.DB +} + +const sizeTplMysql = ` +SELECT SUM(data_length + index_length) +FROM information_schema.tables +WHERE table_schema = ? +GROUP BY table_schema` + +const sizeTplPostgres = `SELECT pg_database_size('%s');` + +const sizeTplSqlite = ` +SELECT page_count * page_size as size +FROM pragma_page_count(), pragma_page_size();` + +func NewMetricsRepository(db *gorm.DB) *MetricsRepository { + return &MetricsRepository{config: config.Get(), db: db} +} + +func (srv *MetricsRepository) GetDatabaseSize() (size int64, err error) { + cfg := srv.config.Db + + query := srv.db.Raw("SELECT 0") + if cfg.IsMySQL() { + query = srv.db.Raw(sizeTplMysql, cfg.Name) + } else if cfg.IsPostgres() { + query = srv.db.Raw(sizeTplPostgres, cfg.Name) + } else if cfg.IsSQLite() { + query = srv.db.Raw(sizeTplSqlite) + } + + err = query.Scan(&size).Error + return size, err +} diff --git a/routes/api/metrics.go b/routes/api/metrics.go index aedadfa..c63f2c1 100644 --- a/routes/api/metrics.go +++ b/routes/api/metrics.go @@ -9,6 +9,7 @@ import ( "github.com/muety/wakapi/models" v1 "github.com/muety/wakapi/models/compat/wakatime/v1" mm "github.com/muety/wakapi/models/metrics" + "github.com/muety/wakapi/repositories" "github.com/muety/wakapi/services" "github.com/muety/wakapi/utils" "net/http" @@ -39,6 +40,7 @@ const ( DescMemAllocTotal = "Total number of bytes allocated for heap" DescMemSysTotal = "Total number of bytes obtained from the OS" DescGoroutines = "Total number of running goroutines" + DescDatabaseSize = "Total database size in bytes" ) type MetricsHandler struct { @@ -47,14 +49,16 @@ type MetricsHandler struct { summarySrvc services.ISummaryService heartbeatSrvc services.IHeartbeatService keyValueSrvc services.IKeyValueService + metricsRepo *repositories.MetricsRepository } -func NewMetricsHandler(userService services.IUserService, summaryService services.ISummaryService, heartbeatService services.IHeartbeatService, keyValueService services.IKeyValueService) *MetricsHandler { +func NewMetricsHandler(userService services.IUserService, summaryService services.ISummaryService, heartbeatService services.IHeartbeatService, keyValueService services.IKeyValueService, metricsRepo *repositories.MetricsRepository) *MetricsHandler { return &MetricsHandler{ userSrvc: userService, summarySrvc: summaryService, heartbeatSrvc: heartbeatService, keyValueSrvc: keyValueService, + metricsRepo: metricsRepo, config: conf.Get(), } } @@ -141,21 +145,21 @@ func (h *MetricsHandler) getUserMetrics(user *models.User) (*mm.Metrics, error) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_cumulative_seconds_total", Desc: DescAllTime, - Value: int(v1.NewAllTimeFrom(summaryAllTime).Data.TotalSeconds), + Value: int64(v1.NewAllTimeFrom(summaryAllTime).Data.TotalSeconds), Labels: []mm.Label{}, }) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_seconds_total", Desc: DescTotal, - Value: int(summaryToday.TotalTime().Seconds()), + Value: int64(summaryToday.TotalTime().Seconds()), Labels: []mm.Label{}, }) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_heartbeats_total", Desc: DescHeartbeats, - Value: int(heartbeatCount), + Value: int64(heartbeatCount), Labels: []mm.Label{}, }) @@ -163,7 +167,7 @@ func (h *MetricsHandler) getUserMetrics(user *models.User) (*mm.Metrics, error) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_project_seconds_total", Desc: DescProjects, - Value: int(summaryToday.TotalTimeByKey(models.SummaryProject, p.Key).Seconds()), + Value: int64(summaryToday.TotalTimeByKey(models.SummaryProject, p.Key).Seconds()), Labels: []mm.Label{{Key: "name", Value: p.Key}}, }) } @@ -172,7 +176,7 @@ func (h *MetricsHandler) getUserMetrics(user *models.User) (*mm.Metrics, error) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_language_seconds_total", Desc: DescLanguages, - Value: int(summaryToday.TotalTimeByKey(models.SummaryLanguage, l.Key).Seconds()), + Value: int64(summaryToday.TotalTimeByKey(models.SummaryLanguage, l.Key).Seconds()), Labels: []mm.Label{{Key: "name", Value: l.Key}}, }) } @@ -181,7 +185,7 @@ func (h *MetricsHandler) getUserMetrics(user *models.User) (*mm.Metrics, error) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_editor_seconds_total", Desc: DescEditors, - Value: int(summaryToday.TotalTimeByKey(models.SummaryEditor, e.Key).Seconds()), + Value: int64(summaryToday.TotalTimeByKey(models.SummaryEditor, e.Key).Seconds()), Labels: []mm.Label{{Key: "name", Value: e.Key}}, }) } @@ -190,7 +194,7 @@ func (h *MetricsHandler) getUserMetrics(user *models.User) (*mm.Metrics, error) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_operating_system_seconds_total", Desc: DescOperatingSystems, - Value: int(summaryToday.TotalTimeByKey(models.SummaryOS, o.Key).Seconds()), + Value: int64(summaryToday.TotalTimeByKey(models.SummaryOS, o.Key).Seconds()), Labels: []mm.Label{{Key: "name", Value: o.Key}}, }) } @@ -199,7 +203,7 @@ func (h *MetricsHandler) getUserMetrics(user *models.User) (*mm.Metrics, error) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_machine_seconds_total", Desc: DescMachines, - Value: int(summaryToday.TotalTimeByKey(models.SummaryMachine, m.Key).Seconds()), + Value: int64(summaryToday.TotalTimeByKey(models.SummaryMachine, m.Key).Seconds()), Labels: []mm.Label{{Key: "name", Value: m.Key}}, }) } @@ -208,7 +212,7 @@ func (h *MetricsHandler) getUserMetrics(user *models.User) (*mm.Metrics, error) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_label_seconds_total", Desc: DescLabels, - Value: int(summaryToday.TotalTimeByKey(models.SummaryLabel, m.Key).Seconds()), + Value: int64(summaryToday.TotalTimeByKey(models.SummaryLabel, m.Key).Seconds()), Labels: []mm.Label{{Key: "name", Value: m.Key}}, }) } @@ -220,21 +224,34 @@ func (h *MetricsHandler) getUserMetrics(user *models.User) (*mm.Metrics, error) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_goroutines_total", Desc: DescGoroutines, - Value: runtime.NumGoroutine(), + Value: int64(runtime.NumGoroutine()), Labels: []mm.Label{}, }) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_mem_alloc_total", Desc: DescMemAllocTotal, - Value: int(memStats.Alloc), + Value: int64(memStats.Alloc), Labels: []mm.Label{}, }) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_mem_sys_total", Desc: DescMemSysTotal, - Value: int(memStats.Sys), + 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) + } + + metrics = append(metrics, &mm.CounterMetric{ + Name: MetricsPrefix + "_db_total_bytes", + Desc: DescDatabaseSize, + Value: dbSize, Labels: []mm.Label{}, }) @@ -267,28 +284,28 @@ func (h *MetricsHandler) getAdminMetrics(user *models.User) (*mm.Metrics, error) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_admin_seconds_total", Desc: DescAdminTotalTime, - Value: totalSeconds, + Value: int64(totalSeconds), Labels: []mm.Label{}, }) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_admin_heartbeats_total", Desc: DescAdminTotalHeartbeats, - Value: int(totalHeartbeats), + Value: totalHeartbeats, Labels: []mm.Label{}, }) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_admin_users_total", Desc: DescAdminTotalUsers, - Value: int(totalUsers), + Value: totalUsers, Labels: []mm.Label{}, }) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_admin_users_active_total", Desc: DescAdminActiveUsers, - Value: len(activeUsers), + Value: int64(len(activeUsers)), Labels: []mm.Label{}, }) @@ -304,7 +321,7 @@ func (h *MetricsHandler) getAdminMetrics(user *models.User) (*mm.Metrics, error) metrics = append(metrics, &mm.CounterMetric{ Name: MetricsPrefix + "_admin_user_heartbeats_total", Desc: DescAdminUserHeartbeats, - Value: int(uc.Count), + Value: uc.Count, Labels: []mm.Label{{Key: "user", Value: uc.User}}, }) }