diff --git a/components/usage/pkg/contentservice/client.go b/components/usage/pkg/contentservice/client.go index 8f45e0958120ad..c5174d0a2ca056 100644 --- a/components/usage/pkg/contentservice/client.go +++ b/components/usage/pkg/contentservice/client.go @@ -11,6 +11,7 @@ import ( "encoding/json" "fmt" "net/http" + "time" "github.com/gitpod-io/gitpod/common-go/log" "github.com/gitpod-io/gitpod/content-service/api" @@ -29,7 +30,12 @@ func New(service api.UsageReportServiceClient) *Client { return &Client{service: service} } -func (c *Client) UploadUsageReport(ctx context.Context, filename string, report UsageReport) error { +func (c *Client) UploadUsageReport(ctx context.Context, filename string, report UsageReport) (err error) { + start := time.Now() + defer func() { + observeReportUploadDuration(time.Since(start), err) + }() + uploadURLResp, err := c.service.UploadURL(ctx, &api.UsageReportUploadURLRequest{Name: filename}) if err != nil { return fmt.Errorf("failed to get upload URL from usage report service: %w", err) @@ -67,7 +73,12 @@ func (c *Client) UploadUsageReport(ctx context.Context, filename string, report return nil } -func (c *Client) DownloadUsageReport(ctx context.Context, filename string) (UsageReport, error) { +func (c *Client) DownloadUsageReport(ctx context.Context, filename string) (report UsageReport, err error) { + start := time.Now() + defer func() { + observeReportDownloadDuration(time.Since(start), err) + }() + downloadURlResp, err := c.service.DownloadURL(ctx, &api.UsageReportDownloadURLRequest{ Name: filename, }) @@ -104,7 +115,6 @@ func (c *Client) DownloadUsageReport(ctx context.Context, filename string) (Usag defer decompressor.Close() decoder := json.NewDecoder(decompressor) - var report UsageReport if err := decoder.Decode(&report); err != nil { return UsageReport{}, fmt.Errorf("failed to deserialize report: %w", err) } diff --git a/components/usage/pkg/contentservice/metrics.go b/components/usage/pkg/contentservice/metrics.go new file mode 100644 index 00000000000000..076b08c8333f88 --- /dev/null +++ b/components/usage/pkg/contentservice/metrics.go @@ -0,0 +1,63 @@ +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. +// Licensed under the GNU Affero General Public License (AGPL). +// See License-AGPL.txt in the project root for license information. + +package contentservice + +import ( + "fmt" + "github.com/prometheus/client_golang/prometheus" + "time" +) + +const ( + namespace = "gitpod" + subsystem = "usage" +) + +var ( + reportUploadDurationSeconds = prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: subsystem, + Name: "report_upload_duration_seconds", + Help: "Histogram of time it takes (in seconds) to upload a usage report to content service", + }, []string{"outcome"}) + + reportDownloadDurationSeconds = prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: subsystem, + Name: "report_download_duration_seconds", + Help: "Histogram of time it takes (in seconds) to download usage report from content service", + }, []string{"outcome"}) +) + +func RegisterMetrics(reg *prometheus.Registry) error { + metrics := []prometheus.Collector{ + reportUploadDurationSeconds, + reportDownloadDurationSeconds, + } + for _, metric := range metrics { + err := reg.Register(metric) + if err != nil { + return fmt.Errorf("failed to register metric: %w", err) + } + } + + return nil +} + +func observeReportUploadDuration(d time.Duration, err error) { + outcome := "ok" + if err != nil { + outcome = "error" + } + reportUploadDurationSeconds.WithLabelValues(outcome).Observe(d.Seconds()) +} + +func observeReportDownloadDuration(d time.Duration, err error) { + outcome := "ok" + if err != nil { + outcome = "error" + } + reportDownloadDurationSeconds.WithLabelValues(outcome).Observe(d.Seconds()) +} diff --git a/components/usage/pkg/server/server.go b/components/usage/pkg/server/server.go index afa239d67854cf..cc700c47196eb8 100644 --- a/components/usage/pkg/server/server.go +++ b/components/usage/pkg/server/server.go @@ -130,6 +130,10 @@ func Start(cfg Config) error { log.Info("No controller schedule specified, controller will be disabled.") } + err = contentservice.RegisterMetrics(srv.MetricsRegistry()) + if err != nil { + return fmt.Errorf("failed to register content service metrics: %w", err) + } var contentService contentservice.Interface = &contentservice.NoOpClient{} if cfg.ContentServiceAddress != "" { contentServiceConn, err := grpc.Dial(cfg.ContentServiceAddress, grpc.WithTransportCredentials(insecure.NewCredentials()))