-
-
Notifications
You must be signed in to change notification settings - Fork 222
fix: include Data
set via ITransactionTracer
in SentryTransaction
#4148
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
base: main
Are you sure you want to change the base?
Changes from all commits
e6e6c9b
f57868e
31e9125
13c2a16
cb9e0c3
3db4876
e91052d
3745efe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -159,15 +159,12 @@ public IReadOnlyList<string> Fingerprint | |||
/// <inheritdoc /> | ||||
public IReadOnlyCollection<Breadcrumb> Breadcrumbs => _breadcrumbs; | ||||
|
||||
private readonly ConcurrentDictionary<string, object?> _data = new(); | ||||
|
||||
/// <inheritdoc /> | ||||
[Obsolete("Use Data")] | ||||
public IReadOnlyDictionary<string, object?> Extra => _data; | ||||
public IReadOnlyDictionary<string, object?> Extra => _contexts.Trace.Data; | ||||
|
||||
/// <inheritdoc /> | ||||
public IReadOnlyDictionary<string, object?> Data => _data; | ||||
|
||||
public IReadOnlyDictionary<string, object?> Data => _contexts.Trace.Data; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that if you're copying all objects from the collection while they are mutable and still on the scope, mutations done on the scope will be reflected on the transaction object resulting in possible inaccurate data being sent to Sentry, or potentially crashes when we read the data for serialization while the data is being mutated |
||||
|
||||
private readonly ConcurrentDictionary<string, string> _tags = new(); | ||||
|
||||
|
@@ -278,10 +275,10 @@ internal TransactionTracer(IHub hub, ITransactionContext context, TimeSpan? idle | |||
|
||||
/// <inheritdoc /> | ||||
[Obsolete("Use SetData")] | ||||
public void SetExtra(string key, object? value) => _data[key] = value; | ||||
public void SetExtra(string key, object? value) => _contexts.Trace.SetData(key, value); | ||||
|
||||
/// <inheritdoc /> | ||||
public void SetData(string key, object? value) => _data[key] = value; | ||||
public void SetData(string key, object? value) => _contexts.Trace.SetData(key, value); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: concurrency The now used
currently is a regular Dictionary`2 .Should this now become a ConcurrentDictionary`2 ?Because it's also the private readonly ConcurrentDictionary<string, object?> _data = new(); that this PR removes from this type?
Or is that an indicator that this fix is technically not quite right? Relates to #3936 (comment) and #3936 (comment) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The TransactionTracer is potentially accessed concurrently (we set it onto the Scope which has concurrent access in both global mode (every Throwing Concurrent around is not always the solution. In this case for sure not since we want SentryTrasaction to be a snapshot, so we need a deep clone here. We don't want just a clone of the collection if the items in the collection are mutable. I wrote a note above about this. If things on the context are immutable, things are easier though we could have data loss if we have concurrent updates without synchronization. |
||||
|
||||
/// <inheritdoc /> | ||||
public void SetTag(string key, string value) => _tags[key] = value; | ||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -22,7 +22,11 @@ | |||||
Origin: auto.http.aspnetcore, | ||||||
Description: , | ||||||
Status: Ok, | ||||||
IsSampled: true | ||||||
IsSampled: true, | ||||||
Data: { | ||||||
http.request.method: GET, | ||||||
http.response.status_code: 200 | ||||||
} | ||||||
Comment on lines
+26
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note: from
|
||||||
} | ||||||
}, | ||||||
User: { | ||||||
|
@@ -80,7 +84,11 @@ | |||||
route.controller: Version, | ||||||
route.version: 1.1 | ||||||
}, | ||||||
IsFinished: true | ||||||
IsFinished: true, | ||||||
Data: { | ||||||
http.request.method: GET, | ||||||
http.response.status_code: 200 | ||||||
} | ||||||
} | ||||||
} | ||||||
] | ||||||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -231,7 +231,7 @@ public void SerializeObject_AllPropertiesSetToNonDefault_SerializesValidObject() | |||
|
||||
// Act | ||||
var finalTransaction = new SentryTransaction(transaction); | ||||
var actualString = finalTransaction.ToJsonString(_testOutputLogger); | ||||
var actualString = finalTransaction.ToJsonString(_testOutputLogger, indented: true); | ||||
var actual = Json.Parse(actualString, SentryTransaction.FromJson); | ||||
|
||||
// Assert | ||||
|
@@ -244,6 +244,30 @@ public void SerializeObject_AllPropertiesSetToNonDefault_SerializesValidObject() | |||
|
||||
return o; | ||||
}); | ||||
|
||||
Assert.Contains($$""" | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note: I kept this test similar to
which also does a serialize/deserialize-roundtrip test plus an additional "Contains-JSON" check on the serialized string |
||||
"contexts": { | ||||
".NET Framework": { | ||||
".NET Framework": "\u0022v2.0.50727\u0022, \u0022v3.0\u0022, \u0022v3.5\u0022", | ||||
".NET Framework Client": "\u0022v4.8\u0022, \u0022v4.0.0.0\u0022", | ||||
".NET Framework Full": "\u0022v4.8\u0022" | ||||
}, | ||||
"context_key": "context_value", | ||||
"trace": { | ||||
"type": "trace", | ||||
"span_id": "{{context.SpanId}}", | ||||
"parent_span_id": "{{context.ParentSpanId}}", | ||||
"trace_id": "{{context.TraceId}}", | ||||
"op": "op123", | ||||
"origin": "auto.serialize.transaction", | ||||
"description": "desc123", | ||||
"status": "aborted", | ||||
"data": { | ||||
"extra_key": "extra_value" | ||||
} | ||||
} | ||||
} | ||||
""", actualString); | ||||
} | ||||
|
||||
[Fact] | ||||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: reference Contexts in
SentryTransaction
vs copy Contexts toSentryTransaction
When creating a
SentryTransaction
fromITransactionTracer
, we reference theSentryContexts
:sentry-dotnet/src/Sentry/SentryTransaction.cs
Line 246 in 3745efe
Should we instead copy the Contexts (via e.g.
SentryContexts.Clone
)?Or is that an indicator that this fix here is not quite right, technically?
Should we maybe, rather than have
SetData
writing intoSentryContexts.Trace.Data
directly, instead when creating theSentryTransaction
from theITransactionTracer
copy theITransactionTracer.Data
(backed by it's ownDictionary`2
) over to the Context of the newSentryTransaction
.E.g. something like
or similar.
But that would have a side-effect on the
Contexts
ofTransactionTracer
, which I don't think is expected when creating a newSentryTransaction
fromTransactionTracer
.Should we perhaps indeed make a copy of the
Trace
and/orSentryContext
?Relates to #3936 (comment)