From 763551963dd3208dde6f9bf35b31ef76cc83814f Mon Sep 17 00:00:00 2001 From: Brian Sardo <1168933+bsardo@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:49:31 -0400 Subject: [PATCH] Metrics: Add per adapter metric indicating when buyeruid was scrubbed (#3623) --- config/config.go | 4 ++ config/config_test.go | 3 ++ exchange/utils.go | 9 +++- exchange/utils_test.go | 26 ++++++++++-- metrics/config/metrics.go | 11 +++++ metrics/config/metrics_test.go | 2 + metrics/go_metrics.go | 20 +++++++++ metrics/go_metrics_test.go | 60 +++++++++++++++++++++++---- metrics/metrics.go | 1 + metrics/metrics_mock.go | 5 +++ metrics/prometheus/preload.go | 6 +++ metrics/prometheus/prometheus.go | 17 ++++++++ metrics/prometheus/prometheus_test.go | 43 ++++++++++++++++++- 13 files changed, 194 insertions(+), 13 deletions(-) diff --git a/config/config.go b/config/config.go index 86521c4c36c..d2f2103972d 100644 --- a/config/config.go +++ b/config/config.go @@ -560,6 +560,9 @@ type DisabledMetrics struct { // that were created or reused. AdapterConnectionMetrics bool `mapstructure:"adapter_connections_metrics"` + // True if we don't want to collect the per adapter buyer UID scrubbed metric + AdapterBuyerUIDScrubbed bool `mapstructure:"adapter_buyeruid_scrubbed"` + // True if we don't want to collect the per adapter GDPR request blocked metric AdapterGDPRRequestBlocked bool `mapstructure:"adapter_gdpr_request_blocked"` @@ -945,6 +948,7 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) { v.SetDefault("metrics.disabled_metrics.account_debug", true) v.SetDefault("metrics.disabled_metrics.account_stored_responses", true) v.SetDefault("metrics.disabled_metrics.adapter_connections_metrics", true) + v.SetDefault("metrics.disabled_metrics.adapter_buyeruid_scrubbed", true) v.SetDefault("metrics.disabled_metrics.adapter_gdpr_request_blocked", false) v.SetDefault("metrics.influxdb.host", "") v.SetDefault("metrics.influxdb.database", "") diff --git a/config/config_test.go b/config/config_test.go index 4c97f125997..4a553e41fb8 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -158,6 +158,7 @@ func TestDefaults(t *testing.T) { cmpBools(t, "account_debug", true, cfg.Metrics.Disabled.AccountDebug) cmpBools(t, "account_stored_responses", true, cfg.Metrics.Disabled.AccountStoredResponses) cmpBools(t, "adapter_connections_metrics", true, cfg.Metrics.Disabled.AdapterConnectionMetrics) + cmpBools(t, "adapter_buyeruid_scrubbed", true, cfg.Metrics.Disabled.AdapterBuyerUIDScrubbed) cmpBools(t, "adapter_gdpr_request_blocked", false, cfg.Metrics.Disabled.AdapterGDPRRequestBlocked) cmpStrings(t, "certificates_file", "", cfg.PemCertsFile) cmpBools(t, "stored_requests.filesystem.enabled", false, cfg.StoredRequests.Files.Enabled) @@ -445,6 +446,7 @@ metrics: account_debug: false account_stored_responses: false adapter_connections_metrics: true + adapter_buyeruid_scrubbed: false adapter_gdpr_request_blocked: true account_modules_metrics: true blacklisted_apps: ["spamAppID","sketchy-app-id"] @@ -858,6 +860,7 @@ func TestFullConfig(t *testing.T) { cmpBools(t, "account_debug", false, cfg.Metrics.Disabled.AccountDebug) cmpBools(t, "account_stored_responses", false, cfg.Metrics.Disabled.AccountStoredResponses) cmpBools(t, "adapter_connections_metrics", true, cfg.Metrics.Disabled.AdapterConnectionMetrics) + cmpBools(t, "adapter_buyeruid_scrubbed", false, cfg.Metrics.Disabled.AdapterBuyerUIDScrubbed) cmpBools(t, "adapter_gdpr_request_blocked", true, cfg.Metrics.Disabled.AdapterGDPRRequestBlocked) cmpStrings(t, "certificates_file", "/etc/ssl/cert.pem", cfg.PemCertsFile) cmpStrings(t, "request_validation.ipv4_private_networks", "1.1.1.0/24", cfg.RequestValidation.IPv4PrivateNetworks[0]) diff --git a/exchange/utils.go b/exchange/utils.go index 671e2ff86b1..7d2e47c2f21 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -200,20 +200,27 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context, } passIDActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitUserFPD, scopedName, privacy.NewRequestFromBidRequest(*req)) + buyerUIDSet := reqWrapper.User != nil && reqWrapper.User.BuyerUID != "" + buyerUIDRemoved := false if !passIDActivityAllowed { - //UFPD privacy.ScrubUserFPD(reqWrapper) + buyerUIDRemoved = true } else { // run existing policies (GDPR, CCPA, COPPA, LMT) // potentially block passing IDs based on GDPR if gdprEnforced && (gdprErr != nil || !auctionPermissions.PassID) { privacy.ScrubGdprID(reqWrapper) + buyerUIDRemoved = true } // potentially block passing IDs based on CCPA if ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String()) { privacy.ScrubDeviceIDsIPsUserDemoExt(reqWrapper, ipConf, "eids", false) + buyerUIDRemoved = true } } + if buyerUIDSet && buyerUIDRemoved { + rs.me.RecordAdapterBuyerUIDScrubbed(bidderRequest.BidderCoreName) + } passGeoActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitPreciseGeo, scopedName, privacy.NewRequestFromBidRequest(*req)) if !passGeoActivityAllowed { diff --git a/exchange/utils_test.go b/exchange/utils_test.go index 3b723d4e5b9..4ca13c524ed 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -1015,10 +1015,13 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) { }, }.Builder + metricsMock := metrics.MetricsEngineMock{} + metricsMock.Mock.On("RecordAdapterBuyerUIDScrubbed", mock.Anything).Return() + bidderToSyncerKey := map[string]string{} reqSplitter := &requestSplitter{ bidderToSyncerKey: bidderToSyncerKey, - me: &metrics.MetricsEngineMock{}, + me: &metricsMock, privacyConfig: privacyConfig, gdprPermsBuilder: gdprPermissionsBuilder, hostSChainNode: nil, @@ -1032,9 +1035,11 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) { if test.expectDataScrub { assert.Equal(t, result.BidRequest.User.BuyerUID, "", test.description+":User.BuyerUID") assert.Equal(t, result.BidRequest.Device.DIDMD5, "", test.description+":Device.DIDMD5") + metricsMock.AssertCalled(t, "RecordAdapterBuyerUIDScrubbed", openrtb_ext.BidderAppnexus) } else { assert.NotEqual(t, result.BidRequest.User.BuyerUID, "", test.description+":User.BuyerUID") assert.NotEqual(t, result.BidRequest.Device.DIDMD5, "", test.description+":Device.DIDMD5") + metricsMock.AssertNotCalled(t, "RecordAdapterBuyerUIDScrubbed", openrtb_ext.BidderAppnexus) } assert.Equal(t, test.expectPrivacyLabels, privacyLabels, test.description+":PrivacyLabels") } @@ -2141,9 +2146,12 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { gdprDefaultValue = gdpr.SignalNo } + metricsMock := metrics.MetricsEngineMock{} + metricsMock.Mock.On("RecordAdapterBuyerUIDScrubbed", mock.Anything).Return() + reqSplitter := &requestSplitter{ bidderToSyncerKey: map[string]string{}, - me: &metrics.MetricsEngineMock{}, + me: &metricsMock, privacyConfig: privacyConfig, gdprPermsBuilder: gdprPermissionsBuilder, hostSChainNode: nil, @@ -2162,9 +2170,11 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) { if test.gdprScrub { assert.Equal(t, result.BidRequest.User.BuyerUID, "", test.description+":User.BuyerUID") assert.Equal(t, result.BidRequest.Device.DIDMD5, "", test.description+":Device.DIDMD5") + metricsMock.AssertCalled(t, "RecordAdapterBuyerUIDScrubbed", openrtb_ext.BidderAppnexus) } else { assert.NotEqual(t, result.BidRequest.User.BuyerUID, "", test.description+":User.BuyerUID") assert.NotEqual(t, result.BidRequest.Device.DIDMD5, "", test.description+":Device.DIDMD5") + metricsMock.AssertNotCalled(t, "RecordAdapterBuyerUIDScrubbed", openrtb_ext.BidderAppnexus) } assert.Equal(t, test.expectPrivacyLabels, privacyLabels, test.description+":PrivacyLabels") } @@ -4523,6 +4533,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) { allow bool expectedReqNumber int expectedUser openrtb2.User + expectUserScrub bool expectedDevice openrtb2.Device expectedSource openrtb2.Source expectedImpExt json.RawMessage @@ -4570,6 +4581,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) { Ext: json.RawMessage(`{"test":2}`), Data: nil, }, + expectUserScrub: true, expectedDevice: openrtb2.Device{ UA: deviceUA, Language: "EN", @@ -4670,10 +4682,13 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) { TCF2Config: gdpr.NewTCF2Config(config.TCF2{}, config.AccountGDPR{}), } + metricsMock := metrics.MetricsEngineMock{} + metricsMock.Mock.On("RecordAdapterBuyerUIDScrubbed", mock.Anything).Return() + bidderToSyncerKey := map[string]string{} reqSplitter := &requestSplitter{ bidderToSyncerKey: bidderToSyncerKey, - me: &metrics.MetricsEngineMock{}, + me: &metricsMock, hostSChainNode: nil, bidderInfo: config.BidderInfos{}, } @@ -4690,6 +4705,11 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) { if len(test.expectedImpExt) > 0 { assert.JSONEq(t, string(test.expectedImpExt), string(bidderRequests[0].BidRequest.Imp[0].Ext)) } + if test.expectUserScrub { + metricsMock.AssertCalled(t, "RecordAdapterBuyerUIDScrubbed", openrtb_ext.BidderAppnexus) + } else { + metricsMock.AssertNotCalled(t, "RecordAdapterBuyerUIDScrubbed", openrtb_ext.BidderAppnexus) + } } }) } diff --git a/metrics/config/metrics.go b/metrics/config/metrics.go index 7ed387512fd..8b544bd966b 100644 --- a/metrics/config/metrics.go +++ b/metrics/config/metrics.go @@ -266,6 +266,13 @@ func (me *MultiMetricsEngine) RecordRequestPrivacy(privacy metrics.PrivacyLabels } } +// RecordAdapterBuyerUIDScrubbed across all engines +func (me *MultiMetricsEngine) RecordAdapterBuyerUIDScrubbed(adapter openrtb_ext.BidderName) { + for _, thisME := range *me { + thisME.RecordAdapterBuyerUIDScrubbed(adapter) + } +} + // RecordAdapterGDPRRequestBlocked across all engines func (me *MultiMetricsEngine) RecordAdapterGDPRRequestBlocked(adapter openrtb_ext.BidderName) { for _, thisME := range *me { @@ -484,6 +491,10 @@ func (me *NilMetricsEngine) RecordTimeoutNotice(success bool) { func (me *NilMetricsEngine) RecordRequestPrivacy(privacy metrics.PrivacyLabels) { } +// RecordAdapterBuyerUIDScrubbed as a noop +func (me *NilMetricsEngine) RecordAdapterBuyerUIDScrubbed(adapter openrtb_ext.BidderName) { +} + // RecordAdapterGDPRRequestBlocked as a noop func (me *NilMetricsEngine) RecordAdapterGDPRRequestBlocked(adapter openrtb_ext.BidderName) { } diff --git a/metrics/config/metrics_test.go b/metrics/config/metrics_test.go index 5badc348e61..e201cf52cf5 100644 --- a/metrics/config/metrics_test.go +++ b/metrics/config/metrics_test.go @@ -143,6 +143,7 @@ func TestMultiMetricsEngine(t *testing.T) { metricsEngine.RecordStoredImpCacheResult(metrics.CacheHit, 5) metricsEngine.RecordAccountCacheResult(metrics.CacheHit, 6) + metricsEngine.RecordAdapterBuyerUIDScrubbed(openrtb_ext.BidderAppnexus) metricsEngine.RecordAdapterGDPRRequestBlocked(openrtb_ext.BidderAppnexus) metricsEngine.RecordRequestQueueTime(false, metrics.ReqTypeVideo, time.Duration(1)) @@ -188,6 +189,7 @@ func TestMultiMetricsEngine(t *testing.T) { VerifyMetrics(t, "StoredImpCache.Hit", goEngine.StoredImpCacheMeter[metrics.CacheHit].Count(), 5) VerifyMetrics(t, "AccountCache.Hit", goEngine.AccountCacheMeter[metrics.CacheHit].Count(), 6) + VerifyMetrics(t, "AdapterMetrics.appNexus.BuyerUIDScrubbed", goEngine.AdapterMetrics[strings.ToLower(string(openrtb_ext.BidderAppnexus))].BuyerUIDScrubbed.Count(), 1) VerifyMetrics(t, "AdapterMetrics.appNexus.GDPRRequestBlocked", goEngine.AdapterMetrics[strings.ToLower(string(openrtb_ext.BidderAppnexus))].GDPRRequestBlocked.Count(), 1) // verify that each module has its own metric recorded diff --git a/metrics/go_metrics.go b/metrics/go_metrics.go index 5bc4ab965f6..ac28a18c8e1 100644 --- a/metrics/go_metrics.go +++ b/metrics/go_metrics.go @@ -99,6 +99,7 @@ type AdapterMetrics struct { ConnCreated metrics.Counter ConnReused metrics.Counter ConnWaitTime metrics.Timer + BuyerUIDScrubbed metrics.Meter GDPRRequestBlocked metrics.Meter BidValidationCreativeSizeErrorMeter metrics.Meter @@ -406,6 +407,9 @@ func makeBlankAdapterMetrics(disabledMetrics config.DisabledMetrics) *AdapterMet newAdapter.ConnReused = metrics.NilCounter{} newAdapter.ConnWaitTime = &metrics.NilTimer{} } + if !disabledMetrics.AdapterBuyerUIDScrubbed { + newAdapter.BuyerUIDScrubbed = blankMeter + } if !disabledMetrics.AdapterGDPRRequestBlocked { newAdapter.GDPRRequestBlocked = blankMeter } @@ -484,6 +488,7 @@ func registerAdapterMetrics(registry metrics.Registry, adapterOrAccount string, am.BidsReceivedMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("%[1]s.%[2]s.bids_received", adapterOrAccount, exchange), registry) } am.PanicMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("%[1]s.%[2]s.requests.panic", adapterOrAccount, exchange), registry) + am.BuyerUIDScrubbed = metrics.GetOrRegisterMeter(fmt.Sprintf("%[1]s.%[2]s.buyeruid_scrubbed", adapterOrAccount, exchange), registry) am.GDPRRequestBlocked = metrics.GetOrRegisterMeter(fmt.Sprintf("%[1]s.%[2]s.gdpr_request_blocked", adapterOrAccount, exchange), registry) am.BidValidationCreativeSizeErrorMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("%[1]s.%[2]s.response.validation.size.err", adapterOrAccount, exchange), registry) @@ -926,6 +931,21 @@ func (me *Metrics) RecordRequestPrivacy(privacy PrivacyLabels) { } } +func (me *Metrics) RecordAdapterBuyerUIDScrubbed(adapterName openrtb_ext.BidderName) { + adapterStr := adapterName.String() + if me.MetricsDisabled.AdapterBuyerUIDScrubbed { + return + } + + am, ok := me.AdapterMetrics[strings.ToLower(adapterStr)] + if !ok { + glog.Errorf("Trying to log adapter buyeruid scrubbed metric for %s: adapter not found", adapterStr) + return + } + + am.BuyerUIDScrubbed.Mark(1) +} + func (me *Metrics) RecordAdapterGDPRRequestBlocked(adapterName openrtb_ext.BidderName) { adapterStr := string(adapterName) if me.MetricsDisabled.AdapterGDPRRequestBlocked { diff --git a/metrics/go_metrics_test.go b/metrics/go_metrics_test.go index 05529220f16..7cacd9099db 100644 --- a/metrics/go_metrics_test.go +++ b/metrics/go_metrics_test.go @@ -790,43 +790,87 @@ func TestRecordRequestPrivacy(t *testing.T) { assert.Equal(t, m.PrivacyTCFRequestVersion[TCFVersionV2].Count(), int64(1), "TCF V2") } +func TestRecordAdapterBuyerUIDScrubbed(t *testing.T) { + var fakeBidder openrtb_ext.BidderName = "fooAdvertising" + adapter := "AnyName" + lowerCaseAdapterName := "anyname" + + tests := []struct { + name string + metricsDisabled bool + adapterName openrtb_ext.BidderName + expectedCount int64 + }{ + { + name: "enabled_bidder_found", + metricsDisabled: false, + adapterName: openrtb_ext.BidderName(adapter), + expectedCount: 1, + }, + { + name: "enabled_bidder_not_found", + metricsDisabled: false, + adapterName: fakeBidder, + expectedCount: 0, + }, + { + name: "disabled", + metricsDisabled: true, + adapterName: openrtb_ext.BidderName(adapter), + expectedCount: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + registry := metrics.NewRegistry() + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapter)}, config.DisabledMetrics{AdapterBuyerUIDScrubbed: tt.metricsDisabled}, nil, nil) + + m.RecordAdapterBuyerUIDScrubbed(tt.adapterName) + + assert.Equal(t, tt.expectedCount, m.AdapterMetrics[lowerCaseAdapterName].BuyerUIDScrubbed.Count()) + }) + } +} + func TestRecordAdapterGDPRRequestBlocked(t *testing.T) { var fakeBidder openrtb_ext.BidderName = "fooAdvertising" adapter := "AnyName" lowerCaseAdapterName := "anyname" tests := []struct { - description string + name string metricsDisabled bool adapterName openrtb_ext.BidderName expectedCount int64 }{ { - description: "", + name: "enabled_bidder_found", metricsDisabled: false, adapterName: openrtb_ext.BidderName(adapter), expectedCount: 1, }, { - description: "", + name: "enabled_bidder_not_found", metricsDisabled: false, adapterName: fakeBidder, expectedCount: 0, }, { - description: "", + name: "disabled", metricsDisabled: true, adapterName: openrtb_ext.BidderName(adapter), expectedCount: 0, }, } for _, tt := range tests { - registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapter)}, config.DisabledMetrics{AdapterGDPRRequestBlocked: tt.metricsDisabled}, nil, nil) + t.Run(tt.name, func(t *testing.T) { + registry := metrics.NewRegistry() + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapter)}, config.DisabledMetrics{AdapterGDPRRequestBlocked: tt.metricsDisabled}, nil, nil) - m.RecordAdapterGDPRRequestBlocked(tt.adapterName) + m.RecordAdapterGDPRRequestBlocked(tt.adapterName) - assert.Equal(t, tt.expectedCount, m.AdapterMetrics[lowerCaseAdapterName].GDPRRequestBlocked.Count(), tt.description) + assert.Equal(t, tt.expectedCount, m.AdapterMetrics[lowerCaseAdapterName].GDPRRequestBlocked.Count()) + }) } } diff --git a/metrics/metrics.go b/metrics/metrics.go index 7d3dc819341..2696c642e21 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -455,6 +455,7 @@ type MetricsEngine interface { RecordRequestQueueTime(success bool, requestType RequestType, length time.Duration) RecordTimeoutNotice(success bool) RecordRequestPrivacy(privacy PrivacyLabels) + RecordAdapterBuyerUIDScrubbed(adapterName openrtb_ext.BidderName) RecordAdapterGDPRRequestBlocked(adapterName openrtb_ext.BidderName) RecordDebugRequest(debugEnabled bool, pubId string) RecordStoredResponse(pubId string) diff --git a/metrics/metrics_mock.go b/metrics/metrics_mock.go index ef9fdcaf7a4..56e2ab8fb1b 100644 --- a/metrics/metrics_mock.go +++ b/metrics/metrics_mock.go @@ -156,6 +156,11 @@ func (me *MetricsEngineMock) RecordRequestPrivacy(privacy PrivacyLabels) { me.Called(privacy) } +// RecordAdapterBuyerUIDScrubbed mock +func (me *MetricsEngineMock) RecordAdapterBuyerUIDScrubbed(adapterName openrtb_ext.BidderName) { + me.Called(adapterName) +} + // RecordAdapterGDPRRequestBlocked mock func (me *MetricsEngineMock) RecordAdapterGDPRRequestBlocked(adapterName openrtb_ext.BidderName) { me.Called(adapterName) diff --git a/metrics/prometheus/preload.go b/metrics/prometheus/preload.go index a4a70017355..73ea643722b 100644 --- a/metrics/prometheus/preload.go +++ b/metrics/prometheus/preload.go @@ -229,6 +229,12 @@ func preloadLabelValues(m *Metrics, syncerKeys []string, moduleStageNames map[st versionLabel: tcfVersionValues, }) + if !m.metricsDisabled.AdapterBuyerUIDScrubbed { + preloadLabelValuesForCounter(m.adapterScrubbedBuyerUIDs, map[string][]string{ + adapterLabel: adapterValues, + }) + } + if !m.metricsDisabled.AdapterGDPRRequestBlocked { preloadLabelValuesForCounter(m.adapterGDPRBlockedRequests, map[string][]string{ adapterLabel: adapterValues, diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index 6bd30544662..89b589badf8 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -70,6 +70,7 @@ type Metrics struct { adapterReusedConnections *prometheus.CounterVec adapterCreatedConnections *prometheus.CounterVec adapterConnectionWaitTime *prometheus.HistogramVec + adapterScrubbedBuyerUIDs *prometheus.CounterVec adapterGDPRBlockedRequests *prometheus.CounterVec adapterBidResponseValidationSizeError *prometheus.CounterVec adapterBidResponseValidationSizeWarn *prometheus.CounterVec @@ -334,6 +335,12 @@ func NewMetrics(cfg config.PrometheusMetrics, disabledMetrics config.DisabledMet "Count of total requests to Prebid Server where the LMT flag was set by source", []string{sourceLabel}) + if !metrics.metricsDisabled.AdapterBuyerUIDScrubbed { + metrics.adapterScrubbedBuyerUIDs = newCounter(cfg, reg, + "adapter_buyeruids_scrubbed", + "Count of total bidder requests with a scrubbed buyeruid due to a privacy policy", + []string{adapterLabel}) + } if !metrics.metricsDisabled.AdapterGDPRRequestBlocked { metrics.adapterGDPRBlockedRequests = newCounter(cfg, reg, "adapter_gdpr_requests_blocked", @@ -952,6 +959,16 @@ func (m *Metrics) RecordRequestPrivacy(privacy metrics.PrivacyLabels) { } } +func (m *Metrics) RecordAdapterBuyerUIDScrubbed(adapterName openrtb_ext.BidderName) { + if m.metricsDisabled.AdapterBuyerUIDScrubbed { + return + } + + m.adapterScrubbedBuyerUIDs.With(prometheus.Labels{ + adapterLabel: strings.ToLower(string(adapterName)), + }).Inc() +} + func (m *Metrics) RecordAdapterGDPRRequestBlocked(adapterName openrtb_ext.BidderName) { if m.metricsDisabled.AdapterGDPRRequestBlocked { return diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go index a74c8b6c0fa..3ce2f222896 100644 --- a/metrics/prometheus/prometheus_test.go +++ b/metrics/prometheus/prometheus_test.go @@ -64,7 +64,7 @@ func TestMetricCountGatekeeping(t *testing.T) { // Verify Per-Adapter Cardinality // - This assertion provides a warning for newly added adapter metrics. Threre are 40+ adapters which makes the // cost of new per-adapter metrics rather expensive. Thought should be given when adding new per-adapter metrics. - assert.True(t, perAdapterCardinalityCount <= 30, "Per-Adapter Cardinality count equals %d \n", perAdapterCardinalityCount) + assert.True(t, perAdapterCardinalityCount <= 31, "Per-Adapter Cardinality count equals %d \n", perAdapterCardinalityCount) } func TestConnectionMetrics(t *testing.T) { @@ -1635,6 +1635,7 @@ func TestDisabledMetrics(t *testing.T) { Namespace: "prebid", Subsystem: "server", }, config.DisabledMetrics{ + AdapterBuyerUIDScrubbed: true, AdapterConnectionMetrics: true, AdapterGDPRRequestBlocked: true, }, @@ -1642,6 +1643,7 @@ func TestDisabledMetrics(t *testing.T) { // Assert counter vector was not initialized assert.Nil(t, prometheusMetrics.adapterReusedConnections, "Counter Vector adapterReusedConnections should be nil") + assert.Nil(t, prometheusMetrics.adapterScrubbedBuyerUIDs, "Counter Vector adapterScrubbedBuyerUIDs should be nil") assert.Nil(t, prometheusMetrics.adapterCreatedConnections, "Counter Vector adapterCreatedConnections should be nil") assert.Nil(t, prometheusMetrics.adapterConnectionWaitTime, "Counter Vector adapterConnectionWaitTime should be nil") assert.Nil(t, prometheusMetrics.adapterGDPRBlockedRequests, "Counter Vector adapterGDPRBlockedRequests should be nil") @@ -1789,6 +1791,45 @@ func assertHistogram(t *testing.T, name string, histogram dto.Histogram, expecte assert.Equal(t, expectedSum, histogram.GetSampleSum(), name+":sum") } +func TestRecordAdapterBuyerUIDScrubbed(t *testing.T) { + + tests := []struct { + name string + disabled bool + expectedCount float64 + }{ + { + name: "enabled", + disabled: false, + expectedCount: 1, + }, + { + name: "disabled", + disabled: true, + expectedCount: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := createMetricsForTesting() + m.metricsDisabled.AdapterBuyerUIDScrubbed = tt.disabled + adapterName := openrtb_ext.BidderName("AnyName") + lowerCasedAdapterName := "anyname" + m.RecordAdapterBuyerUIDScrubbed(adapterName) + + assertCounterVecValue(t, + "Increment adapter buyeruid scrubbed counter", + "adapter_buyeruids_scrubbed", + m.adapterScrubbedBuyerUIDs, + tt.expectedCount, + prometheus.Labels{ + adapterLabel: lowerCasedAdapterName, + }) + }) + } +} + func TestRecordAdapterGDPRRequestBlocked(t *testing.T) { m := createMetricsForTesting() adapterName := openrtb_ext.BidderName("AnyName")