diff --git a/go.mod b/go.mod index 56aac8194..bba87d47c 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.17 require ( github.com/alessio/shellescape v1.2.2 github.com/alicebob/miniredis/v2 v2.13.3 + github.com/blang/semver v3.5.1+incompatible github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce github.com/calebcase/tmpfile v1.0.3 github.com/cheggaaa/pb/v3 v3.0.5 @@ -60,7 +61,6 @@ require ( github.com/VividCortex/ewma v1.1.1 // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect github.com/apache/thrift v0.12.0 // indirect - github.com/blang/semver v3.5.1+incompatible // indirect github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect diff --git a/satellite/metainfo/metainfo.go b/satellite/metainfo/metainfo.go index 75efcdca0..d24dc5b1f 100644 --- a/satellite/metainfo/metainfo.go +++ b/satellite/metainfo/metainfo.go @@ -138,7 +138,7 @@ func NewEndpoint(log *zap.Logger, buckets *buckets.Service, metabaseDB *metabase revocations: revocations, defaultRS: defaultRSScheme, config: config, - versionCollector: newVersionCollector(), + versionCollector: newVersionCollector(log), }, nil } diff --git a/satellite/metainfo/version_collector.go b/satellite/metainfo/version_collector.go index 8bebfee3d..afa6b9e27 100644 --- a/satellite/metainfo/version_collector.go +++ b/satellite/metainfo/version_collector.go @@ -4,11 +4,13 @@ package metainfo import ( + "fmt" "strings" - "sync" + "github.com/blang/semver" "github.com/spacemonkeygo/monkit/v3" "github.com/zeebo/errs" + "go.uber.org/zap" "storj.io/common/useragent" ) @@ -16,53 +18,66 @@ import ( const uplinkProduct = "uplink" type versionOccurrence struct { + Product string Version string Method string } type versionCollector struct { - mu sync.Mutex - versions map[versionOccurrence]*monkit.Meter + log *zap.Logger } -func newVersionCollector() *versionCollector { +func newVersionCollector(log *zap.Logger) *versionCollector { return &versionCollector{ - versions: make(map[versionOccurrence]*monkit.Meter), + log: log, } } func (vc *versionCollector) collect(useragentRaw []byte, method string) error { - var meter *monkit.Meter + if len(useragentRaw) == 0 { + return nil + } - version := "unknown" - if len(useragentRaw) != 0 { - entries, err := useragent.ParseEntries(useragentRaw) - if err != nil { - return errs.New("invalid user agent %q: %v", string(useragentRaw), err) - } + entries, err := useragent.ParseEntries(useragentRaw) + if err != nil { + return errs.New("invalid user agent %q: %v", string(useragentRaw), err) + } - for _, entry := range entries { - if strings.EqualFold(entry.Product, uplinkProduct) { - version = entry.Version - break + for _, entry := range entries { + if strings.EqualFold(entry.Product, uplinkProduct) { + vo := versionOccurrence{ + Product: entry.Product, + Version: entry.Version, + Method: method, } + + vc.sendUplinkMetric(vo) + } else { + // for other user agents monitor only product + product := entry.Product + if product == "" { + product = "unknown" + } + mon.Meter("user_agents", monkit.NewSeriesTag("user_agent", product)).Mark(1) } } - vo := versionOccurrence{ - Version: version, - Method: method, - } - - vc.mu.Lock() - meter, ok := vc.versions[vo] - if !ok { - meter = monkit.NewMeter(monkit.NewSeriesKey("uplink_versions").WithTag("version", version).WithTag("method", method)) - mon.Chain(meter) - vc.versions[vo] = meter - } - vc.mu.Unlock() - - meter.Mark(1) return nil } + +func (vc *versionCollector) sendUplinkMetric(vo versionOccurrence) { + if vo.Version == "" { + vo.Version = "unknown" + } else { + // use only major and minor to avoid using too many resources and + // minimize risk of abusing by sending lots of different versions + semVer, err := semver.ParseTolerant(vo.Version) + if err != nil { + vc.log.Warn("invalid uplink library user agent version", zap.String("version", vo.Version), zap.Error(err)) + return + } + vo.Version = fmt.Sprintf("v%d.%d", semVer.Major, semVer.Minor) + } + + mon.Meter("uplink_versions", monkit.NewSeriesTag("version", vo.Version), monkit.NewSeriesTag("method", vo.Method)).Mark(1) +}