-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
impr: BKTCLT-34 add Prometheus metrics
Add metrics when requests are processed, in order for them to be exposed by the API user and scraped by Prometheus.
- Loading branch information
1 parent
da505af
commit 9c1df9a
Showing
7 changed files
with
269 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package bucketclient | ||
|
||
import ( | ||
"sync" | ||
|
||
"github.com/prometheus/client_golang/prometheus" | ||
) | ||
|
||
type BucketClientMetrics struct { | ||
RequestsTotal *prometheus.CounterVec | ||
RequestDurationSeconds *prometheus.SummaryVec | ||
RequestBytesSentTotal *prometheus.CounterVec | ||
ResponseBytesReceivedTotal *prometheus.CounterVec | ||
} | ||
|
||
var metricsLabels = []string{ | ||
"endpoint", | ||
"method", | ||
"action", | ||
"code", | ||
} | ||
|
||
var globalMetrics *BucketClientMetrics | ||
var globalMetricsLock sync.Mutex | ||
|
||
// EnableMetrics enables Prometheus metrics gathering for the provided client and registers | ||
// them in the provided registerer. | ||
// | ||
// Metrics implemented: | ||
// - `s3_metadata_bucketclient_requests_total`: | ||
// Number of requests processed (counter) | ||
// - `s3_metadata_bucketclient_request_duration_seconds`: | ||
// Time elapsed processing requests to bucketd, in seconds (summary) | ||
// - `s3_metadata_bucketclient_request_bytes_sent_total`: | ||
// Number of request body bytes sent to bucketd (counter) | ||
// - `s3_metadata_bucketclient_response_bytes_received_total`: | ||
// Number of response body bytes received from bucketd (counter) | ||
// | ||
// Metrics have the following labels attached: | ||
// - `endpoint`: | ||
// bucketd endpoint such as `http://localhost:9000` | ||
// - `method`: | ||
// HTTP method | ||
// - `action`: | ||
// name of the API action, such as `CreateBucket`. Admin actions are prefixed with `Admin`. | ||
// - `code`: | ||
// HTTP status code returned, or "0" for generic network or protocol errors | ||
func (client *BucketClient) EnableMetrics(registerer prometheus.Registerer) error { | ||
globalMetricsLock.Lock() | ||
defer globalMetricsLock.Unlock() | ||
|
||
if globalMetrics == nil { | ||
globalMetrics = &BucketClientMetrics{ | ||
RequestsTotal: prometheus.NewCounterVec(prometheus.CounterOpts{ | ||
Namespace: MetricsNamespace, | ||
Name: "requests_total", | ||
Help: "Number of requests processed", | ||
}, metricsLabels), | ||
|
||
RequestDurationSeconds: prometheus.NewSummaryVec(prometheus.SummaryOpts{ | ||
Namespace: MetricsNamespace, | ||
Name: "request_duration_seconds", | ||
Help: "Time elapsed processing requests to bucketd, in seconds", | ||
Objectives: MetricsSummaryDefaultObjectives, | ||
}, metricsLabels), | ||
|
||
RequestBytesSentTotal: prometheus.NewCounterVec(prometheus.CounterOpts{ | ||
Namespace: MetricsNamespace, | ||
Name: "request_bytes_sent_total", | ||
Help: "Number of request body bytes sent to bucketd", | ||
}, metricsLabels), | ||
|
||
ResponseBytesReceivedTotal: prometheus.NewCounterVec(prometheus.CounterOpts{ | ||
Namespace: MetricsNamespace, | ||
Name: "response_bytes_received_total", | ||
Help: "Number of response body bytes received from bucketd", | ||
}, metricsLabels), | ||
} | ||
if err := registerer.Register(globalMetrics.RequestsTotal); err != nil { | ||
return err | ||
} | ||
if err := registerer.Register(globalMetrics.RequestDurationSeconds); err != nil { | ||
return err | ||
} | ||
if err := registerer.Register(globalMetrics.RequestBytesSentTotal); err != nil { | ||
return err | ||
} | ||
if err := registerer.Register(globalMetrics.ResponseBytesReceivedTotal); err != nil { | ||
return err | ||
} | ||
} | ||
client.Metrics = globalMetrics | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package bucketclient_test | ||
|
||
import ( | ||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
|
||
"io" | ||
"net/http" | ||
"strings" | ||
|
||
"github.com/jarcoal/httpmock" | ||
"github.com/prometheus/client_golang/prometheus" | ||
promTestutil "github.com/prometheus/client_golang/prometheus/testutil" | ||
|
||
"github.com/scality/bucketclient/go" | ||
) | ||
|
||
var _ = Describe("BucketClientMetrics", func() { | ||
Describe("RegisterMetrics()", func() { | ||
It("enables metrics collection for all requests of all clients with metrics enabled", func(ctx SpecContext) { | ||
client1 := bucketclient.New("http://localhost:9000") | ||
Expect(client1).ToNot(BeNil()) | ||
client2 := bucketclient.New("http://localhost:9001") | ||
Expect(client2).ToNot(BeNil()) | ||
|
||
registry := prometheus.NewPedanticRegistry() | ||
Expect(client1.EnableMetrics(registry)).To(Succeed()) | ||
Expect(client2.EnableMetrics(registry)).To(Succeed()) | ||
|
||
mockAttributes := `{"foo":"bar"}` | ||
httpmock.RegisterResponder( | ||
"GET", "http://localhost:9000/default/attributes/my-bucket", | ||
httpmock.NewStringResponder(200, mockAttributes), | ||
) | ||
expectedBatch := `{"batch":[{"key":"foo","value":"{}"}]}` | ||
batchErrorResponse := "OOPS!" | ||
httpmock.RegisterResponder( | ||
"POST", "http://localhost:9001/default/batch/my-bucket", | ||
func(req *http.Request) (*http.Response, error) { | ||
defer req.Body.Close() | ||
Expect(io.ReadAll(req.Body)).To(Equal([]byte(expectedBatch))) | ||
return httpmock.NewStringResponse(500, batchErrorResponse), nil | ||
}, | ||
) | ||
|
||
_, err := client1.GetBucketAttributes(ctx, "my-bucket") | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
err = client2.PostBatch(ctx, "my-bucket", []bucketclient.PostBatchEntry{ | ||
{Key: "foo", Value: "{}"}, | ||
}) | ||
Expect(err).To(HaveOccurred()) | ||
|
||
Expect(promTestutil.GatherAndCompare(registry, strings.NewReader(` | ||
# HELP s3_metadata_bucketclient_requests_total Number of requests processed | ||
# TYPE s3_metadata_bucketclient_requests_total counter | ||
s3_metadata_bucketclient_requests_total{action="GetBucketAttributes",code="200",endpoint="http://localhost:9000",method="GET"} 1 | ||
s3_metadata_bucketclient_requests_total{action="PostBatch",code="500",endpoint="http://localhost:9001",method="POST"} 1 | ||
# HELP s3_metadata_bucketclient_request_bytes_sent_total Number of request body bytes sent to bucketd | ||
# TYPE s3_metadata_bucketclient_request_bytes_sent_total counter | ||
s3_metadata_bucketclient_request_bytes_sent_total{action="GetBucketAttributes",code="200",endpoint="http://localhost:9000",method="GET"} 0 | ||
s3_metadata_bucketclient_request_bytes_sent_total{action="PostBatch",code="500",endpoint="http://localhost:9001",method="POST"} 38 | ||
# HELP s3_metadata_bucketclient_response_bytes_received_total Number of response body bytes received from bucketd | ||
# TYPE s3_metadata_bucketclient_response_bytes_received_total counter | ||
s3_metadata_bucketclient_response_bytes_received_total{action="GetBucketAttributes",code="200",endpoint="http://localhost:9000",method="GET"} 13 | ||
s3_metadata_bucketclient_response_bytes_received_total{action="PostBatch",code="500",endpoint="http://localhost:9001",method="POST"} 5 | ||
`), | ||
"s3_metadata_bucketclient_requests_total", | ||
"s3_metadata_bucketclient_request_bytes_sent_total", | ||
"s3_metadata_bucketclient_response_bytes_received_total", | ||
)).To(Succeed()) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.