Skip to content

Commit

Permalink
Prometheus metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
Charles-Antoine Mathieu committed Aug 3, 2023
1 parent 67a8589 commit 0be1fb5
Show file tree
Hide file tree
Showing 203 changed files with 34,752 additions and 26 deletions.
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ require (
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0
github.com/pilagod/gorm-cursor-paginator/v2 v2.4.1
github.com/prometheus/client_golang v1.16.0
github.com/prometheus/client_model v0.3.0
github.com/root-gg/logger v0.0.0-20150501173826-5d9a47a35312
github.com/root-gg/utils v0.0.0-20201113182430-4cef83d8cdcf
github.com/spf13/cobra v1.7.0
Expand All @@ -34,13 +36,16 @@ require (
gorm.io/driver/postgres v1.5.2
gorm.io/driver/sqlite v1.5.2
gorm.io/gorm v1.25.2
gorm.io/plugin/prometheus v0.0.0-20230504115745-1aec2356381b
)

require (
cloud.google.com/go v0.110.3 // indirect
cloud.google.com/go/compute v1.20.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.1.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.9.0 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect
Expand All @@ -62,11 +67,14 @@ require (
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-sqlite3 v1.14.17 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rs/xid v1.5.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
Expand Down
376 changes: 374 additions & 2 deletions go.sum

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions server/common/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ type Configuration struct {
DebugRequests bool `json:"-"`
LogLevel string `json:"-"`

ListenAddress string `json:"-"`
ListenPort int `json:"-"`
Path string `json:"-"`
ListenAddress string `json:"-"`
ListenPort int `json:"-"`
MetricsAddress string `json:"-"`
MetricsPort int `json:"-"`
Path string `json:"-"`

MaxFileSizeStr string `json:"-"`
MaxFileSize int64 `json:"maxFileSize"`
Expand Down Expand Up @@ -109,6 +111,8 @@ func NewConfiguration() (config *Configuration) {

config.ListenAddress = "0.0.0.0"
config.ListenPort = 8080
config.MetricsAddress = "0.0.0."
config.MetricsPort = 0
config.EnhancedWebSecurity = false
config.SessionTimeout = "365d"

Expand Down
183 changes: 183 additions & 0 deletions server/common/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package common

import (
"strconv"
"time"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
)

// PlikMetrics handles Prometheus metrics
type PlikMetrics struct {
reg *prometheus.Registry

httpCounter *prometheus.CounterVec

uploads prometheus.Gauge
anonymousUploads prometheus.Gauge
users prometheus.Gauge
files prometheus.Gauge
size prometheus.Gauge
anonymousSize prometheus.Gauge

serverStatsRefreshDuration prometheus.Histogram
cleaningDuration prometheus.Histogram

cleaningRemovedUploads prometheus.Counter
cleaningDeletedFiles prometheus.Counter
cleaningDeletedUploads prometheus.Counter
cleaningOrphanFiles prometheus.Counter
cleaningOrphanTokens prometheus.Counter

lastStatsRefresh prometheus.Gauge
lastCleaning prometheus.Gauge
}

// NewPlikMetrics initialize Plik metrics
func NewPlikMetrics() (m *PlikMetrics) {
m = &PlikMetrics{reg: prometheus.NewRegistry()}

// Runtime metrics
m.reg.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))
m.reg.MustRegister(collectors.NewGoCollector())

m.httpCounter = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "plik_http_request_total",
Help: "Count of HTTP requests",
}, []string{"method", "path", "code"})
m.reg.MustRegister(m.httpCounter)

m.uploads = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "plik_uploads_count",
Help: "Total number of uploads in the database",
})
m.reg.MustRegister(m.uploads)

m.anonymousUploads = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "plik_anonymous_uploads_count",
Help: "Total number of anonymous uploads in the database",
})
m.reg.MustRegister(m.anonymousUploads)

m.users = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "plik_users_count",
Help: "Total number of users in the database",
})
m.reg.MustRegister(m.users)

m.files = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "plik_files_count",
Help: "Total number of files in the database",
})
m.reg.MustRegister(m.files)

m.anonymousSize = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "plik_anonymous_size_bytes",
Help: "Total anonymous upload size in the database",
})
m.reg.MustRegister(m.anonymousSize)

m.size = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "plik_size_bytes",
Help: "Total upload size in the database",
})
m.reg.MustRegister(m.size)

m.serverStatsRefreshDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "plik_server_stats_refresh_duration_second",
Help: "Duration of server stats refresh requests",
Buckets: prometheus.ExponentialBucketsRange((10 * time.Millisecond).Seconds(), (10 * time.Second).Seconds(), 20),
})
m.reg.MustRegister(m.serverStatsRefreshDuration)

m.cleaningDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "plik_cleaning_duration_second",
Help: "Duration of cleaning runs",
Buckets: prometheus.ExponentialBucketsRange((10 * time.Millisecond).Seconds(), (600 * time.Second).Seconds(), 20),
})
m.reg.MustRegister(m.cleaningDuration)

m.cleaningRemovedUploads = prometheus.NewCounter(prometheus.CounterOpts{
Name: "plik_cleaning_removed_uploads",
Help: "Cleaning routine removed uploads",
})
m.reg.MustRegister(m.cleaningRemovedUploads)

m.cleaningDeletedFiles = prometheus.NewCounter(prometheus.CounterOpts{
Name: "plik_cleaning_deleted_files",
Help: "Cleaning routine deleted files",
})
m.reg.MustRegister(m.cleaningDeletedFiles)

m.cleaningDeletedUploads = prometheus.NewCounter(prometheus.CounterOpts{
Name: "plik_cleaning_deleted_uploads",
Help: "Cleaning routine deleted uploads",
})
m.reg.MustRegister(m.cleaningDeletedUploads)

m.cleaningOrphanFiles = prometheus.NewCounter(prometheus.CounterOpts{
Name: "plik_cleaning_removed_orphan_files",
Help: "Cleaning routine removed orphan files",
})
m.reg.MustRegister(m.cleaningOrphanFiles)

m.cleaningOrphanTokens = prometheus.NewCounter(prometheus.CounterOpts{
Name: "plik_cleaning_removed_orphan_tokens",
Help: "Cleaning routine removed orphan tokens",
})
m.reg.MustRegister(m.cleaningOrphanTokens)

m.lastStatsRefresh = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "plik_last_stats_refresh_timestamp",
Help: "Timestamp of the last server stats refresh",
})
m.reg.MustRegister(m.lastStatsRefresh)

m.lastCleaning = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "plik_last_cleaning_timestamp",
Help: "Timestamp of the last server cleaning",
})
m.reg.MustRegister(m.lastCleaning)

return m
}

// GetRegistry returns the dedicated Prometheus Registry
func (m *PlikMetrics) GetRegistry() *prometheus.Registry {
return m.reg
}

// UpdateHTTPMetrics update metrics about HTTP requests/responses
func (m *PlikMetrics) UpdateHTTPMetrics(method string, path string, statusCode int, elapsed time.Duration) {
m.httpCounter.WithLabelValues(method, path, strconv.Itoa(statusCode)).Add(1)
}

// UpdateServerStatistics update metrics about plik metadata
func (m *PlikMetrics) UpdateServerStatistics(stats *ServerStats, elapsed time.Duration) {
m.uploads.Set(float64(stats.Uploads))
m.anonymousUploads.Set(float64(stats.AnonymousUploads))
m.files.Set(float64(stats.Files))
m.size.Set(float64(stats.TotalSize))
m.anonymousSize.Set(float64(stats.AnonymousSize))
m.users.Set(float64(stats.Users))
m.lastStatsRefresh.Set(float64(time.Now().Second()))
m.serverStatsRefreshDuration.Observe(elapsed.Seconds())
}

// UpdateCleaningStatistics update metrics about plik cleaning
func (m *PlikMetrics) UpdateCleaningStatistics(stats *CleaningStats, elapsed time.Duration) {
m.cleaningRemovedUploads.Add(float64(stats.RemovedUploads))
m.cleaningDeletedFiles.Add(float64(stats.DeletedFiles))
m.cleaningDeletedUploads.Add(float64(stats.DeletedUploads))
m.cleaningOrphanFiles.Add(float64(stats.OrphanFilesCleaned))
m.cleaningOrphanTokens.Add(float64(stats.OrphanTokensCleaned))
m.lastCleaning.Set(float64(time.Now().Second()))
m.cleaningDuration.Observe(elapsed.Seconds())
}

// Register a set of collectors to the dedicated Prometheus registry
// This can be used by modules to register dedicated metrics
func (m *PlikMetrics) Register(collectors ...prometheus.Collector) {
m.reg.MustRegister(collectors...)
}
144 changes: 144 additions & 0 deletions server/common/metrics_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package common

import (
"strconv"
"testing"
"time"

dto "github.com/prometheus/client_model/go"
"github.com/stretchr/testify/require"
)

func TestNewPlikMetrics(t *testing.T) {
m := NewPlikMetrics()
require.NotNil(t, m)

require.NotNil(t, m.reg)

require.NotNil(t, m.httpCounter)

require.NotNil(t, m.uploads)
require.NotNil(t, m.anonymousUploads)
require.NotNil(t, m.users)
require.NotNil(t, m.files)
require.NotNil(t, m.size)
require.NotNil(t, m.anonymousSize)

require.NotNil(t, m.serverStatsRefreshDuration)
require.NotNil(t, m.cleaningDuration)

require.NotNil(t, m.cleaningRemovedUploads)
require.NotNil(t, m.cleaningDeletedFiles)
require.NotNil(t, m.cleaningDeletedUploads)
require.NotNil(t, m.cleaningOrphanFiles)
require.NotNil(t, m.cleaningOrphanTokens)

require.NotNil(t, m.lastStatsRefresh)
require.NotNil(t, m.lastCleaning)
}

func TestGetRegistry(t *testing.T) {
m := NewPlikMetrics()
require.Equal(t, m.reg, m.GetRegistry())
}

func TestUpdateHttpMetrics(t *testing.T) {
m := NewPlikMetrics()
m.UpdateHTTPMetrics("GET", "/upload", 200, time.Second)
counter, err := m.httpCounter.GetMetricWithLabelValues("GET", "/upload", strconv.Itoa(200))
require.NoError(t, err)

metric := &dto.Metric{}
err = counter.Write(metric)
require.NoError(t, err)

require.Equal(t, float64(1), *metric.GetCounter().Value)
}

func TestUpdateServerStatistics(t *testing.T) {
m := NewPlikMetrics()
stats := &ServerStats{
Users: 1,
Uploads: 2,
AnonymousUploads: 3,
TotalSize: 4,
AnonymousSize: 5,
}
m.UpdateServerStatistics(stats, 1*time.Second)

metric := &dto.Metric{}

err := m.uploads.Write(metric)
require.NoError(t, err)
require.Equal(t, float64(stats.Uploads), *metric.GetGauge().Value)

err = m.anonymousUploads.Write(metric)
require.NoError(t, err)
require.Equal(t, float64(stats.AnonymousUploads), *metric.GetGauge().Value)

err = m.files.Write(metric)
require.NoError(t, err)
require.Equal(t, float64(stats.Files), *metric.GetGauge().Value)

err = m.size.Write(metric)
require.NoError(t, err)
require.Equal(t, float64(stats.TotalSize), *metric.GetGauge().Value)

err = m.anonymousSize.Write(metric)
require.NoError(t, err)
require.Equal(t, float64(stats.AnonymousSize), *metric.GetGauge().Value)

err = m.users.Write(metric)
require.NoError(t, err)
require.Equal(t, float64(stats.Users), *metric.GetGauge().Value)

err = m.lastStatsRefresh.Write(metric)
require.NoError(t, err)
require.NotZero(t, *metric.GetGauge().Value)

err = m.serverStatsRefreshDuration.Write(metric)
require.NoError(t, err)
require.Equal(t, uint64(1), *metric.GetHistogram().SampleCount)
}

func TestUpdateCleaningStatistics(t *testing.T) {
m := NewPlikMetrics()
stats := &CleaningStats{
RemovedUploads: 1,
DeletedFiles: 2,
DeletedUploads: 3,
OrphanFilesCleaned: 4,
OrphanTokensCleaned: 5,
}
m.UpdateCleaningStatistics(stats, 1*time.Second)

metric := &dto.Metric{}

err := m.cleaningRemovedUploads.Write(metric)
require.NoError(t, err)
require.Equal(t, float64(stats.RemovedUploads), *metric.GetCounter().Value)

err = m.cleaningDeletedFiles.Write(metric)
require.NoError(t, err)
require.Equal(t, float64(stats.DeletedFiles), *metric.GetCounter().Value)

err = m.cleaningDeletedUploads.Write(metric)
require.NoError(t, err)
require.Equal(t, float64(stats.DeletedUploads), *metric.GetCounter().Value)

err = m.cleaningOrphanFiles.Write(metric)
require.NoError(t, err)
require.Equal(t, float64(stats.OrphanFilesCleaned), *metric.GetCounter().Value)

err = m.cleaningOrphanTokens.Write(metric)
require.NoError(t, err)
require.Equal(t, float64(stats.OrphanTokensCleaned), *metric.GetCounter().Value)

err = m.lastCleaning.Write(metric)
require.NoError(t, err)
require.NotZero(t, *metric.GetGauge().Value)

err = m.cleaningDuration.Write(metric)
require.NoError(t, err)
require.Equal(t, uint64(1), *metric.GetHistogram().SampleCount)
}
Loading

0 comments on commit 0be1fb5

Please sign in to comment.