From 97b5424e078e8e1d778fbc78eea9cb329d63d4d2 Mon Sep 17 00:00:00 2001 From: Ben McClelland Date: Wed, 29 May 2024 15:36:21 -0700 Subject: [PATCH] feat: move metrics actions and service to tags --- metrics/actions.go | 195 ++++++++++++++++++++++++++++++++++++++ metrics/dogstats.go | 18 +++- metrics/metrics.go | 76 ++++++++++----- metrics/statsd.go | 6 +- s3api/controllers/base.go | 10 +- 5 files changed, 271 insertions(+), 34 deletions(-) diff --git a/metrics/actions.go b/metrics/actions.go index 035e831..4a31d81 100644 --- a/metrics/actions.go +++ b/metrics/actions.go @@ -14,6 +14,15 @@ package metrics +type Action struct { + Name string + Service string +} + +var ( + ActionMap map[string]Action +) + var ( ActionUndetected = "ActionUnDetected" ActionAbortMultipartUpload = "s3_AbortMultipartUpload" @@ -61,3 +70,189 @@ var ( ActionUploadPart = "s3_UploadPart" ActionUploadPartCopy = "s3_UploadPartCopy" ) + +func init() { + ActionMap = make(map[string]Action) + + ActionMap[ActionUndetected] = Action{ + Name: "ActionUnDetected", + Service: "unknown", + } + + ActionMap[ActionAbortMultipartUpload] = Action{ + Name: "AbortMultipartUpload", + Service: "s3", + } + ActionMap[ActionCompleteMultipartUpload] = Action{ + Name: "CompleteMultipartUpload", + Service: "s3", + } + ActionMap[ActionCopyObject] = Action{ + Name: "CopyObject", + Service: "s3", + } + ActionMap[ActionCreateBucket] = Action{ + Name: "CreateBucket", + Service: "s3", + } + ActionMap[ActionCreateMultipartUpload] = Action{ + Name: "CreateMultipartUpload", + Service: "s3", + } + ActionMap[ActionDeleteBucket] = Action{ + Name: "DeleteBucket", + Service: "s3", + } + ActionMap[ActionDeleteBucketPolicy] = Action{ + Name: "DeleteBucketPolicy", + Service: "s3", + } + ActionMap[ActionDeleteBucketTagging] = Action{ + Name: "DeleteBucketTagging", + Service: "s3", + } + ActionMap[ActionDeleteObject] = Action{ + Name: "DeleteObject", + Service: "s3", + } + ActionMap[ActionDeleteObjectTagging] = Action{ + Name: "DeleteObjectTagging", + Service: "s3", + } + ActionMap[ActionDeleteObjects] = Action{ + Name: "DeleteObjects", + Service: "s3", + } + ActionMap[ActionGetBucketAcl] = Action{ + Name: "GetBucketAcl", + Service: "s3", + } + ActionMap[ActionGetBucketPolicy] = Action{ + Name: "GetBucketPolicy", + Service: "s3", + } + ActionMap[ActionGetBucketTagging] = Action{ + Name: "GetBucketTagging", + Service: "s3", + } + ActionMap[ActionGetBucketVersioning] = Action{ + Name: "GetBucketVersioning", + Service: "s3", + } + ActionMap[ActionGetObject] = Action{ + Name: "GetObject", + Service: "s3", + } + ActionMap[ActionGetObjectAcl] = Action{ + Name: "GetObjectAcl", + Service: "s3", + } + ActionMap[ActionGetObjectAttributes] = Action{ + Name: "GetObjectAttributes", + Service: "s3", + } + ActionMap[ActionGetObjectLegalHold] = Action{ + Name: "GetObjectLegalHold", + Service: "s3", + } + ActionMap[ActionGetObjectLockConfiguration] = Action{ + Name: "GetObjectLockConfiguration", + Service: "s3", + } + ActionMap[ActionGetObjectRetention] = Action{ + Name: "GetObjectRetention", + Service: "s3", + } + ActionMap[ActionGetObjectTagging] = Action{ + Name: "GetObjectTagging", + Service: "s3", + } + ActionMap[ActionHeadBucket] = Action{ + Name: "HeadBucket", + Service: "s3", + } + ActionMap[ActionHeadObject] = Action{ + Name: "HeadObject", + Service: "s3", + } + ActionMap[ActionListAllMyBuckets] = Action{ + Name: "ListAllMyBuckets", + Service: "s3", + } + ActionMap[ActionListMultipartUploads] = Action{ + Name: "ListMultipartUploads", + Service: "s3", + } + ActionMap[ActionListObjectVersions] = Action{ + Name: "ListObjectVersions", + Service: "s3", + } + ActionMap[ActionListObjects] = Action{ + Name: "ListObjects", + Service: "s3", + } + ActionMap[ActionListObjectsV2] = Action{ + Name: "ListObjectsV2", + Service: "s3", + } + ActionMap[ActionListParts] = Action{ + Name: "ListParts", + Service: "s3", + } + ActionMap[ActionPutBucketAcl] = Action{ + Name: "PutBucketAcl", + Service: "s3", + } + ActionMap[ActionPutBucketPolicy] = Action{ + Name: "PutBucketPolicy", + Service: "s3", + } + ActionMap[ActionPutBucketTagging] = Action{ + Name: "PutBucketTagging", + Service: "s3", + } + ActionMap[ActionPutBucketVersioning] = Action{ + Name: "PutBucketVersioning", + Service: "s3", + } + ActionMap[ActionPutObject] = Action{ + Name: "PutObject", + Service: "s3", + } + ActionMap[ActionPutObjectAcl] = Action{ + Name: "PutObjectAcl", + Service: "s3", + } + ActionMap[ActionPutObjectLegalHold] = Action{ + Name: "PutObjectLegalHold", + Service: "s3", + } + ActionMap[ActionPutObjectLockConfiguration] = Action{ + Name: "PutObjectLockConfiguration", + Service: "s3", + } + ActionMap[ActionPutObjectRetention] = Action{ + Name: "PutObjectRetention", + Service: "s3", + } + ActionMap[ActionPutObjectTagging] = Action{ + Name: "PutObjectTagging", + Service: "s3", + } + ActionMap[ActionRestoreObject] = Action{ + Name: "RestoreObject", + Service: "s3", + } + ActionMap[ActionSelectObjectContent] = Action{ + Name: "SelectObjectContent", + Service: "s3", + } + ActionMap[ActionUploadPart] = Action{ + Name: "UploadPart", + Service: "s3", + } + ActionMap[ActionUploadPartCopy] = Action{ + Name: "UploadPartCopy", + Service: "s3", + } +} diff --git a/metrics/dogstats.go b/metrics/dogstats.go index 4c3117c..52b253c 100644 --- a/metrics/dogstats.go +++ b/metrics/dogstats.go @@ -1,3 +1,17 @@ +// Copyright 2024 Versity Software +// This file is licensed under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + package metrics import ( @@ -42,10 +56,10 @@ func (t Tag) ddString() string { } // Add adds value to key -func (s *vgwDogStatsd) Add(module, key string, value int64, tags ...Tag) { +func (s *vgwDogStatsd) Add(key string, value int64, tags ...Tag) { stags := make([]string, len(tags)) for i, t := range tags { stags[i] = t.ddString() } - s.c.Count(fmt.Sprintf("%v.%v", module, key), value, stags, rateSampleAlways) + s.c.Count(key, value, stags, rateSampleAlways) } diff --git a/metrics/metrics.go b/metrics/metrics.go index b8be840..22cad81 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -16,10 +16,15 @@ package metrics import ( "context" + "errors" "fmt" + "net/http" "os" "strings" "sync" + + "github.com/gofiber/fiber/v2" + "github.com/versity/versitygw/s3err" ) var ( @@ -107,50 +112,76 @@ func NewManager(ctx context.Context, conf Config) (*Manager, error) { return mgr, nil } -func (m *Manager) Send(err error, action string, count int64) { +func (m *Manager) Send(ctx *fiber.Ctx, err error, action string, count int64, status int) { // In case of Authentication failures, url parsing ... if action == "" { action = ActionUndetected } + + a := ActionMap[action] + reqTags := []Tag{ + {Key: "method", Value: ctx.Method()}, + {Key: "api", Value: a.Service}, + {Key: "action", Value: a.Name}, + } + + reqStatus := status + if err != nil { - m.increment(action, "failed_count") + var apierr s3err.APIError + if errors.As(err, &apierr) { + reqStatus = apierr.HTTPStatusCode + } else { + reqStatus = http.StatusInternalServerError + } + } + if reqStatus == 0 { + reqStatus = http.StatusOK + } + + reqTags = append(reqTags, Tag{ + Key: "status", + Value: fmt.Sprintf("%v", reqStatus), + }) + + if err != nil { + m.increment("failed_count", reqTags...) } else { - m.increment(action, "success_count") + m.increment("success_count", reqTags...) } switch action { case ActionPutObject: - m.add(action, "bytes_written", count) - m.increment(action, "object_created_count") + m.add("bytes_written", count, reqTags...) + m.increment("object_created_count", reqTags...) case ActionCompleteMultipartUpload: - m.increment(action, "object_created_count") + m.increment("object_created_count", reqTags...) case ActionUploadPart: - m.add(action, "bytes_written", count) + m.add("bytes_written", count, reqTags...) case ActionGetObject: - m.add(action, "bytes_read", count) + m.add("bytes_read", count, reqTags...) case ActionDeleteObject: - m.increment(action, "object_removed_count") + m.increment("object_removed_count", reqTags...) case ActionDeleteObjects: - m.add(action, "object_removed_count", count) + m.add("object_removed_count", count, reqTags...) } } // increment increments the key by one -func (m *Manager) increment(module, key string, tags ...Tag) { - m.add(module, key, 1, tags...) +func (m *Manager) increment(key string, tags ...Tag) { + m.add(key, 1, tags...) } // add adds value to key -func (m *Manager) add(module, key string, value int64, tags ...Tag) { +func (m *Manager) add(key string, value int64, tags ...Tag) { if m.ctx.Err() != nil { return } d := datapoint{ - module: module, - key: key, - value: value, - tags: tags, + key: key, + value: value, + tags: tags, } select { @@ -174,22 +205,21 @@ func (m *Manager) Close() { // publisher is the interface for interacting with the metrics plugins type publisher interface { - Add(module, key string, value int64, tags ...Tag) + Add(key string, value int64, tags ...Tag) Close() } func (m *Manager) addForwarder(addChan <-chan datapoint) { for data := range addChan { for _, s := range m.publishers { - s.Add(data.module, data.key, data.value, data.tags...) + s.Add(data.key, data.value, data.tags...) } } m.wg.Done() } type datapoint struct { - module string - key string - value int64 - tags []Tag + key string + value int64 + tags []Tag } diff --git a/metrics/statsd.go b/metrics/statsd.go index adcc333..fc620f8 100644 --- a/metrics/statsd.go +++ b/metrics/statsd.go @@ -15,8 +15,6 @@ package metrics import ( - "fmt" - "github.com/smira/go-statsd" ) @@ -44,10 +42,10 @@ func (s *vgwStatsd) Close() { } // Add adds value to key -func (s *vgwStatsd) Add(module, key string, value int64, tags ...Tag) { +func (s *vgwStatsd) Add(key string, value int64, tags ...Tag) { stags := make([]statsd.Tag, len(tags)) for i, t := range tags { stags[i] = statsd.StringTag(t.Key, t.Value) } - s.c.Incr(fmt.Sprintf("%v.%v", module, key), value, stags...) + s.c.Incr(key, value, stags...) } diff --git a/s3api/controllers/base.go b/s3api/controllers/base.go index c01b86a..5a8c67c 100644 --- a/s3api/controllers/base.go +++ b/s3api/controllers/base.go @@ -2753,7 +2753,7 @@ func (c S3ApiController) CreateActions(ctx *fiber.Ctx) error { &MetaOpts{ Logger: c.logger, MetricsMng: c.mm, - Action: "CreateMultipartUpload", + Action: metrics.ActionCreateMultipartUpload, BucketOwner: parsedAcl.Owner, }) } @@ -2805,9 +2805,9 @@ func SendResponse(ctx *fiber.Ctx, err error, l *MetaOpts) error { } if l.MetricsMng != nil { if l.ObjectCount > 0 { - l.MetricsMng.Send(err, l.Action, l.ObjectCount) + l.MetricsMng.Send(ctx, err, l.Action, l.ObjectCount, l.Status) } else { - l.MetricsMng.Send(err, l.Action, l.ContentLength) + l.MetricsMng.Send(ctx, err, l.Action, l.ContentLength, l.Status) } } if err != nil { @@ -2854,9 +2854,9 @@ const ( func SendXMLResponse(ctx *fiber.Ctx, resp any, err error, l *MetaOpts) error { if l.MetricsMng != nil { if l.ObjectCount > 0 { - l.MetricsMng.Send(err, l.Action, l.ObjectCount) + l.MetricsMng.Send(ctx, err, l.Action, l.ObjectCount, l.Status) } else { - l.MetricsMng.Send(err, l.Action, l.ContentLength) + l.MetricsMng.Send(ctx, err, l.Action, l.ContentLength, l.Status) } } if err != nil {