-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Observability support for gateway #2715
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
Conversation
pom.xml
Outdated
@@ -57,10 +57,26 @@ | |||
<spring-cloud-circuitbreaker.version>3.0.0-SNAPSHOT</spring-cloud-circuitbreaker.version> | |||
<spring-cloud-commons.version>4.0.0-SNAPSHOT</spring-cloud-commons.version> | |||
<testcontainers.version>1.17.3</testcontainers.version> | |||
<micrometer.version>1.10.0-SNAPSHOT</micrometer.version> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What versions does boot manage?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1.10.0-M3
right now but I have an open PR for M4
.
They also want to move to SNAPSHOT
~in September I guess.
How does relate to spring cloud sleuth? |
Only indirectly as the new micrometer functionality replaces sleuth |
if (context.getCarrier() == null) { | ||
return keyValues; | ||
} | ||
// TODO: URI is high cardinality |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have the pattern/template available instead of the raw URI?
// TODO: URI is high cardinality | ||
keyValues = keyValues.and(METHOD.withValue(context.getRequest().getMethod().name()), URI.withValue(context.getRequest().getURI().toString())); | ||
if (context.getResponse() != null && context.getResponse().getStatusCode() != null) { | ||
keyValues = keyValues.and(STATUS.withValue(String.valueOf(context.getResponse().getStatusCode().value()))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can lead to an interesting issue, please bear with me.
If the status code does not present (e.g.: timeout?) this will not add a status key-value which means that in those scenarios this tag will be missing from the Timer
.
This can cause issues in certain backends (i.e.: Prometheus) since it will lead to time series where the name is the same but the tags are different, e.g.:
http_client_requests{method="GET", status="200"} ...
http_client_requests{method="GET"} ...
So I guess my question is: will the status code always be there if we already initiated the call (so we are not handling the LongTaskTimer case)?
If so, with some default value (0
, -1
), we are good. But if not, I would do this:
else {
keyValues = keyValues.and(STATUS.withValue("n/a"));
}
...gframework/cloud/gateway/filter/headers/observation/DefaultGatewayObservationConvention.java
Outdated
Show resolved
Hide resolved
...ringframework/cloud/gateway/filter/headers/observation/ObservedRequestHttpHeadersFilter.java
Outdated
Show resolved
Hide resolved
...ringframework/cloud/gateway/filter/headers/observation/ObservedRequestHttpHeadersFilter.java
Outdated
Show resolved
Hide resolved
if (parentObservation != null) { | ||
childObservation = childObservation.parentObservation(parentObservation); | ||
} | ||
childObservation = childObservation.start(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a comment about where to find stop so that whoever reads this will be able to easily navigate to the other half of this?
...ingframework/cloud/gateway/filter/headers/observation/ObservedResponseHttpHeadersFilter.java
Outdated
Show resolved
Hide resolved
if (log.isDebugEnabled()) { | ||
log.debug("The response was handled for observation " + childObservation); | ||
} | ||
childObservation.stop(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a comment here that tells the reader where to find start
?
* @author Marcin Grzejszczak | ||
* @since 3.0.0 | ||
*/ | ||
public class ObservedResponseHttpHeadersFilter implements HttpHeadersFilter { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if there was a network error and we don't have a response (e.g.: timeout, connection reset, etc)?
88f2497
to
26282f9
Compare
26282f9
to
709ee78
Compare
} | ||
ServerWebExchange exchange = context.getExchange(); | ||
Map<String, String> uriTemplateVariables = ServerWebExchangeUtils.getUriTemplateVariables(exchange); | ||
String url = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are two options, the one used here or:
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
URI routeUri = route.getUri();
This is GATEWAY_REQUEST_URL_ATTR
is merged with the request URI, the one from the Route
object is as configured.
if (log.isDebugEnabled()) { | ||
log.debug("Will instrument the HTTP request headers " + newHeaders); | ||
} | ||
Observation parentObservation = exchange.getAttribute("micrometer.observation"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does "micrometer.observation"
come from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It comes from https://github.com/micrometer-metrics/micrometer/blob/main/micrometer-observation/src/main/java/io/micrometer/observation/contextpropagation/ObservationThreadLocalAccessor.java#L37 - we can make a direct reference to that but then the context propagation dependency would be a mandatory one. I don't really see a problem with that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Builds are failing with
java.lang.ClassCastException: class java.net.URI cannot be cast to class java.lang.String (java.net.URI and java.lang.String are in module java.base of loader 'bootstrap')
at org.springframework.cloud.gateway.filter.headers.observation.DefaultGatewayObservationConvention.getLowCardinalityKeyValues(DefaultGatewayObservationConvention.java:54) ~[classes/:na]
I added the following dependency Configured the But when I log any message, no traceId/SpanId is completed. Should I do any additional step/configuration ? |
Check this doc https://micrometer.io/docs/observation#instrumentation_of_reactive_libraries and if you don't want to do anything manually you have to wait until we patch reactor and boot to support this out of the box |
@marcingrzejszczak I added;
but traceIds do not appear in the logs. I created a sample project to reproduce. |
No description provided.