-
Notifications
You must be signed in to change notification settings - Fork 860
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
Http spans #466
Http spans #466
Conversation
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.
Thanks for tackling this!
I think I'd keep scope a separate (optional) concept to this new API, and have HttpServerTracer
return and work directly on HttpServerSpan
, and remove HttpServerSpanWithScope
.
SpanWithScope
is primarily an auto-instrumentation detail to make it easier to pass them both between @OnMethodEnter
and @OnMethodExit
(in the future we could potentially get rid of it altogether using an extra @Local
bytebuddy parameter, reducing memory allocations).
For now, we could add a generic version of SpanWithScope
, so we can use e.g. SpanWithScope<HttpServerSpan>
, or we could start adding the extra @Local
now, migrating away from SpanWithScope
altogether.
return new HttpServerSpanWithScope(span, currentContextWith(span)); | ||
} | ||
|
||
protected HttpServerSpan findExistingSpan(REQUEST request) { |
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.
would be nice for the names of these two methods to reflect that they are opposites, e.g. something like
findExistingSpan
--> getSpan(REQUEST)
persistSpanToRequest
--> setSpan(REQUEST, HttpServerSpan)
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.
probably even attach/detach
. And #465 will change that anyway.
return throwable instanceof ExecutionException ? throwable.getCause() : throwable; | ||
} | ||
|
||
public static <C> SpanContext extract(final C carrier, final HttpTextFormat.Getter<C> getter) { |
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.
public static <C> SpanContext extract(final C carrier, final HttpTextFormat.Getter<C> getter) { | |
private static <C> SpanContext extract(final C carrier, final HttpTextFormat.Getter<C> getter) { |
//tasks.withType(Test) { | ||
// forkEvery = 1 | ||
//} |
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.
was this just for local testing? i'm not opposed to trying this again, but let's do it in a separate PR
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.
Yes, that is purely for local testing. I decided to post this draft PR as it is for faster feedback instead of taking 2 more days for polishing it
|
||
@AutoService(Instrumenter.class) | ||
public class GrizzlyHttpHandlerInstrumentation extends Instrumenter.Default { | ||
|
||
public static final GrizzlyHttpServerTracer TRACER = new GrizzlyHttpServerTracer(); |
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.
I think putting this here is going to give you muzzle
problems, because it will cause GrizzlyHttpHandlerInstrumentation
and all of its dependencies (e.g. bytebuddy) to be pulled into the transitive closure of what's needed to run the advice
spanWithScope.closeScope(); | ||
} | ||
|
||
public void end(HttpServerSpan span, RESPONSE response) { |
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.
We may end up wanting something more general, but for now we are only capturing statusCode
, so I think this would simplify things (e.g. removes need ResponseWithStatus
)
public void end(HttpServerSpan span, RESPONSE response) { | |
public void end(HttpServerSpan span, int statusCode) { |
//TODO I think this is wrong. | ||
//We must report that response status that was actually sent to end user | ||
//We may change span status, but not http_status attribute |
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.
I believe the problem here is that if a servlet throws an exception, the servlet container will then set response status to 500, so 500 really is the status code that the user receives.
//TODO Specification does not recommend using this attribute | ||
//See https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md#http-server-semantic-conventions |
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.
👍
//TODO why? | ||
request.setAttribute("traceId", span.getTraceId().toLowerBase16()); | ||
request.setAttribute("spanId", span.getSpanId().toLowerBase16()); |
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 is for adding traceId
and spanId
to access logs, so you can correlate access logs with traces (e.g. https://tomcat.apache.org/tomcat-8.0-doc/config/valve.html#Access_Log_Valve/Attributes).
span.setAttribute(Tags.HTTP_STATUS, 500); | ||
span.setStatus(Status.UNKNOWN); | ||
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) { | ||
TRACER.setPrincipal((HttpServletRequest) request); |
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.
You have faithfully translated the existing logic, but I think the existing logic is incorrect here, wdyt?
TRACER.setPrincipal((HttpServletRequest) request); | |
if (spanWithScope != null) { | |
TRACER.setPrincipal(spanWithScope.getSpan()); | |
} |
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.
Existing logic has a comment "set principal no matter who created the span". If that was the goal, then existing code is correct. But I don't know yet, if the goal was correct.
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.
Yeah, I'm confused by that comment as well, but I don't think that comment is related to that particular logic of not looking at the current span. If you look back in the history:
that comment existed when it used to apply to the current span at one time.
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.
But then it still should be not spanWithScope
but Tracer.getCurrentSpan()
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.
They are the same in this case (the span was just put into scope above, so will be the same span returned by getCurrentSpan
)
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) { | ||
final HttpServletRequest req = (HttpServletRequest) request; | ||
final HttpServletResponse resp = (HttpServletResponse) response; | ||
TRACER.setPrincipal((HttpServletRequest) request); |
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.
similar to above, wdyt?
TRACER.setPrincipal((HttpServletRequest) request); | |
TRACER.setPrincipal(spanWithScope.getSpan()); |
I don't know the requirements for the instrumentation project, but I find strange that no attribute is set before the Span starts. This way, Samplers that want to take a decision on specific attributes (e.g. to only trace a business-critical path) will not work! |
You are absolutely correct on this. There is even #388 for that. |
@@ -24,6 +24,7 @@ testSets { | |||
|
|||
dependencies { | |||
compileOnly group: 'javax.servlet', name: 'servlet-api', version: '2.2' | |||
compile(project(':instrumentation:servlet:servlet-common')) |
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.
@trask can you advice me? I currently get java.lang.ClassNotFoundException: io.opentelemetry.auto.instrumentation.servlet.ServletHttpServerTracer
during smoke tests. How instrumentations can share classes in general? If they can...
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.
I got it :)
@iNikem can we close this? |
This draft/POC PR try to solve too many concerns at once but here it is...
First, it solves #423 by extracting all common functionality of servlet instrumentation into one common place. So far Servlet 2, Servlet 3 and Grizzly were de-duplicated.
Second, that common place mention above is
io.opentelemetry.auto.typed.server.http.HttpServerTracer
. It together with more specific subclasses aims to provide a single place where all http server instrumentations can use common functionality. It does more than currentHttpServerDecorator
and exposes smaller API.Third, it demonstrates rudimental
HttpServerSpan
. This is an example of semantic span that was a focus of #195, #229 and open-telemetry/opentelemetry-java#964I tried to follow these guidelines:
HttpServerTracer
knows the "shape" of a specific semantic span, fixed values of some attributes (e.g. SpanKind) and how and from where to extract other values. E.g. server traces know about incoming http requests and responses but delegate to subclasses to deal with specific libraries, such as Servlet or Project Reactor.