Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more prometheus metrics #33307

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions assets/go-licenses.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.4.0
github.com/prometheus/client_golang v1.20.5
github.com/prometheus/common v0.60.1
github.com/quasoft/websspi v1.1.2
github.com/redis/go-redis/v9 v9.7.0
github.com/robfig/cron/v3 v3.0.1
Expand Down Expand Up @@ -241,6 +242,7 @@ require (
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/libdns/libdns v0.2.2 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
Expand Down Expand Up @@ -268,7 +270,6 @@ require (
github.com/pjbgf/sha1cd v0.3.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rhysd/actionlint v1.7.3 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
Expand Down
31 changes: 30 additions & 1 deletion modules/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,39 @@ import (

"code.gitea.io/gitea/modules/setting"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"

_ "gitea.com/go-chi/cache/memcache" //nolint:depguard // memcache plugin for cache, it is required for config "ADAPTER=memcache"
)

var defaultCache StringCache
var (
defaultCache StringCache

// TODO: Combine hit and miss into one
hitCounter = promauto.NewCounter(prometheus.CounterOpts{
Namespace: "gitea",
Help: "Cache count",
Subsystem: "cache",
Name: "response",
ConstLabels: prometheus.Labels{"state": "hit"},
})
missCounter = promauto.NewCounter(prometheus.CounterOpts{
Namespace: "gitea",
Help: "Cache count",
Subsystem: "cache",
Name: "response",
ConstLabels: prometheus.Labels{"state": "miss"},
})
latencyHistogram = promauto.NewHistogram(
prometheus.HistogramOpts{
Namespace: "gitea",
Help: "Cache latency",
Subsystem: "cache",
Name: "duration",
},
)
)

// Init start cache service
func Init() error {
Expand Down
6 changes: 6 additions & 0 deletions modules/cache/string_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package cache
import (
"errors"
"strings"
"time"

"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
Expand Down Expand Up @@ -63,10 +64,15 @@ func (sc *stringCache) Ping() error {
}

func (sc *stringCache) Get(key string) (string, bool) {
start := time.Now()
v := sc.chiCache.Get(key)
elapsed := time.Since(start).Seconds()
latencyHistogram.Observe(elapsed)
if v == nil {
missCounter.Add(1)
return "", false
}
hitCounter.Add(1)
s, ok := v.(string)
return s, ok
}
Expand Down
19 changes: 15 additions & 4 deletions modules/metrics/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@ import (
"code.gitea.io/gitea/modules/setting"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/model"
)

const namespace = "gitea_"

func init() {
model.NameValidationScheme = model.UTF8Validation
}

// Collector implements the prometheus.Collector interface and
// exposes gitea metrics for prometheus
type Collector struct {
Expand Down Expand Up @@ -89,7 +94,7 @@ func NewCollector() Collector {
Issues: prometheus.NewDesc(
namespace+"issues",
"Number of Issues",
nil, nil,
[]string{"state"}, nil,
),
IssuesByLabel: prometheus.NewDesc(
namespace+"issues_by_label",
Expand All @@ -103,12 +108,12 @@ func NewCollector() Collector {
),
IssuesOpen: prometheus.NewDesc(
namespace+"issues_open",
"Number of open Issues",
"DEPRECATED: Use Issues with state: open",
nil, nil,
),
IssuesClosed: prometheus.NewDesc(
namespace+"issues_closed",
"Number of closed Issues",
"DEPRECATED: Use Issues with state: closed",
nil, nil,
),
Labels: prometheus.NewDesc(
Expand Down Expand Up @@ -272,8 +277,14 @@ func (c Collector) Collect(ch chan<- prometheus.Metric) {
ch <- prometheus.MustNewConstMetric(
c.Issues,
prometheus.GaugeValue,
float64(stats.Counter.Issue),
float64(stats.Counter.IssueOpen), "open",
)
ch <- prometheus.MustNewConstMetric(
c.Issues,
prometheus.GaugeValue,
float64(stats.Counter.IssueClosed), "closed",
)

for _, il := range stats.Counter.IssueByLabel {
ch <- prometheus.MustNewConstMetric(
c.IssuesByLabel,
Expand Down
68 changes: 68 additions & 0 deletions routers/common/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ package common
import (
"fmt"
"net/http"
"strconv"
"strings"
"time"

"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/gtprof"
Expand All @@ -19,6 +21,45 @@ import (
"gitea.com/go-chi/session"
"github.com/chi-middleware/proxy"
"github.com/go-chi/chi/v5"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)

const (
httpRequestMethod = "http_request_method"
httpResponseStatusCode = "http_response_status_code"
httpRoute = "http_route"
)

var (
// reqInflightGauge tracks the amount of currently handled requests
reqInflightGauge = promauto.NewGaugeVec(prometheus.GaugeOpts{
Namespace: "http",
Subsystem: "server",
Name: "active_requests",
Help: "Number of active HTTP server requests.",
}, []string{httpRequestMethod})
// reqDurationHistogram tracks the time taken by http request
reqDurationHistogram = promauto.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "http",
Subsystem: "server",
Name: "request_duration",
Help: "Measures the latency of HTTP requests processed by the server",
}, []string{httpRequestMethod, httpResponseStatusCode, httpRoute})
// reqSizeHistogram tracks the size of request
reqSizeHistogram = promauto.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "http",
Subsystem: "server_request",
Name: "body_size",
Help: "Size of HTTP server request bodies.",
}, []string{httpRequestMethod, httpResponseStatusCode, httpRoute})
// respSizeHistogram tracks the size of the response
respSizeHistogram = promauto.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "http",
Subsystem: "server_response",
Name: "body_size",
Help: "Size of HTTP server response bodies.",
}, []string{httpRequestMethod, httpResponseStatusCode, httpRoute})
)

// ProtocolMiddlewares returns HTTP protocol related middlewares, and it provides a global panic recovery
Expand All @@ -38,6 +79,9 @@ func ProtocolMiddlewares() (handlers []any) {
if setting.IsAccessLogEnabled() {
handlers = append(handlers, context.AccessLogger())
}
if setting.Metrics.Enabled {
handlers = append(handlers, RouteMetrics())
}

return handlers
}
Expand Down Expand Up @@ -107,6 +151,30 @@ func ForwardedHeadersHandler(limit int, trustedProxies []string) func(h http.Han
return proxy.ForwardedHeaders(opt)
}

// RouteMetrics instruments http requests and responses
func RouteMetrics() func(h http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
inflight := reqInflightGauge.WithLabelValues(req.Method)
inflight.Inc()
defer inflight.Dec()
start := time.Now()

next.ServeHTTP(resp, req)
m := context.WrapResponseWriter(resp)
route := chi.RouteContext(req.Context()).RoutePattern()
code := strconv.Itoa(m.WrittenStatus())
reqDurationHistogram.WithLabelValues(req.Method, code, route).Observe(time.Since(start).Seconds())
respSizeHistogram.WithLabelValues(req.Method, code, route).Observe(float64(m.WrittenSize()))
size := req.ContentLength
if size < 0 {
size = 0
}
reqSizeHistogram.WithLabelValues(req.Method, code, route).Observe(float64(size))
})
}
}

func Sessioner() func(next http.Handler) http.Handler {
return session.Sessioner(session.Options{
Provider: setting.SessionConfig.Provider,
Expand Down
38 changes: 38 additions & 0 deletions routers/common/middleware_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package common

Check failure on line 1 in routers/common/middleware_test.go

View workflow job for this annotation

GitHub Actions / lint-backend

Copyright did not match check

Check failure on line 1 in routers/common/middleware_test.go

View workflow job for this annotation

GitHub Actions / lint-backend

SPDX-License-Identifier did not match check

Check failure on line 1 in routers/common/middleware_test.go

View workflow job for this annotation

GitHub Actions / lint-go-windows

Copyright did not match check

Check failure on line 1 in routers/common/middleware_test.go

View workflow job for this annotation

GitHub Actions / lint-go-windows

SPDX-License-Identifier did not match check

import (
"net/http"
"net/http/httptest"
"testing"
"time"

"github.com/go-chi/chi/v5"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestMetricsMiddlewere(t *testing.T) {
middleware := RouteMetrics()
r := chi.NewRouter()
r.Use(middleware)
r.Get("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("test"))
time.Sleep(5 * time.Millisecond)
}))
testServer := httptest.NewServer(r)
// Check all defined metrics
verify := func(i int) {
assert.Equal(t, testutil.CollectAndCount(reqDurationHistogram, "http_server_request_duration"), i)
assert.Equal(t, testutil.CollectAndCount(reqSizeHistogram, "http_server_request_body_size"), i)
assert.Equal(t, testutil.CollectAndCount(respSizeHistogram, "http_server_response_body_size"), i)
assert.Equal(t, testutil.CollectAndCount(reqInflightGauge, "http_server_active_requests"), i)
}

// Check they don't exist before making a request
verify(0)
_, err := http.Get(testServer.URL)
require.NoError(t, err)
// Check they do exist after making the request
verify(1)
}
2 changes: 2 additions & 0 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import (
"github.com/go-chi/cors"
"github.com/klauspost/compress/gzhttp"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
)

var GzipMinSize = 1400 // min size to compress for the body size of response
Expand Down Expand Up @@ -258,6 +259,7 @@ func Routes() *web.Router {
}

if setting.Metrics.Enabled {
prometheus.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{Namespace: "gitea"}))
prometheus.MustRegister(metrics.NewCollector())
routes.Get("/metrics", append(mid, Metrics)...)
}
Expand Down
13 changes: 13 additions & 0 deletions services/migrations/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ import (
base "code.gitea.io/gitea/modules/migration"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"

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

var (
repoMigrationsInflightGauge = promauto.NewGauge(prometheus.GaugeOpts{Namespace: "gitea", Subsystem: "repository", Name: "inflight_migrations", Help: "Number of inflight repository migrations"})
repoMigrationsCounter = promauto.NewGaugeVec(prometheus.GaugeOpts{Namespace: "gitea", Subsystem: "repository", Name: "migrations", Help: "Total migrations"}, []string{"result"})
)

// MigrateOptions is equal to base.MigrateOptions
Expand Down Expand Up @@ -124,6 +132,9 @@ func MigrateRepository(ctx context.Context, doer *user_model.User, ownerName str
return nil, err
}

repoMigrationsInflightGauge.Inc()
defer repoMigrationsInflightGauge.Dec()

uploader := NewGiteaLocalUploader(ctx, doer, ownerName, opts.RepoName)
uploader.gitServiceType = opts.GitServiceType

Expand All @@ -134,8 +145,10 @@ func MigrateRepository(ctx context.Context, doer *user_model.User, ownerName str
if err2 := system_model.CreateRepositoryNotice(fmt.Sprintf("Migrate repository from %s failed: %v", opts.OriginalURL, err)); err2 != nil {
log.Error("create respotiry notice failed: ", err2)
}
repoMigrationsCounter.WithLabelValues("fail").Inc()
return nil, err
}
repoMigrationsCounter.WithLabelValues("success").Inc()
return uploader.repo, nil
}

Expand Down
Loading