Skip to content

Inconsistent normalization of Transaction/Span data #2646

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

Closed
rhcarvalho opened this issue Jun 4, 2020 · 9 comments · Fixed by #2655
Closed

Inconsistent normalization of Transaction/Span data #2646

rhcarvalho opened this issue Jun 4, 2020 · 9 comments · Fixed by #2655

Comments

@rhcarvalho
Copy link
Contributor

rhcarvalho commented Jun 4, 2020

Summary

  • Transaction.data is too easily truncated
  • Span.data is never normalized

image

Details

Introduction

The JS SDKs have a feature to normalize events before serialization that prevents serializing very deeply nested objects.

/**
* Maximum number of levels that normalization algorithm will traverse in objects and arrays.
* Used when normalizing an event before sending, on all of the listed attributes:
* - `breadcrumbs.data`
* - `user`
* - `contexts`
* - `extra`
* Defaults to `3`. Set to `0` to disable.
*/
normalizeDepth?: number;

This is a great feature, however it does not apply consistently to Transaction/Span data.

Problem

  • Transaction data is stored in Event.contexts.trace.data.
  • Span data is stored in Event.spans[].data.

Unlike for breadcrumbs that normalization applies only to the .data property of each breadcrumb, normalization is applied to Event.contexts as a whole:

return {
...event,
...(event.breadcrumbs && {
breadcrumbs: event.breadcrumbs.map(b => ({
...b,
...(b.data && {
data: normalize(b.data, depth),
}),
})),
}),
...(event.user && {
user: normalize(event.user, depth),
}),
...(event.contexts && {
contexts: normalize(event.contexts, depth),
}),
...(event.extra && {
extra: normalize(event.extra, depth),
}),
};
}

What that means in practice is that, without changes to normalizeDepth, it is impossible to store objects in Transaction.data and have them sent to Sentry. Transaction.data is indirectly forced to be a flat object.

Users unaware of normalizeDepth can hack it and JSON.stringify the values they send, but that gives worse user experience 2 times:

  1. Seeing [Object] where the expect some data; fix the frustration with JSON.stringify and then
  2. Having a string instead of proper JSON means we cannot pretty print the value in the UI:

Manually stringified object
image

Regular JSON object
image

Possible solutions

Considering that all documented context types are flat objects, it is possible that we could relax the way how normalization applies to Event.contexts.

The new contexts.trace is the only key under contexts that may have nested objects under contexts.trace.data.

Option 1: liberal

An obvious option to consistently handle Transaction/Span data is to remove normalization from contexts altogether.

Option 2: restrictive

Alternatively, normalization should:

  • not apply to contexts as a whole; and
  • apply only to contexts.trace.data; and
  • apply to spans[].data.

Option 3: Improve Contexts normalization

  • not apply to contexts as a whole; and
  • Instead, walk through all the keys in contexts and apply it to that values of the key

edit: this is not an option to fix the inconsistency, it would still treat Transaction.data and Span.data differently.

rhcarvalho added a commit that referenced this issue Jun 4, 2020
All of the context types documented in [1] are flat objects.

The new contexts.trace is the only key under contexts that may have
nested objects under contexts.trace.data. That value holds the data for
the Transaction Span and matches spans[].data for invidual Spans. At the
moment one of the data fields is heavily limited, while the other is
ignored by normalization.

[1]: https://develop.sentry.dev/sdk/event-payloads/contexts/

This is the simplest to implement fix to #2646, thus what I'm doing here
perhaps temporarily.

Fixes #2646
@HazAT
Copy link
Member

HazAT commented Jun 5, 2020

Option 3️⃣
Reasoning:
Option 1
changes the whole behavior of the SDK again -> We can drop whole normalization once we decided what to do with the payload issue.

Option 2
I don't want to apply this now to span.data since it wasn't there in the first place

Option 3
It fixes the current problem while leaving most of the stuff the same as it was before

@dashed
Copy link
Member

dashed commented Jun 5, 2020

This shouldn't affect how we should display this on product side if any of the 3 options are implemented, right?

@HazAT
Copy link
Member

HazAT commented Jun 5, 2020

@dashed nope

@rhcarvalho
Copy link
Contributor Author

@HazAT

Option 3 would not fix the inconsistency. Just delays the time until someone will hit the [Object] result on the transaction level, where it would otherwise not happen for the very same content stored in a span.

rhcarvalho added a commit that referenced this issue Jun 5, 2020
All of the context types documented in [1] are flat objects.

The new contexts.trace is the only key under contexts that may have
nested objects under contexts.trace.data. That value holds the data for
the Transaction Span and matches spans[].data for invidual Spans. At the
moment one of the data fields is heavily limited, while the other is
ignored by normalization.

[1]: https://develop.sentry.dev/sdk/event-payloads/contexts/

This is the simplest to implement fix to #2646, thus what I'm doing here
perhaps temporarily.

Fixes #2646
@kamilogorek
Copy link
Contributor

Related: #2539

rhcarvalho added a commit that referenced this issue Jun 5, 2020
All of the context types documented in [1] are flat objects.

The more recent addition contexts.trace is the only context type that
may have nested objects under contexts.trace.data. That value holds the
data for the Transaction Span and matches spans[].data for invidual
Spans.

Removing normalization is one solution to the inconsistency in which
data in Transactions is heavily limited, while data in Span is ignored
by normalization.

This is the simplest to implement fix to #2646, thus what we implement
here to evaluate. We expect that no other key under contexts will be
affected negatively by this change.

[1]: https://develop.sentry.dev/sdk/event-payloads/contexts/

Fixes #2646
@rhcarvalho
Copy link
Contributor Author

In light of hearing that Event.contexts may also be used to store arbitrary data similar to Event.extra, here comes a slightly more convoluted solution scoped only to transactions and spans:

Option 4

  • remember the original contexts.trace; and
  • apply normalization to contexts as a whole; and
  • overwrite contexts.trace with the original value prior to normalization.

The end result is normalization applies to every part of Event as before, except for contexts.trace.

The serialization of Transactions and Spans is then consistent, and neither Transaction.data nor Span.data get normalized.

rhcarvalho added a commit that referenced this issue Jun 5, 2020
All of the context types documented in [1] are flat objects.

The new contexts.trace is the only key under contexts that may have
nested objects under contexts.trace.data. That value holds the data for
the Transaction Span and matches spans[].data for invidual Spans. At the
moment one of the data fields is heavily limited, while the other is
ignored by normalization.

[1]: https://develop.sentry.dev/sdk/event-payloads/contexts/

This is the simplest to implement fix to #2646, thus what I'm doing here
perhaps temporarily.

Fixes #2646
rhcarvalho added a commit that referenced this issue Jun 8, 2020
All of the context types documented in [1] are flat objects.

The new contexts.trace is the only key under contexts that may have
nested objects under contexts.trace.data. That value holds the data for
the Transaction Span and matches spans[].data for invidual Spans. At the
moment one of the data fields is heavily limited, while the other is
ignored by normalization.

[1]: https://develop.sentry.dev/sdk/event-payloads/contexts/

This is the simplest to implement fix to #2646, thus what I'm doing here
perhaps temporarily.

Fixes #2646
rhcarvalho added a commit that referenced this issue Jun 8, 2020
This is such that setting data on a Transaction behaves the same as
setting data on a Span.

Fixes #2646.
rhcarvalho added a commit that referenced this issue Jun 8, 2020
All of the context types documented in [1] are flat objects.

The new contexts.trace is the only key under contexts that may have
nested objects under contexts.trace.data. That value holds the data for
the Transaction Span and matches spans[].data for invidual Spans. At the
moment one of the data fields is heavily limited, while the other is
ignored by normalization.

[1]: https://develop.sentry.dev/sdk/event-payloads/contexts/

This is the simplest to implement fix to #2646, thus what I'm doing here
perhaps temporarily.

Fixes #2646
@rhcarvalho
Copy link
Contributor Author

FTR, we merged an implementation of Option 4.

@dashed
Copy link
Member

dashed commented Jun 8, 2020

@rhcarvalho as an optimization, would it be better to delete event.contexts.trace after saving the original value, and before the normalization?

@rhcarvalho
Copy link
Contributor Author

@rhcarvalho as an optimization, would it be better to delete event.contexts.trace after saving the original value, and before the normalization?

Sounds good. I bet if we invest time on profiling the SDK we may find other/better optimizations in the hot path of events.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants