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

golang: allow accessing {request,response}/{headers,trailer} in the OnLog phase #34810

Merged
merged 2 commits into from
Aug 14, 2024

Conversation

spacewander
Copy link
Contributor

Commit Message: allow accessing {request,response}/{headers,trailer} in the OnLog phase
Additional Description:
Risk Level: Low
Testing: Integration
Docs Changes:
Release Notes: Done
Platform Specific Features:
[Optional Runtime guard:]
[Optional Fixes #Issue]
[Optional Fixes commit #PR or SHA]
[Optional Deprecated:]
[Optional API Considerations:]

Copy link

As a reminder, PRs marked as draft will not be automatically assigned reviewers,
or be handled by maintainer-oncall triage.

Please mark your PR as ready when you want it to be reviewed!

🐱

Caused by: #34810 was opened by spacewander.

see: more, trace.

@spacewander spacewander marked this pull request as ready for review June 19, 2024 11:19
@spacewander
Copy link
Contributor Author

CC @doujiang24
Please take a look.

@wbpcode wbpcode assigned wangfakang and doujiang24 and unassigned wangfakang Jun 20, 2024
Copy link
Member

@doujiang24 doujiang24 left a comment

Choose a reason for hiding this comment

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

There is a related issute #33873 to get headers at any stage, but I'm not against to get header in log phase for now.

is_golang_processing_log_ = true;
dynamic_lib_->envoyGoFilterOnHttpLog(req_, int(log_context.accessLogType()));
is_golang_processing_log_ = false;
decoding_state_.setFilterState(FilterState::ProcessingLog);
Copy link
Member

Choose a reason for hiding this comment

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

we'd better not overwrite the state?
Processing Log and Process DecodeXXX/EncodeXXX may run concurrently in Golang side.
use another flag, is_golang_processing_log_, could be much clear, thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@doujiang24
What about adding a pair of new methods:

  • setProcessingLog
  • isProcessingLog

So we can check if we are processing Go in the instance of ProcessorState.

Copy link
Member

Choose a reason for hiding this comment

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

Do you mean still write the logging state into decoding/encoding_state?
I don't think it is a good idea, logging state does not belongs to decoding/encoding, it would be some kind of hacky and make the code a bit complicated.
A new flag could be much clear.

},
}

var reqTrailer *requestTrailerMapImpl
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
var reqTrailer *requestTrailerMapImpl
var reqTrailer RequestTrailerMap

it will be nil as default value, so that we do not need those lines start at line 354?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@doujiang24
Thanks, updated.

@@ -276,16 +284,97 @@ func envoyGoFilterOnHttpLog(r *C.httpRequest, logType uint64) {

v := api.AccessLogType(logType)

// Request headers must exist because the HTTP filter won't be run if the headers are
// not sent yet.
reqHeader := &requestHeaderMapImpl{
Copy link
Member

Choose a reason for hiding this comment

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

better add a TODO commet: it's better to make it readonly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

@tyxia
Copy link
Member

tyxia commented Jun 21, 2024

/wait

@spacewander
Copy link
Contributor Author

There is a related issute #33873 to get headers at any stage, but I'm not against to get header in log phase for now.

Getting headers at any stage is well but it requires an extra event loop to fetch the headers from the Envoy, which will add ~100 microseconds. So using it in the per-request hot path is not a good choice.

@spacewander
Copy link
Contributor Author

There is a related issute #33873 to get headers at any stage, but I'm not against to get header in log phase for now.

Getting headers at any stage is well but it requires an extra event loop to fetch the headers from the Envoy, which will add ~100 microseconds. So using it in the per-request hot path is not a good choice.

Getting request info in the log phase is a common requirement in API Gateway, for example, nearly 15% plugins of APISIX use this feature. So it would be better to avoid ~200 microseconds of extra latency (from both req and resp headers).

@spacewander
Copy link
Contributor Author

There is a related issute #33873 to get headers at any stage, but I'm not against to get header in log phase for now.

Getting headers at any stage is well but it requires an extra event loop to fetch the headers from the Envoy, which will add ~100 microseconds. So using it in the per-request hot path is not a good choice.

Forget this, it's my mistake. Getting headers in the log phase doesn't require an extra event loop because the OnLog can't be run nonblockingly.

case api.AccessLogDownstreamEnd:
f.OnLog()
f.OnLog(reqHeader, reqTrailer, respHeader, respTrailer)
Copy link
Member

Choose a reason for hiding this comment

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

How about use the Envoy C++ API style:

OnLog(logContext)
OnLogDownstreamPeriodic(logContext)
OnLogDownstreamStart(logContext)

type logContext interface {
    requestHeaders() RequestHeaderMap
    ...
}

benifits:

  1. we do not need to pre create four interface values even people won't use it. (we do only need one instead)
  2. API fexible, we can add more APIs into logContext in the feature.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@doujiang24
I wonder how to connect this with the goal in #33873.
If we add the methods in the logContext, then we will have two different implementations to get request headers:
one can only use in OnLog phase and the other is used in the Encode phase.

Copy link
Member

Choose a reason for hiding this comment

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

This is just API style suggestion, no relation to #33873.
The implementations could keep the same as now, just user face API tweaks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@doujiang24
It's not just user face API tweaks. It also affects how we implement it internally. If we bind the requestHeaders to the logContext, then we need to get the header from the logContext, which is unavailable in Encode phases. However, if we decide to support RequestHeaders in StreamInfo, and now we have two FilterCallbacks, we will probably fetch the headers from the XXFilterCallback directly.

Copy link
Member

Choose a reason for hiding this comment

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

This is my thought:

  1. save the all of the parameters into logContext, i.e. decodingState, encodingState ...
  2. create a RequestHeaderMap in logContext.requestHeaders, and etc.

we do not need any C/cgo related changes, just Go code tweaks, thoughts?

@mattklein123 mattklein123 self-assigned this Jun 25, 2024
Copy link

This pull request has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed in 7 days if no further activity occurs. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions!

@github-actions github-actions bot added the stale stalebot believes this issue/PR has not been touched recently label Jul 26, 2024
@spacewander
Copy link
Contributor Author

@doujiang24
What about creating new decodingState and encodingState in logContext?

We could not copy the States simply, as we may run both the encode header or something similar and the log phase, it could be not very clear to share the same state as they may affect each other. For example, they should use different phases to indicate which one is called.

@github-actions github-actions bot removed the stale stalebot believes this issue/PR has not been touched recently label Jul 29, 2024
Copy link
Member

@doujiang24 doujiang24 left a comment

Choose a reason for hiding this comment

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

otherwise, lgtm

return
}

reqHeader.Set("a", "b")
Copy link
Member

Choose a reason for hiding this comment

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

we'd better not write header in test? we comment they will be readonly: https://github.com/envoyproxy/envoy/pull/34810/files#r1648874832

uint64_t resp_trailer_num = 0;
uint64_t resp_trailer_bytes = 0;

auto decoding_s = dynamic_cast<processState*>(&decoding_state_);
Copy link
Member

Choose a reason for hiding this comment

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

style: decoding_state could be better.

@@ -266,7 +266,13 @@ func envoyGoFilterOnHttpData(s *C.processState, endStream, buffer, length uint64
}

//export envoyGoFilterOnHttpLog
func envoyGoFilterOnHttpLog(r *C.httpRequest, logType uint64) {
func envoyGoFilterOnHttpLog(r *C.httpRequest, logType uint64,
decodingS *C.processState, encodingS *C.processState,
Copy link
Member

Choose a reason for hiding this comment

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

style: decodingState could be better.

@doujiang24
Copy link
Member

/retest

@doujiang24
Copy link
Member

Copy link
Member

@doujiang24 doujiang24 left a comment

Choose a reason for hiding this comment

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

lgtm

@spacewander
Copy link
Contributor Author

@doujiang24
Sorry for the push -f. I have to use this way to solve the DCO failure.

@spacewander
Copy link
Contributor Author

/retest

@spacewander
Copy link
Contributor Author

@doujiang24
Copy link
Member

/retest

@mattklein123 mattklein123 merged commit 69ee8cf into envoyproxy:main Aug 14, 2024
47 checks passed
asingh-g pushed a commit to asingh-g/envoy that referenced this pull request Aug 20, 2024
spacewander added a commit to mosn/envoy that referenced this pull request Sep 6, 2024
duxin40 pushed a commit to duxin40/envoy that referenced this pull request Oct 15, 2024
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.

5 participants