Path: blob/main/components/openvsx-proxy/pkg/prometheus.go
2498 views
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package pkg56import (7"fmt"8"net/http"9"strings"1011"github.com/gitpod-io/gitpod/common-go/log"12"github.com/prometheus/client_golang/prometheus"13"github.com/prometheus/client_golang/prometheus/collectors"14"github.com/prometheus/client_golang/prometheus/promhttp"15)1617type Prometheus struct {18reg *prometheus.Registry19BackupCacheHitCounter prometheus.Counter20BackupCacheMissCounter prometheus.Counter21BackupCacheServeCounter prometheus.Counter22RegularCacheHitServeCounter prometheus.Counter23RegularCacheMissCounter prometheus.Counter24RequestsCounter *prometheus.CounterVec25DurationOverallHistogram prometheus.Histogram26DurationRequestProcessingHistogram prometheus.Histogram27DurationUpstreamCallHistorgram prometheus.Histogram28DurationResponseProcessingHistogram prometheus.Histogram29}3031func (p *Prometheus) Start(cfg *Config) {32p.reg = prometheus.NewRegistry()3334if cfg.PrometheusAddr != "" {35p.reg.MustRegister(36collectors.NewGoCollector(),37collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),38)3940handler := http.NewServeMux()41handler.Handle("/metrics", promhttp.HandlerFor(p.reg, promhttp.HandlerOpts{}))4243go func() {44err := http.ListenAndServe(cfg.PrometheusAddr, handler)45if err != nil {46log.WithError(err).Error("Prometheus metrics server failed")47}48}()49log.WithField("addr", cfg.PrometheusAddr).Debug("started Prometheus metrics server")50}5152p.createMetrics()53collectors := []prometheus.Collector{54p.BackupCacheHitCounter,55p.BackupCacheMissCounter,56p.BackupCacheServeCounter,57p.RegularCacheHitServeCounter,58p.RegularCacheMissCounter,59p.RequestsCounter,60p.DurationOverallHistogram,61p.DurationRequestProcessingHistogram,62p.DurationUpstreamCallHistorgram,63p.DurationResponseProcessingHistogram,64}65for _, c := range collectors {66err := p.reg.Register(c)67if err != nil {68log.WithError(err).Error("register Prometheus metric failed")69}70}71}7273func (p *Prometheus) createMetrics() {74namespace := "gitpod"75subsystem := "openvsx_proxy"76p.BackupCacheHitCounter = prometheus.NewCounter(prometheus.CounterOpts{77Namespace: namespace,78Subsystem: subsystem,79Name: "backup_cache_hit_total",80Help: "The total amount of requests where we had a cached response that we could use as backup when the upstream server is down.",81})82p.BackupCacheMissCounter = prometheus.NewCounter(prometheus.CounterOpts{83Namespace: namespace,84Subsystem: subsystem,85Name: "backup_cache_miss_total",86Help: "The total amount of requests where we haven't had a cached response that we could use as backup when the upstream server is down.",87})88p.BackupCacheServeCounter = prometheus.NewCounter(prometheus.CounterOpts{89Namespace: namespace,90Subsystem: subsystem,91Name: "backup_cache_serve_total",92Help: "The total amount of requests where we actually answered with a cached response because the upstream server is down.",93})94p.RegularCacheHitServeCounter = prometheus.NewCounter(prometheus.CounterOpts{95Namespace: namespace,96Subsystem: subsystem,97Name: "regular_cache_hit_and_serve_total",98Help: "The total amount or requests where we answered with a cached response for performance reasons.",99})100p.RegularCacheMissCounter = prometheus.NewCounter(prometheus.CounterOpts{101Namespace: namespace,102Subsystem: subsystem,103Name: "regular_cache_miss_total",104Help: "The total amount or requests we haven't had a young enough cached requests to use it for performance reasons.",105})106p.RequestsCounter = prometheus.NewCounterVec(prometheus.CounterOpts{107Namespace: namespace,108Subsystem: subsystem,109Name: "requests_total",110Help: "The total amount of requests by response status.",111}, []string{"status", "path"})112p.DurationOverallHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{113Namespace: namespace,114Subsystem: subsystem,115Name: "duration_overall_seconds",116Help: "The duration in seconds of the HTTP requests.",117})118p.DurationRequestProcessingHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{119Namespace: namespace,120Subsystem: subsystem,121Name: "duration_request_processing_seconds",122Help: "The duration in seconds of the processing of the HTTP requests before we call the upstream.",123})124p.DurationUpstreamCallHistorgram = prometheus.NewHistogram(prometheus.HistogramOpts{125Namespace: namespace,126Subsystem: subsystem,127Name: "duration_upstream_call_seconds",128Help: "The duration in seconds of the call of the upstream server.",129})130p.DurationResponseProcessingHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{131Namespace: namespace,132Subsystem: subsystem,133Name: "duration_response_processing_seconds",134Help: "The duration in seconds of the processing of the HTTP responses after we have called the upstream.",135})136}137138var expectedPaths = map[string]struct{}{139"/api/-/query": {},140"/vscode/asset": {},141"/vscode/gallery/extensionquery": {},142"/vscode/gallery/itemName": {},143"/vscode/gallery/publishers": {},144}145146func (p *Prometheus) IncStatusCounter(r *http.Request, status string) {147path := r.URL.Path148if strings.HasPrefix(path, "/vscode/asset/") {149// remove everything after /vscode/asset/ to decrease the unique numbers of paths150path = path[:len("/vscode/asset/")]151}152if strings.HasPrefix(path, "/vscode/gallery/itemName/") {153// remove everything after /vscode/gallery/itemName/ to decrease the unique numbers of paths154path = path[:len("/vscode/gallery/itemName/")]155}156// just to make sure that a long path doesn't slip through cut after 3 segements157// since path starts with a / the first segment is an emtpy string, therefore len > 4 and not len > 3158if s := strings.SplitN(path, "/", 5); len(s) > 4 {159path = strings.Join(s[:4], "/")160}161// don't track unexepected paths (e.g. requests from crawlers/bots)162if _, ok := expectedPaths[strings.TrimSuffix(path, "/")]; !ok {163log.WithField("path", path).Debug("unexpected path")164path = "(other)"165}166p.RequestsCounter.WithLabelValues(status, fmt.Sprintf("%s %s", r.Method, path)).Inc()167}168169170