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 the ability to use span links when consuming a message amqp plugin #1972

Merged

Conversation

McSick
Copy link
Contributor

@McSick McSick commented Feb 28, 2024

Which problem is this PR solving?

  • Per the spec on consumer spans, they should link to the message's creation context. The current behavior is instead continuing the trace where the message was produced. This can cause traces to be quite large for apps that produce a lot of messages. This PR offer's a config option to follow the spec more closely.

Short description of the changes

  • A config option was added that when set to true, it follows the spec behavior to link to the produce context instead of continuing the context. The default behavior will be as it is today, so one has to opt-in to this change via the config setting.

@McSick McSick requested a review from a team February 28, 2024 17:22
Copy link

linux-foundation-easycla bot commented Feb 28, 2024

CLA Signed

The committers listed above are authorized under a signed CLA.

Copy link
Member

@dyladan dyladan left a comment

Choose a reason for hiding this comment

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

I assume this was made a configuration because it is a breaking change. We will probably try to go to this as the default behavior in a future version when the messaging semconv stabilizes. For now, I think this is a decent compromise.

Can you please add tests for the new behavior?

Copy link

codecov bot commented Mar 4, 2024

Codecov Report

Attention: Patch coverage is 91.66667% with 1 line in your changes missing coverage. Please review.

Project coverage is 90.42%. Comparing base (dfb2dff) to head (9184cc3).
Report is 188 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1972      +/-   ##
==========================================
- Coverage   90.97%   90.42%   -0.56%     
==========================================
  Files         146      148       +2     
  Lines        7492     7333     -159     
  Branches     1502     1524      +22     
==========================================
- Hits         6816     6631     -185     
- Misses        676      702      +26     
Files Coverage Δ
plugins/node/instrumentation-amqplib/src/types.ts 100.00% <ø> (ø)
...lugins/node/instrumentation-amqplib/src/amqplib.ts 90.42% <91.66%> (+0.02%) ⬆️

... and 62 files with indirect coverage changes

@McSick
Copy link
Contributor Author

McSick commented Mar 5, 2024

I assume this was made a configuration because it is a breaking change. We will probably try to go to this as the default behavior in a future version when the messaging semconv stabilizes. For now, I think this is a decent compromise.

Can you please add tests for the new behavior?

I added in tests for the new option. And yeah agree with you in the future, likely we will switch over the default. This gives us an easy way to do that as well. Let me know if you need anything else! I ran the linter and there are quite a few warnings that for code I did not touch. If you need me to correct those, let me know.

@McSick McSick requested a review from dyladan March 6, 2024 17:59
@dyladan
Copy link
Member

dyladan commented Mar 14, 2024

I ran the linter and there are quite a few warnings that for code I did not touch. If you need me to correct those, let me know.

Warnings are typically fine, especially if they're on code you didn't introduce. There are many hundreds across the codebase and requiring them to be fixed is a losing battle at this point. Mostly the linter just makes the code style consistent enough to make it easier to review.

@McSick
Copy link
Contributor Author

McSick commented Mar 18, 2024

I ran the linter and there are quite a few warnings that for code I did not touch. If you need me to correct those, let me know.

Warnings are typically fine, especially if they're on code you didn't introduce. There are many hundreds across the codebase and requiring them to be fixed is a losing battle at this point. Mostly the linter just makes the code style consistent enough to make it easier to review.

Perfect! the code is ready to be reviewed then.

@blumamir
Copy link
Member

This PR touches on 2 things:

  • parent-child relationship between producer and consumer
  • storing link to the producer on the consumer.

I wonder if they are mutually exclusive.

Always recording a link to the producer makes sense to me, and is not a breaking change.

regarding the parent-child relationship - I can see how some users would want it one way and others will want it the other way (regardless of the default value). perhaps the new config option can address this property instead? something like startNewTraceOnConsume?

`${queue} process`,
{
let span: Span;
if (self._config.useLinksForConsume) {
Copy link
Member

Choose a reason for hiding this comment

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

the 2 cases in the if-else contains some duplication.

WDYT about preparing the links array only once (with 0 or 1 elements) and the parent based on the instrumentation configuration, then just invoking self.tracer.startSpan once with these values? or at least calculate the attributes once, outside the if-else

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would a null parent be fine in this instance? Because with the config option, the parentcontext should not exists. If that's fine, then I can make those changes.

Copy link
Member

Choose a reason for hiding this comment

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

undefined is fine. It's the default anyway

Copy link
Member

Choose a reason for hiding this comment

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

I'd do something like this

        const links: Link[] = [];
        if (self._config.useLinksForConsume) {
          const parentSpanContext = trace.getSpan(parentContext)?.spanContext();
          if (parentSpanContext) {
            links.push({ context: parentSpanContext });
          }
        }

        const span = self.tracer.startSpan(`${queue} process`, {
          kind: SpanKind.CONSUMER,
          attributes: {
            ...channel?.connection?.[CONNECTION_ATTRIBUTES],
            [SemanticAttributes.MESSAGING_DESTINATION]: exchange,
            [SemanticAttributes.MESSAGING_DESTINATION_KIND]:
              MessagingDestinationKindValues.TOPIC,
            [SemanticAttributes.MESSAGING_RABBITMQ_ROUTING_KEY]:
              msg.fields?.routingKey,
            [SemanticAttributes.MESSAGING_OPERATION]:
              MessagingOperationValues.PROCESS,
            [SemanticAttributes.MESSAGING_MESSAGE_ID]:
              msg?.properties.messageId,
            [SemanticAttributes.MESSAGING_CONVERSATION_ID]:
              msg?.properties.correlationId,
          },
          links,
        },
        self._config.useLinksForConsume ? undefined : parentContext,
        );

Copy link
Member

Choose a reason for hiding this comment

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

@McSick looks like you ended up making the change for this, albeit a bit more verbose. I think it achieves the same thing though. As a nit I tend to lean toward the more succinct version but non blocking.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had to change it a little as parentContext was used deeper in the code so I wanted to explicitly make it undefined in this instance so the trace was not continued.

@dyladan
Copy link
Member

dyladan commented Mar 26, 2024

This PR touches on 2 things:

  • parent-child relationship between producer and consumer
  • storing link to the producer on the consumer.

I wonder if they are mutually exclusive.

I don't think they're exclusive. If you remove the parent-child link you lose all relationship without the link. If you add the link, it is a bit overlapping with the parent-child relationship and potentially confusing. Because this is opt-in, I think it is probably fine.

edit: Also, this is just implementing the currently specified behavior. It could be argued we don't have a choice.

Copy link
Member

@dyladan dyladan left a comment

Choose a reason for hiding this comment

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

Left a suggestion to remove some duplication. Didn't test it (typed in browser) so it might need some tweaks

`${queue} process`,
{
let span: Span;
if (self._config.useLinksForConsume) {
Copy link
Member

Choose a reason for hiding this comment

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

I'd do something like this

        const links: Link[] = [];
        if (self._config.useLinksForConsume) {
          const parentSpanContext = trace.getSpan(parentContext)?.spanContext();
          if (parentSpanContext) {
            links.push({ context: parentSpanContext });
          }
        }

        const span = self.tracer.startSpan(`${queue} process`, {
          kind: SpanKind.CONSUMER,
          attributes: {
            ...channel?.connection?.[CONNECTION_ATTRIBUTES],
            [SemanticAttributes.MESSAGING_DESTINATION]: exchange,
            [SemanticAttributes.MESSAGING_DESTINATION_KIND]:
              MessagingDestinationKindValues.TOPIC,
            [SemanticAttributes.MESSAGING_RABBITMQ_ROUTING_KEY]:
              msg.fields?.routingKey,
            [SemanticAttributes.MESSAGING_OPERATION]:
              MessagingOperationValues.PROCESS,
            [SemanticAttributes.MESSAGING_MESSAGE_ID]:
              msg?.properties.messageId,
            [SemanticAttributes.MESSAGING_CONVERSATION_ID]:
              msg?.properties.correlationId,
          },
          links,
        },
        self._config.useLinksForConsume ? undefined : parentContext,
        );

@blumamir
Copy link
Member

@McSick Thanks again for working on this.
Are you still interested in finalizing it? should we keep the PR open?

@McSick
Copy link
Contributor Author

McSick commented May 23, 2024

I do need to finalize it. Ive been pulled into some other things but will try to get it done within the next week.

@McSick
Copy link
Contributor Author

McSick commented May 29, 2024

@dyladan @blumamir , I went ahead and updated the code based on some feedback, tests also updated. Let me know if it looks good.

@McSick McSick requested review from dyladan and blumamir May 30, 2024 14:08
Copy link
Member

@JamieDanielson JamieDanielson left a comment

Choose a reason for hiding this comment

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

Thanks for working on this! Generally this looks good to me. Left a few notes though non-blocking from me. Also sorry for the churn but the readme needs to be updated based on some recent changes

`${queue} process`,
{
let span: Span;
if (self._config.useLinksForConsume) {
Copy link
Member

Choose a reason for hiding this comment

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

@McSick looks like you ended up making the change for this, albeit a bit more verbose. I think it achieves the same thing though. As a nit I tend to lean toward the more succinct version but non blocking.

plugins/node/instrumentation-amqplib/src/amqplib.ts Outdated Show resolved Hide resolved
plugins/node/instrumentation-amqplib/src/types.ts Outdated Show resolved Hide resolved
@McSick
Copy link
Contributor Author

McSick commented Jun 21, 2024

@JamieDanielson , went ahead and updated based on your suggestions and updated the readme to resolve the conflict.

Copy link
Member

@JamieDanielson JamieDanielson left a comment

Choose a reason for hiding this comment

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

@McSick Hmm I think something got weird with a merge from a remote branch somewhere along the way here... maybe this commit? I found one section of the README that should be removed, suggestion highlighted. I don't think there is anything in the other files but I need to take another look through.

plugins/node/instrumentation-amqplib/README.md Outdated Show resolved Hide resolved
@JamieDanielson
Copy link
Member

@McSick Hmm I think something got weird with a merge from a remote branch somewhere along the way here... maybe this commit? I found one section of the README that should be removed, suggestion highlighted. I don't think there is anything in the other files but I need to take another look through.

I went through and did some branch cleanup to check things out and it seems like this README change is the only change (aside from whitespace)... so I committed that and the tests are running. I included the git diff for my branch and yours below.

git diff
diff --git a/plugins/node/instrumentation-amqplib/README.md b/plugins/node/instrumentation-amqplib/README.md
index dcfac7fb..dd211d3a 100644
--- a/plugins/node/instrumentation-amqplib/README.md
+++ b/plugins/node/instrumentation-amqplib/README.md
@@ -51,7 +51,7 @@ registerInstrumentations({
 amqplib instrumentation has few options available to choose from. You can set the following:

 | Options              | Type                                           | Description                                                                         |
-| --------------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
+| -------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------- |
 | `publishHook`        | `AmqplibPublishCustomAttributeFunction`        | hook for adding custom attributes before publish message is sent.                   |
 | `publishConfirmHook` | `AmqplibPublishConfirmCustomAttributeFunction` | hook for adding custom attributes after publish message is confirmed by the broker. |
 | `consumeHook`        | `AmqplibConsumeCustomAttributeFunction`        | hook for adding custom attributes before consumer message is processed.             |
@@ -77,18 +77,6 @@ By default, consume spans continue the trace where a message was produced. Howev

 Default is false

-## Migration From opentelemetry-instrumentation-amqplib
-
-This instrumentation was originally published under the name `"opentelemetry-instrumentation-amqplib"` in [this repo](https://github.com/aspecto-io/opentelemetry-ext-js). Few breaking changes were made during porting to the contrib repo to align with conventions:
-
-### Hook Info
-
-The instrumentation's config `publishHook`, `publishConfirmHook`, `consumeHook` and `consumeEndHook` functions signature changed, so the second function parameter is info object, containing the relevant hook data.
-
-### `moduleVersionAttributeName` config option
-
-The `moduleVersionAttributeName` config option is removed. To add the amqplib package version to spans, use the `moduleVersion` attribute in hook info for `publishHook` and `consumeHook` functions.
-
 ## Running Tests Locally

 To run the tests locally, you need to have a RabbitMQ server running.  You can use the following command to start a RabbitMQ server using Docker: 

@JamieDanielson
Copy link
Member

@dyladan @blumamir do you have any remaining concerns on this PR that haven't been addressed? If not I think this is good to go.

@blumamir
Copy link
Member

@dyladan @blumamir do you have any remaining concerns on this PR that haven't been addressed? If not I think this is good to go.

Thank you @JamieDanielson , sorry for the delay, busy days at work 🥵
If you think it's good to go I am good with merging 👍

@JamieDanielson
Copy link
Member

The failed test is for browser and unrelated, looks like a flaky timeout issue. Rerunning just in case

@JamieDanielson JamieDanielson merged commit 5f2c160 into open-telemetry:main Jul 11, 2024
19 checks passed
@dyladan dyladan mentioned this pull request Jul 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants