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

Added step withSpanAttributes to set the attributes also on child spans, added setSpanAttributes for setting the attribute only on the target #827

Conversation

christophe-kamphaus-jemmic
Copy link
Contributor

@christophe-kamphaus-jemmic christophe-kamphaus-jemmic commented Mar 19, 2024

The withSpanAttributes step was added. This new step sets the attributes on all child spans created inside the body block. This is implemented using the StepContext.

setSpanAttributes was added to set attribute on the target without a body block similar to the existing withSpanAttribute.
setSpanAttributes is preferred over withSpanAttribute for consistency with other plugins (eg. withCredentials).

Fixes #173

The main difficulty in implementing a solution for #173 was how to efficiently

  • get child nodes of the target
    • I did not find an easy API for this.
    • I would have to either walk the whole flow graph or keep the span hierarchy somehow in memory. The OpenTelemetry API does not expose the parent context ID that I could find, so that was also not feasible.
    • In the end I just used the few otelTraceService.getSpan to set the attributes of some child spans of the root span and documented the limitations of the current implementation.
  • remember the hierarchy of withSpanAttribute calls, so that new spans can efficiently set the required attributes
    • I solved this by adding the invisible action OpenTelemetryAttributesAction to the Run tracking child attributes for later agent and phase spans. The OpenTelemetryAttributesAction is added to the SpanContext to track attributes to be set to new spans within that context.
    • I adapted OpenTelemetryAttributesAction to be serializable to fix the error Failed to serialize OpenTelemetryAttributesAction.

Limitations

Some child spans that were created before the execution of setSpanAttributes / withSpanAttributes might be missed.
For example closed spans or some open child spans.
Child spans created after the execution of withSpanAttributes will all have the attribute set correctly.

Declarative pipelines can only use a single withSpanAttributes step inside the options block.

Potential future work (not in this PR)

Testing done

  • I added JUnit tests to verify the desired behavior.
    • Added test verifying the behavior of withSpanAttributes and setSpanAttributes
    • Added tests checks overriding behavior, ie. multiple withSpanAttributes steps with the same key.
  • Tested locally using the docker-compose in https://github.com/jenkinsci/opentelemetry-plugin/tree/main/demos
    • using withSpanAttributes([spanAttribute(key: 'build.tool', value: 'gradle', target: 'PIPELINE_ROOT_SPAN')]) { ... } and verified in Jaeger that all expected spans have this tag.

Submitter checklist

Preview Give feedback

This parameter is not yet functional, but it should be accepted in a
Jenkinsfile.
…nAttributeStep

This is to make it clear that it's testing the target parameter.
An additional withSpanAttribute asserts that the root span can be
edited from anywhere in the pipeline.
…nAttributeStepSetOn

The test pipeline sets the parameter setOn.
The asserts for setting of attributes on children (not yet implemented) are commented out.
Also we rename some variables to align with the attribute key being tested
This makes it possible to distinguish setAttribute performed by
withSpanAttribute and later setAttributes for child spans.
@christophe-kamphaus-jemmic
Copy link
Contributor Author

@cyrille-leclerc would you mind taking a look?

@christophe-kamphaus-jemmic
Copy link
Contributor Author

christophe-kamphaus-jemmic commented Mar 19, 2024

Should the new parameter setOn of the withSpanAttribute step be documented in a Markdown somewhere?

Should the new behavior of the withSpanAttribute step and the setSpanAttribute step be documented in a Markdown document somewhere?

@kuisathaverat
Copy link
Contributor

get child nodes of the target
I did not find an easy API for this.
I would have to either walk the whole flow graph or keep the span hierarchy somehow in memory. The OpenTelemetry API does not expose the parent context ID that I could find, so that was also not feasible.
In the end I just used the few otelTraceService.getSpan to set the attributes of some child spans of the root span and documented the limitations of the current implementation.

the trace.id identifies all the members in a transaction, so they are already related.

remember the hierarchy of withSpanAttribute calls, so that new spans can efficiently set the required attributes
I solved this by adding invisible actions AttributeSetterAction to the Run and FlowNode's in a way that we can easily retrieve using the getAncestors method
Is this a good way to do this?
I noticed that these AttributeSetterAction are serialized. I refactor the code a bit to fix the error Failed to serialize AttributeSetterAction#attributeKey". Should AttributeSetterAction be serialized?

Serialized and memory consumption are the possible issues

@kuisathaverat
Copy link
Contributor

Is this a good way to do this?

probably not; there are other steps like withCredentials that propagate something to lower levels in the pipeline like environment variables; they enclose the children into a closure to make it obvious that you are propagation something to your children, and I doubt they use actions to propagate the context.

https://www.jenkins.io/doc/pipeline/steps/credentials-binding/
https://github.com/jenkinsci/credentials-binding-plugin/blob/master/src/main/java/org/jenkinsci/plugins/credentialsbinding/impl/BindingStep.java

@christophe-kamphaus-jemmic
Copy link
Contributor Author

there are other steps like withCredentials that propagate something to lower levels in the pipeline like environment variables

I will take a look how they do it. Thanks for the links.

This will prevent more expensive iterations and listing of all actions of type AttributeSetterAction.
Also replace AttributeSetterAction by the existing OpenTelemetryAttributesAction.
OpenTelemetryAttributesAction was not yet used for Run, Build, FlowNode instances.
depending on whether it takes a closure (and sets the attribute on child
spans as well) or not.

Also splits the tests accordingly (WIP).
This way no span will be created for it same as for withSpanAttribute.
Instead of setting child span attributes from this action, the context's
OpenTelemetryAttributesAction is used.
We keep it only for setting it for the "Phase: Finalise" span.
@christophe-kamphaus-jemmic
Copy link
Contributor Author

christophe-kamphaus-jemmic commented Apr 15, 2024

Should the new behavior of the withSpanAttribute step and the setSpanAttribute step be documented in a Markdown document somewhere?

DONE

@christophe-kamphaus-jemmic
Copy link
Contributor Author

Declarative pipelines can only use a single withSpanAttribute step inside the options block.

To fix this limitation and to improve backwards compatibility, I could leave the behavior of withSpanAttribute unchanged and add a withSpanAttributes step that takes a list of attributes to be set.

What do you think?

After some reflection I've implemented this change.

withSpanAttribute is now backwards compatible.
The new withSpanAttributes can be used to set several span attributes on child spans. This also fixes the limitation of declarative pipelines only allowing a single step of a given type in the options block.

For consistency with other plugins (eg. withCredentials) and to better distinguish the behavior to withSpanAttributes I've added setSpanAttributes for only setting the given attributes on the target.

Should withSpanAttribute be marked as deprecated with a recommendation to use setSpanAttributes instead?

@christophe-kamphaus-jemmic christophe-kamphaus-jemmic changed the title Change withSpanAttribute to set the attribute also on child spans, added setSpanAttribute for setting the attribute only on the target Added step withSpanAttributes to set the attributes also on child spans, added setSpanAttributes for setting the attribute only on the target Apr 18, 2024
@kuisathaverat kuisathaverat added the enhancement New feature or request label Apr 19, 2024
@cyrille-leclerc
Copy link
Contributor

Thank you for the effort, it took a long time. I suggested to add an explanation through javadocs.

@kuisathaverat
Copy link
Contributor

The code LGTM, I have to find some time to make a manual test to verify there is nothing weird.

@christophe-kamphaus-jemmic
Copy link
Contributor Author

Any update @kuisathaverat @cyrille-leclerc ?

@kuisathaverat
Copy link
Contributor

I'm sorry. It is still on my plate, but I do not find time to test it. I will try to find some gap this week.

@kuisathaverat kuisathaverat merged commit 862c5d2 into jenkinsci:main May 29, 2024
14 checks passed
@christophe-kamphaus-jemmic christophe-kamphaus-jemmic deleted the 173-set-attributes-on-child-spans branch May 29, 2024 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support defining attributes at the job levels that are added to spans
3 participants