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

Add ExceptionEventData which wraps events that contain exceptions #4162

Merged
merged 10 commits into from
Mar 31, 2022

Conversation

HaloFour
Copy link
Contributor

@HaloFour HaloFour commented Feb 8, 2022

Adds ExceptionEventData interface extending EventData to represent an exception recorded to a span. Processors/exporters can access the exception directly to enable alternate formatting.

@codecov
Copy link

codecov bot commented Feb 8, 2022

Codecov Report

Merging #4162 (730ed87) into main (b9074f1) will increase coverage by 0.05%.
The diff coverage is 95.00%.

Impacted file tree graph

@@             Coverage Diff              @@
##               main    #4162      +/-   ##
============================================
+ Coverage     90.23%   90.28%   +0.05%     
- Complexity     4599     4758     +159     
============================================
  Files           537      558      +21     
  Lines         14082    14618     +536     
  Branches       1347     1402      +55     
============================================
+ Hits          12707    13198     +491     
- Misses          933      960      +27     
- Partials        442      460      +18     
Impacted Files Coverage Δ
...ava/io/opentelemetry/sdk/trace/data/EventData.java 100.00% <ø> (ø)
...ace/internal/data/ImmutableExceptionEventData.java 94.44% <94.44%> (ø)
.../main/java/io/opentelemetry/sdk/trace/SdkSpan.java 97.71% <100.00%> (-0.13%) ⬇️
...ry/sdk/trace/internal/data/ExceptionEventData.java 100.00% <100.00%> (ø)
...entelemetry/sdk/metrics/data/LongExemplarData.java 66.66% <0.00%> (-33.34%) ⬇️
...telemetry/sdk/metrics/data/DoubleExemplarData.java 66.66% <0.00%> (-33.34%) ⬇️
...porter/internal/otlp/traces/SpanLinkMarshaler.java 95.00% <0.00%> (-5.00%) ⬇️
...orter/internal/otlp/metrics/ExemplarMarshaler.java 91.83% <0.00%> (-3.82%) ⬇️
...emetry/sdk/testing/assertj/ExemplarDataAssert.java 87.50% <0.00%> (-2.98%) ⬇️
...etry/exporter/prometheus/PrometheusHttpServer.java 72.34% <0.00%> (-2.66%) ⬇️
... and 88 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b9074f1...730ed87. Read the comment docs.

@anuraaga
Copy link
Contributor

anuraaga commented Feb 9, 2022

This seems reasonable to me - we'd probably want to check on the spec repo if anyone else has any thoughts on how to format exceptions across languages


import io.opentelemetry.api.common.Attributes;

public interface ExceptionEventData extends EventData {
Copy link
Member

Choose a reason for hiding this comment

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

I believe this can be package private.

Copy link
Member

Choose a reason for hiding this comment

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

Oh nvm. It has to be public so that processors / exporters can check instanceof ExceptionEventData. This seems reasonable to me too.

If we wanted to be cautious we could put this interface in :sdk-extensions:tracing-incubator so users have to add a dependency on an unstable artifact in order to use it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The SDK would need to know of the interface in order for Span#recordException to add an event that implements it, wouldn't it?

Copy link
Contributor

Choose a reason for hiding this comment

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

What about putting this in an internal package for now, as we explicitly document anything in internal as being unstable. You would be free to use it, of course, accepting the risk of it changing in the future.

Copy link
Member

Choose a reason for hiding this comment

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

The SDK would need to know of the interface in order for Span#recordException to add an event that implements it, wouldn't it?

Yes, it could place an implementation dependency on :sdk-extensions:tracing-incubator.

What about putting this in an internal package for now, as we explicitly document anything in internal as being unstable.

That would work too, but probably better to not promote using internal classes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Finished pass at moving the ExceptionEventData interface and implementation to tracing-incubator. Added a helper class that looks for it on the classpath and will use the create static method to create instances, otherwise it defaults to the existing behavior. Not terribly thrilled with the manual use of reflection but it seems that MethodHandles and LambdaMetaFactory aren't available due to the target Android API version and desugaring seems to only apply to them when used with invokedynamic, but hopefully given this code is related to exceptions the overhead of invoking the method is relatively a drop in the bucket.

Also made the implementation of ExceptionEventData defer creating of the common attributes until the getAttributes method is called and the result is memoized, so existing exporters should work as expected if using this implementation. That behavior is certainly up for discussion.

Copy link
Member

Choose a reason for hiding this comment

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

Apologies for the confusion - I was describing for an approach where :sdk:trace took an implementation dependency on :sdk-extensions:tracing-incubator. That way, no reflection would be necessary since ExceptionEventData would always be on the classpath, but users would have to have their own dependency on :sd:tracing-incubator in order to use it.

I'm ok with the internal package approach as well, but not especially fond of the reflection approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

:sdk-extensions:tracing-incubator already takes a dependency on :sdk:trace, so I don't think that would be an option? If you'd prefer I can definitely move this code into an internal package in :sdk:trace, that would definitely be simpler, but also wouldn't allow anyone to opt-out unless we add some kind of config?

Copy link
Member

Choose a reason for hiding this comment

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

:sdk-extensions:tracing-incubator already takes a dependency on :sdk:trace, so I don't think that would be an option

Ah that's right. The internal package sounds good. Do users need to opt out?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do users need to opt out?

Not necessarily, just thought it was safer to be able to test it in isolation.

I can get the PR together to move these classes back to :sdk:tracing under an internal package.


private static ExceptionEventFactory initializeEventFactory() {
try {
Class<?> clazz =
Copy link
Contributor

Choose a reason for hiding this comment

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

I worry a bit about having this reflection here, which will get logged as a warning on every SDK startup on a JDK 11+. I'm not sure I have a better solution, though.

Copy link
Contributor Author

@HaloFour HaloFour Feb 16, 2022

Choose a reason for hiding this comment

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

I saw a similar mechanism is also being used for SDK autoconfigure. Unfortunately I can't rely on the better/safer APIs due to Android compatibility. If there is a better option I am all for it, but I can't think of one that doesn't involve putting some public interface in the SDK class.

@HaloFour HaloFour marked this pull request as ready for review February 18, 2022 01:22
@HaloFour HaloFour requested a review from a user February 18, 2022 01:22

@Override
@Memoized
public Attributes getAttributes() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public Attributes getAttributes() {
public final Attributes getAttributes() {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Memoized methods can't be final in AutoValue.

}

@Override
@Memoized
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to point out this changes the getAttributes for exceptions from a normal field to volatile field in the new codepath. I think it's OK just want to make sure everyone notices it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Going for the stretch goal of having ExceptionEventData not generate those attributes unless the exporter tries to use them. Assuming the exporter only calls getAttributes once it's probably not actually necessary to memoize them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If there is hesitation in deferring the generation of those attributes I can definitely back that off. When the interface was in tracing-incubation I felt it was worth exploring. The only attribute I'd prefer to avoid generating is exception.stacktrace as in my Spring codebases they can be quite large, but I don't think it'd a big deal to consider that later.

*
* @return the additional {@link Attributes attributes} of the {@link ExceptionEventData}
*/
Attributes getAdditionalAttributes();
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this can be a package private method on ImmutableExceptionEventData instead of on this interface

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As with that stretch goal I wanted to enable an exporter to access the additional attributes without forcing the semantic attributes for the exception to be created.

Copy link
Contributor

@anuraaga anuraaga left a comment

Choose a reason for hiding this comment

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

Sorry for delay thought had already hit approve

@HaloFour
Copy link
Contributor Author

Just checking in to see what the thoughts are about this PR.

Copy link

@ghost ghost left a comment

Choose a reason for hiding this comment

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

Looks good, thanks!

Copy link
Member

@jack-berg jack-berg left a comment

Choose a reason for hiding this comment

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

Any reason not to merge this?

@anuraaga anuraaga merged commit 6bc06f8 into open-telemetry:main Mar 31, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants