Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug Metrics Implementation #2246

Merged
merged 11 commits into from
May 25, 2022
Merged

Conversation

AlexBVolcy
Copy link
Contributor

@AlexBVolcy AlexBVolcy commented May 13, 2022

This PR implements the feature/issue #1474

The goal is to add new metrics to PBS related to debug

The first new debug metric, is one that counts the number of requests we receive that has debug enabled.

The second new debug metric, is one that counts the number of requests from an account that has debug enabled.

This second metric is controlled by a config flag "metrics.disabled_metrics.account_debug" because of concerns of high cardinality

These metrics are implemented for both prometheus and influx, by creating/implementing a new record function RecordDebugRequest() within both metrics' frameworks.

This function is then called within HoldAuction() after we receive debug information from the getDebugInfo() call.

For influx, I added two new meters to represent the new metrics in the Metrics and AccountMetrics struct, DebugRequestMeter and accountMetrics.DebugRequestMeter

For prometheus, I added two new metrics to the prometheus specific Metrics struct debugRequests and accountDebugRequests

New tests were included to test these updated metrics

Other metrics engines like nil metrics, multi metrics, and metrics mock, only need to be updated with a simple handling of how they implement/call RecordDebugRequest()

@@ -3702,7 +3702,7 @@ type warningsCheckExchange struct {
auctionRequest exchange.AuctionRequest
}

func (e *warningsCheckExchange) HoldAuction(ctx context.Context, r exchange.AuctionRequest, debugLog *exchange.DebugLog) (*openrtb2.BidResponse, error) {
func (e *warningsCheckExchange) HoldAuction(ctx context.Context, r exchange.AuctionRequest, debugLog *exchange.DebugLog, labels *metrics.Labels) (*openrtb2.BidResponse, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not necessary to add a new labels argument. The labels are already present in the r exchange.AuctionRequest argument.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored and removed in latest commit

CookieFlag CookieFlag
RequestStatus RequestStatus
DebugFlag bool
AccountDebugFlag bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you'll be fine with a single debug flag, perhaps naming DebugEnabed or DebugRequested or perhaps even Debug. We don't care how debug is enabled, just that it is is enabled. With the implementation of the debug permissions, I can easily see how you arrived at this though. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah this makes sense! I'll update accordingly.

metrics.requestsDebug = newCounter(cfg, reg,
"requests_debug",
"Count of total requests to Prebid Server that have debug enabled labled by type, status.",
[]string{requestTypeLabel, requestStatusLabel})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not believe we need to have any labels on this metric. I personally don't see any value in the status label and only minimal value in the types label. I won't put up a fight it you want to keep the type label, but I'll need convincing on why this is important for the status label.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I struggled to think of reasons to have the labels, but I initially just matched the format of metrics.requests. But I feel comfortable with removing both

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed during stand up, both labels have been removed

Copy link
Contributor

@SyntaxNode SyntaxNode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Let's try to improve the flow of the debug flag, or put it off for a future update if we think of a good idea and decide it's too much work to include here.

@@ -219,6 +220,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
ao.Errors = append(ao.Errors, err)
return
}
labels.DebugEnabled = debugLog.DebugFlag
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this line? ... since it occurs after the HoldAuction call. Wouldn't the metrics for an incoming request have been recorded before hitting this line? Same applies to openrtb/auction.go

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand this correctly, metrics will be written at the end of endpoint execution. It's defined in defer section, L122:

defer func() {
		deps.metricsEngine.RecordRequest(labels)
		deps.metricsEngine.RecordRequestTime(labels, time.Since(start))
		deps.analytics.LogAmpObject(&ao)
	}()

Auction, video and amp endpoints have this approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah exactly like Veronika said above! And when I sent test postman requests, it acted like this as well!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. Got it. This code is a bit too clever IMHO, but I agree it's beyond the scope of this PR to fix. Can you add a comment before these lines to make it clear what's going on to my future self?

@@ -219,6 +220,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
ao.Errors = append(ao.Errors, err)
return
}
labels.DebugEnabled = debugLog.DebugFlag
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I consider it a "code smell" that debugLog is instantiated here just to return the DebugFlag to the outer scope. It would be preferable IMHO to either calculate the debug flag prior to calling HoldAuction or include it as a return argument somehow. I know you didn't create this situation, but I believe it's worth an investigation to try and make this flow a bit nicer now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better if labels.DebugEnabled will be passed in auctionRequest downstrem to getDebugInfo and modified there?
I cannot think about better solution...

@@ -219,6 +220,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
ao.Errors = append(ao.Errors, err)
return
}
labels.DebugEnabled = debugLog.DebugFlag
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I consider it a "code smell" that debugLog is instantiated here just to return the DebugFlag to the outer scope. It would be preferable IMHO to either calculate the debug flag prior to calling HoldAuction or include it as a return argument somehow. I know you didn't create this situation, but I believe it's worth an investigation to try and make this flow a bit nicer now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with Scott here. This definitely seems odd to instantiate the debugLog struct here just to return the DebugFlag to the outer scope. How about making debugLog a part of the AuctionRequest struct? That way you can read and modify it in HoldAuction as needed and you can again access it outside of HoldAuction for setting the metrics label and such

Copy link
Contributor Author

@AlexBVolcy AlexBVolcy May 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that code smell call out makes total sense! I wrote up some code just now to put debugLog in the AuctionRequest struct, and that solution worked! I need to run some more tests, but I think that would be a valid solution to this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That way you can read and modify it in HoldAuction as needed and you can again access it outside of HoldAuction for setting the metrics label and such

A step in the right direction, but modifying it as a way to pass information upstream is still an undesirable design. We might have to tackle that later on.

Copy link
Contributor

@VeronikaSolovei9 VeronikaSolovei9 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good to me. Please resolve Scott's comments.

@@ -219,6 +220,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
ao.Errors = append(ao.Errors, err)
return
}
labels.DebugEnabled = debugLog.DebugFlag
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this line? ... since it occurs after the HoldAuction call. Wouldn't the metrics for an incoming request have been recorded before hitting this line?

@@ -219,6 +220,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
ao.Errors = append(ao.Errors, err)
return
}
labels.DebugEnabled = debugLog.DebugFlag
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. Got it. This code is a bit too clever IMHO, but I agree it's beyond the scope of this PR to fix. Can you add a comment before these lines to make it clear what's going on to my future self?

@@ -219,6 +220,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
ao.Errors = append(ao.Errors, err)
return
}
labels.DebugEnabled = debugLog.DebugFlag
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That way you can read and modify it in HoldAuction as needed and you can again access it outside of HoldAuction for setting the metrics label and such

A step in the right direction, but modifying it as a way to pass information upstream is still an undesirable design. We might have to tackle that later on.

@mansinahar
Copy link
Contributor

A step in the right direction, but modifying it as a way to pass information upstream is still an undesirable design. We might have to tackle that later on.

There's one other thing I think we should do here that is perhaps create a new function for recording debug enabled requests rather than using the current record request method. Then you can just call that record method in HoldAuction itself rather than calling it in each of the endpoint handlers. The debug enabled metrics anyway don't care about the source of the request. And this way we wouldn't be modifying it just to be able to pass the information back upstream. Also, debug info is per request so I think it makes sense to add it as part of the AuctionRequest struct.

@AlexBVolcy
Copy link
Contributor Author

AlexBVolcy commented May 19, 2022

The latest commit I pushed is the one where I implemented a function RecordDebugRequest to handle the recording of the new metrics. This function gets called now in HoldAuction so that way we wouldn't have to be concerned with passing the debug information found in HoldAuction upstream, and instead can just pass the debug enabled info directly to RecordDebugRequest.

And as a heads up, I wrongfully titled the name of this commit to be Debug Log Refactoring. The DebugLog object is still a separate parameter in HoldAuction, and hasn't been combined with the AuctionRequest object yet. I discussed this with Mansi, and this will be handled in a future PR.

Copy link
Contributor

@VeronikaSolovei9 VeronikaSolovei9 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

}

am := me.getAccountMetrics(pubID)
if !me.MetricsDisabled.AccountDebug && debugEnabled {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can simplify this by moving this block of code into the if condition on L438 as well. This will help get rid of the debugEnabled check here. Also, shouldn't we check that the publisher ID isn't unknown here as well like you're doing for Prometheus's record debug function?

Copy link
Contributor Author

@AlexBVolcy AlexBVolcy May 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I'll refactor this and add the unknown check as well!

endpoints/openrtb2/amp_auction.go Show resolved Hide resolved
metrics/go_metrics.go Show resolved Hide resolved
@@ -182,6 +184,10 @@ func NewMetrics(cfg config.PrometheusMetrics, disabledMetrics config.DisabledMet
"Count of total requests to Prebid Server labeled by type and status.",
[]string{requestTypeLabel, requestStatusLabel})

metrics.requestsDebug = newCounterWithoutLabels(cfg, reg,
"requests_debug",
"Count of total requests to Prebid Server that have debug enabled labled by type, status.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are not going to be labeled by type and status so please update the description accordingly

@@ -370,6 +376,11 @@ func NewMetrics(cfg config.PrometheusMetrics, disabledMetrics config.DisabledMet
"Count of total requests to Prebid Server labeled by account.",
[]string{accountLabel})

metrics.accountDebugRequests = newCounter(cfg, reg,
"account_requests_debug",
"Count of total requests to Prebid Server where the account has debug enabled labeled by account.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please update the description here to be something along the lines of:

Count of total requests to Prebid Server that have debug enabled labled by account

@@ -487,6 +487,9 @@ type DisabledMetrics struct {
// True if we want to stop collecting account-to-adapter metrics
AccountAdapterDetails bool `mapstructure:"account_adapter_details"`

// True if we want to stop collecting account debug request metrics
AccountDebug bool `mapstructure:"account_debug"`

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add tests for this new config option in config_test.go

metrics/go_metrics_test.go Show resolved Hide resolved
metrics/go_metrics_test.go Outdated Show resolved Hide resolved
metrics/prometheus/prometheus_test.go Show resolved Hide resolved
Copy link
Contributor

@mansinahar mansinahar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is looking pretty close. Thanks for addressing all the feedback. I have one major comment about the different types being used for account vs non-account metric in Prometheus. The other two are quite minor

metrics/prometheus/prometheus.go Show resolved Hide resolved
expectedDebugCount float64
}{
{
description: "Boolean flags should result in both metrics being updated",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please specify what the boolean flags are set to in the description. For readability purposes and for when tests fail these descriptions help us easily understand what was passed in and what was expected. I like the descriptions you have for the last two test cases. Could you please update these to have the same format as well?

I noticed the same thing in the go_metrics_test.go file as well

@@ -611,6 +612,7 @@ func TestFullConfig(t *testing.T) {
cmpBools(t, "account_required", cfg.AccountRequired, true)
cmpBools(t, "auto_gen_source_tid", cfg.AutoGenSourceTID, false)
cmpBools(t, "account_adapter_details", cfg.Metrics.Disabled.AccountAdapterDetails, true)
cmpBools(t, "account_debug", cfg.Metrics.Disabled.AccountDebug, true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the TestFullConfig I would suggest setting the value for account_debug to be false (different from the default value) in the above fullConfig value and assert it here to be false as well. This just helps us test through both the flow with default values and non-default values.

Copy link
Contributor

@VeronikaSolovei9 VeronikaSolovei9 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@mansinahar mansinahar merged commit ce57f41 into prebid:master May 25, 2022
jorgeluisrocha pushed a commit to jwplayer/prebid-server that referenced this pull request Sep 28, 2022
shunj-nb pushed a commit to ParticleMedia/prebid-server that referenced this pull request Nov 8, 2022
@SyntaxNode SyntaxNode mentioned this pull request Dec 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants