forked from TrueCloudLab/frostfs-s3-gw
[#271] Add namespace label to billing metrics
Signed-off-by: Marina Biryukova <m.biryukova@yadro.com>
This commit is contained in:
parent
6c5f9b2764
commit
93cf7c462b
8 changed files with 41 additions and 18 deletions
|
@ -42,6 +42,7 @@ This document outlines major changes between releases.
|
|||
- Support frostfsid contract. See `frostfsid` config section (#260)
|
||||
- Support per namespace placement policies configuration (see `namespaces.config` config param) (#266)
|
||||
- Support control api to manage policies. See `control` config section (#258)
|
||||
- Add `namespace` label to billing metrics (#271)
|
||||
|
||||
### Changed
|
||||
- Update prometheus to v1.15.0 (#94)
|
||||
|
|
|
@ -17,10 +17,6 @@ import (
|
|||
)
|
||||
|
||||
type (
|
||||
UsersStat interface {
|
||||
Update(user, bucket, cnrID string, reqType int, in, out uint64)
|
||||
}
|
||||
|
||||
readCounter struct {
|
||||
io.ReadCloser
|
||||
countBytes uint64
|
||||
|
@ -39,6 +35,10 @@ type (
|
|||
startTime time.Time
|
||||
}
|
||||
|
||||
AliasResolver interface {
|
||||
ResolveNamespaceAlias(namespace string) string
|
||||
}
|
||||
|
||||
// BucketResolveFunc is a func to resolve bucket info by name.
|
||||
BucketResolveFunc func(ctx context.Context, bucket string) (*data.BucketInfo, error)
|
||||
|
||||
|
@ -49,14 +49,14 @@ type (
|
|||
const systemPath = "/system"
|
||||
|
||||
// Metrics wraps http handler for api with basic statistics collection.
|
||||
func Metrics(log *zap.Logger, resolveBucket BucketResolveFunc, appMetrics *metrics.AppMetrics) Func {
|
||||
func Metrics(log *zap.Logger, resolveBucket BucketResolveFunc, appMetrics *metrics.AppMetrics, aliasResolver AliasResolver) Func {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return stats(h.ServeHTTP, resolveCID(log, resolveBucket), appMetrics)
|
||||
return stats(h.ServeHTTP, resolveCID(log, resolveBucket), appMetrics, aliasResolver)
|
||||
}
|
||||
}
|
||||
|
||||
// Stats is a handler that update metrics.
|
||||
func stats(f http.HandlerFunc, resolveCID cidResolveFunc, appMetrics *metrics.AppMetrics) http.HandlerFunc {
|
||||
func stats(f http.HandlerFunc, resolveCID cidResolveFunc, appMetrics *metrics.AppMetrics, aliasResolver AliasResolver) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
reqInfo := GetReqInfo(r.Context())
|
||||
|
||||
|
@ -82,7 +82,8 @@ func stats(f http.HandlerFunc, resolveCID cidResolveFunc, appMetrics *metrics.Ap
|
|||
|
||||
user := resolveUser(r.Context())
|
||||
cnrID := resolveCID(r.Context(), reqInfo)
|
||||
appMetrics.Update(user, reqInfo.BucketName, cnrID, requestTypeFromAPI(reqInfo.API), in.countBytes, out.countBytes)
|
||||
appMetrics.Update(user, reqInfo.BucketName, cnrID, aliasResolver.ResolveNamespaceAlias(reqInfo.Namespace),
|
||||
requestTypeFromAPI(reqInfo.API), in.countBytes, out.countBytes)
|
||||
|
||||
code := statsWriter.statusCode
|
||||
// A successful request has a 2xx response code
|
||||
|
|
|
@ -98,6 +98,8 @@ type Config struct {
|
|||
|
||||
RequestMiddlewareSettings s3middleware.RequestSettings
|
||||
|
||||
AliasResolver s3middleware.AliasResolver
|
||||
|
||||
// Domains optional. If empty no virtual hosted domains will be attached.
|
||||
Domains []string
|
||||
|
||||
|
@ -112,7 +114,7 @@ func NewRouter(cfg Config) *chi.Mux {
|
|||
middleware.ThrottleWithOpts(cfg.Throttle),
|
||||
middleware.Recoverer,
|
||||
s3middleware.Tracing(),
|
||||
s3middleware.Metrics(cfg.Log, cfg.Handler.ResolveBucket, cfg.Metrics),
|
||||
s3middleware.Metrics(cfg.Log, cfg.Handler.ResolveBucket, cfg.Metrics, cfg.AliasResolver),
|
||||
s3middleware.LogSuccessResponse(cfg.Log),
|
||||
s3middleware.Auth(cfg.Center, cfg.Log),
|
||||
)
|
||||
|
|
|
@ -385,6 +385,16 @@ func (s *appSettings) setAuthorizedControlAPIKeys(keys keys.PublicKeys) {
|
|||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *appSettings) ResolveNamespaceAlias(namespace string) string {
|
||||
s.mu.RLock()
|
||||
namespaces := s.defaultNamespaces
|
||||
s.mu.RUnlock()
|
||||
if slices.Contains(namespaces, namespace) {
|
||||
return defaultMetricsNamespace
|
||||
}
|
||||
return namespace
|
||||
}
|
||||
|
||||
func (a *App) initAPI(ctx context.Context) {
|
||||
a.initLayer(ctx)
|
||||
a.initHandler()
|
||||
|
@ -620,6 +630,7 @@ func (a *App) Serve(ctx context.Context) {
|
|||
Domains: domains,
|
||||
|
||||
RequestMiddlewareSettings: a.settings,
|
||||
AliasResolver: a.settings,
|
||||
}
|
||||
|
||||
// We cannot make direct assignment if frostfsid.FrostFSID is nil
|
||||
|
|
|
@ -55,6 +55,8 @@ const (
|
|||
defaultNamespaceHeader = "X-Frostfs-Namespace"
|
||||
|
||||
defaultConstraintName = "default"
|
||||
|
||||
defaultMetricsNamespace = ""
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -65,12 +65,12 @@ func (m *AppMetrics) Handler() http.Handler {
|
|||
return m.gate.Handler()
|
||||
}
|
||||
|
||||
func (m *AppMetrics) Update(user, bucket, cnrID string, reqType RequestType, in, out uint64) {
|
||||
func (m *AppMetrics) Update(user, bucket, cnrID, ns string, reqType RequestType, in, out uint64) {
|
||||
if !m.isEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
m.gate.Billing.apiStat.Update(user, bucket, cnrID, reqType, in, out)
|
||||
m.gate.Billing.apiStat.Update(user, bucket, cnrID, ns, reqType, in, out)
|
||||
}
|
||||
|
||||
func (m *AppMetrics) Statistic() *APIStatMetrics {
|
||||
|
|
|
@ -71,6 +71,7 @@ type (
|
|||
bucketKey struct {
|
||||
name string
|
||||
cid string
|
||||
namespace string
|
||||
}
|
||||
|
||||
bucketStat struct {
|
||||
|
@ -88,6 +89,7 @@ type (
|
|||
User string
|
||||
Bucket string
|
||||
ContainerID string
|
||||
Namespace string
|
||||
}
|
||||
|
||||
UserMetricsInfo struct {
|
||||
|
@ -108,7 +110,7 @@ type (
|
|||
}
|
||||
)
|
||||
|
||||
func (u *UsersAPIStats) Update(user, bucket, cnrID string, reqType RequestType, in, out uint64) {
|
||||
func (u *UsersAPIStats) Update(user, bucket, cnrID, ns string, reqType RequestType, in, out uint64) {
|
||||
u.Lock()
|
||||
defer u.Unlock()
|
||||
|
||||
|
@ -127,6 +129,7 @@ func (u *UsersAPIStats) Update(user, bucket, cnrID string, reqType RequestType,
|
|||
key := bucketKey{
|
||||
name: bucket,
|
||||
cid: cnrID,
|
||||
namespace: ns,
|
||||
}
|
||||
|
||||
bktStat := usersStat.buckets[key]
|
||||
|
@ -151,6 +154,7 @@ func (u *UsersAPIStats) DumpMetrics() UserMetrics {
|
|||
User: user,
|
||||
Bucket: key.name,
|
||||
ContainerID: key.cid,
|
||||
Namespace: key.namespace,
|
||||
}
|
||||
|
||||
if bktStat.InTraffic != 0 {
|
||||
|
@ -232,6 +236,7 @@ func (b *billingMetrics) Collect(ch chan<- prometheus.Metric) {
|
|||
value.Bucket,
|
||||
value.ContainerID,
|
||||
value.Operation.String(),
|
||||
value.Namespace,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -244,6 +249,7 @@ func (b *billingMetrics) Collect(ch chan<- prometheus.Metric) {
|
|||
value.Bucket,
|
||||
value.ContainerID,
|
||||
value.Type.String(),
|
||||
value.Namespace,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ var appMetricsDesc = map[string]map[string]Description{
|
|||
Subsystem: billingSubsystem,
|
||||
Name: userRequestsMetric,
|
||||
Help: "Accumulated user requests",
|
||||
VariableLabels: []string{"user", "bucket", "cid", "operation"},
|
||||
VariableLabels: []string{"user", "bucket", "cid", "operation", "namespace"},
|
||||
},
|
||||
userTrafficMetric: Description{
|
||||
Type: dto.MetricType_GAUGE,
|
||||
|
@ -72,7 +72,7 @@ var appMetricsDesc = map[string]map[string]Description{
|
|||
Subsystem: billingSubsystem,
|
||||
Name: userTrafficMetric,
|
||||
Help: "Accumulated user traffic",
|
||||
VariableLabels: []string{"user", "bucket", "cid", "direction"},
|
||||
VariableLabels: []string{"user", "bucket", "cid", "direction", "namespace"},
|
||||
},
|
||||
},
|
||||
stateSubsystem: {
|
||||
|
|
Loading…
Reference in a new issue