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

feat: add service target fields support to http and grpc requests #1287

Merged
merged 19 commits into from
Sep 27, 2022

Conversation

kruskall
Copy link
Member

@kruskall kruskall commented Aug 6, 2022

currently blocked by #1315

@apmmachine
Copy link
Contributor

apmmachine commented Aug 6, 2022

💚 Build Succeeded

the below badges are clickable and redirect to their specific view in the CI or DOCS
Pipeline View Test View Changes Artifacts preview preview

Expand to view the summary

Build stats

  • Start Time: 2022-09-23T19:03:09.301+0000

  • Duration: 58 min 57 sec

Test stats 🧪

Test Results
Failed 0
Passed 8554
Skipped 201
Total 8755

🤖 GitHub comments

Expand to view the GitHub comments

To re-run your PR in the CI, just comment with:

  • /test : Re-trigger the build.

  • run benchmark tests : Run the benchmark test.

  • run elasticsearch-ci/docs : Re-trigger the docs validation. (use unformatted text in the comment!)

@apmmachine
Copy link
Contributor

apmmachine commented Aug 10, 2022

🌐 Coverage report

Name Metrics % (covered/total) Diff
Packages 100.0% (59/59) 💚
Files 99.346% (152/153) 👍
Classes 96.275% (336/349) 👍
Methods 90.405% (961/1063) 👍 0.094
Lines 82.213% (11199/13622) 👎 -0.022
Conditionals 100.0% (0/0) 💚

spancontext.go Outdated
@@ -218,6 +218,10 @@ func (c *SpanContext) SetHTTPRequest(req *http.Request) {
Name: destinationServiceURL.String(),
Resource: destinationServiceResource,
})
c.SetServiceTarget(ServiceTargetSpanContext{
Type: "http",
Copy link
Member

Choose a reason for hiding this comment

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

According to https://github.com/elastic/apm/blob/main/specs/agents/tracing-spans-service-target.md#field-values:

span.context.service.target.type should have the same value as span.subtype and fallback to span.type.

This would mean, for example, Elasticsearch spans would have service.target.type: elasticsearch (which is desirable). According to the pseudocode, we should only set Type if it is empty - and then, only to the span subtype or type.

What I'd suggest is we leave it empty here, and then set the default in Span.End()

Copy link
Member Author

Choose a reason for hiding this comment

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

Note: I was planning to do the fallback mechanism in a separate PR: #1294

This would mean, for example, Elasticsearch spans would have service.target.type: elasticsearch (which is desirable). According to the pseudocode, we should only set Type if it is empty - and then, only to the span subtype or type.

The current approach was mostly based on what destination.resource was doing. In particular, Elasticsearch would overwrite the service target fields (and the old destination resource) because it is calling SetServiceTarget after SetHTTPRequest.
Another option is to move the destination resource and service target fields outside of SetHTTPRequest and leave the responsibility of setting them to the caller.

What I'd suggest is we leave it empty here, and then set the default in Span.End()

I think this approach won't work: span.End will only set the service target fields (or destination resource) if those fields weren't explicitly set (see the PR linked above for the code, it's based on what destination resource was doing). We could add another toggle/field to differentiate whether span.End should overwrite the fields but I feel like it would make the logic less robust.

WDYT ?

spancontext.go Outdated Show resolved Hide resolved
@kruskall kruskall requested a review from axw September 14, 2022 14:38
Copy link
Member

@axw axw left a comment

Choose a reason for hiding this comment

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

LGTM, thanks. Let's do the same thing for grpc. Can you please open an issue to omit service.target for non-exit spans?

@@ -83,6 +83,10 @@ func NewUnaryClientInterceptor(o ...ClientOption) grpc.UnaryClientInterceptor {
Resource: url.Host,
})
}
span.Context.SetServiceTarget(apm.ServiceTargetSpanContext{
Type: "grpc",
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
Type: "grpc",

Copy link
Member Author

Choose a reason for hiding this comment

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

Same issue, test is failing because it's not an exit span. Should we update this in the issue to omitservice.target for non-exit spans?

Copy link
Member

Choose a reason for hiding this comment

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

Same issue, test is failing because it's not an exit span. Should we update this in the issue to omitservice.target for non-exit spans?

Ah, and we don't have a way to mark some gRPC spans as exit spans yet...

Yes, let's just update this PR to omit for non-exit spans.

Copy link
Member Author

Choose a reason for hiding this comment

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

The span context doesn't know if the parent span is an exit span. I'm not sure how to proceed, should we add a field to the span context ? I'd prefer to avoid that as it feels like coupling different components

Copy link
Member

Choose a reason for hiding this comment

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

should we add a field to the span context ? I'd prefer to avoid that as it feels like coupling different components

I also don't think we add the exit flag as a field. Here's a couple of options that come to mind:

  1. Update Span.End to do something like this:
if s.exit {
        // The span was created as an exit span, but the user did not
        // manually set the service.target fields.
        s.setExitSpanServiceTarget()
} else {
        s.clearExitSpanServiceTarget()
}

Maybe like this:

func (s *Span) End() {
    ...
    s.setExitSpanServiceTarget()
    ...
}

func (s *Span) setExitSpanServiceTarget() {
    if !s.exit {
        // Clear any manually set `service.target.*` context.
        s.Context.SetServiceTarget(ServiceTargetSpanContext{})
        return
    }

    fallbackType := s.Subtype 
    if fallbackType == "" {
        fallbackType = s.Type
    }

    // Service target fields explicitly provided.
    if s.Context.setServiceTargetCalled {
    ...
}

func (sc *SpanContext) SetServiceTarget(target ServiceTargetSpanContext) {
    c.setServiceTargetCalled = true
    if service.Name == "" {
        s.model.Service = nil
        return
    }
    c.serviceTarget.Type = truncateString(service.Type)
    c.serviceTarget.Name = truncateString(service.Name)
    c.service.Target = &c.serviceTarget
    c.model.Service = &c.service
}
  1. We could pass span.exit into SpanContext.build() in buildModelSpan:

out.Context = sd.Context.build()

And build() would clear the model.SpanContext.Service field for non-exit spans.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thank you for help! 🙇

Quick question before pushing more changes: do we have a way to mark grpc/http requests as exit spans ? The latest change is going to reduce coverage of this feature because the spans used in testing are not exit spans.

Copy link
Member

Choose a reason for hiding this comment

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

Quick question before pushing more changes: do we have a way to mark grpc/http requests as exit spans ? The latest change is going to reduce coverage of this feature because the spans used in testing are not exit spans.

Not at the moment. For gRPC, we always assume that the target service is instrumented and pass trace context. Thus it should not be considered an "exit" span. I think we should make this configurable. I opened an issue about doing this for apmhttp spans recently, and have just updated it to also take apmgrpc client spans into account: #1304

Copy link
Member

Choose a reason for hiding this comment

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

@kruskall sorry, I led you astray due to my own confusion around exit spans and trace-context propagation. We should be marking HTTP, gRPC, Elasticsearch, and some other spans as "exit spans" by default. See #1315

Copy link
Member Author

Choose a reason for hiding this comment

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

No worries 😄

I think this PR grew in scope quite a lot, I've spli the fix in a separate PR: #1317

@kruskall kruskall requested a review from axw September 15, 2022 07:56
@simitt simitt added the blocked label Sep 19, 2022
@kruskall kruskall removed the blocked label Sep 23, 2022
Copy link
Member

@axw axw left a comment

Choose a reason for hiding this comment

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

🎉 🎉 🎉

LGTM, thanks for working through all the interconnected issues :)

@kruskall kruskall merged commit b4e40b3 into elastic:main Sep 27, 2022
@kruskall kruskall deleted the feat/service-target-http branch September 27, 2022 07:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants