Skip to content

Commit 4169cb7

Browse files
authored
Emit transaction.data inside contexts.trace.data (#3936)
1 parent ba2b968 commit 4169cb7

File tree

12 files changed

+149
-34
lines changed

12 files changed

+149
-34
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Fixes
66

7+
- Emit transaction.data inside contexts.trace.data ([#3936](https://github.com/getsentry/sentry-dotnet/pull/3936))
78
- Native SIGSEGV errors resulting from managed NullReferenceExceptions are now suppressed on Android ([#3903](https://github.com/getsentry/sentry-dotnet/pull/3903))
89
- OTel activities that are marked as not recorded are no longer sent to Sentry ([#3890](https://github.com/getsentry/sentry-dotnet/pull/3890))
910
- Fixed envelopes with oversized attachments getting stuck in __processing ([#3938](https://github.com/getsentry/sentry-dotnet/pull/3938))

src/Sentry/IHasData.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Sentry;
2+
3+
/// <summary>
4+
/// Implemented by objects that contain a map of untyped additional metadata.
5+
/// </summary>
6+
public interface IHasData
7+
{
8+
/// <summary>
9+
/// An arbitrary mapping of additional metadata to store with the event.
10+
/// </summary>
11+
IReadOnlyDictionary<string, object?> Data { get; }
12+
13+
/// <summary>
14+
/// Sets an extra.
15+
/// </summary>
16+
void SetData(string key, object? value);
17+
}

src/Sentry/ISpanData.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace Sentry;
55
/// <summary>
66
/// Immutable data belonging to a span.
77
/// </summary>
8-
public interface ISpanData : ITraceContext, IHasTags, IHasExtra
8+
public interface ISpanData : ITraceContext, IHasData, IHasTags, IHasExtra
99
{
1010
/// <summary>
1111
/// Start timestamp.

src/Sentry/Internal/NoOpSpan.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ protected NoOpSpan()
1919
public bool? IsSampled => default;
2020
public IReadOnlyDictionary<string, string> Tags => ImmutableDictionary<string, string>.Empty;
2121
public IReadOnlyDictionary<string, object?> Extra => ImmutableDictionary<string, object?>.Empty;
22+
public IReadOnlyDictionary<string, object?> Data => ImmutableDictionary<string, object?>.Empty;
2223
public DateTimeOffset StartTimestamp => default;
2324
public DateTimeOffset? EndTimestamp => default;
2425
public bool IsFinished => default;
@@ -71,6 +72,10 @@ public void SetExtra(string key, object? value)
7172
{
7273
}
7374

75+
public void SetData(string key, object? value)
76+
{
77+
}
78+
7479
public SentryTraceHeader GetTraceHeader() => SentryTraceHeader.Empty;
7580

7681
public IReadOnlyDictionary<string, Measurement> Measurements => ImmutableDictionary<string, Measurement>.Empty;

src/Sentry/Protocol/Trace.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,21 @@ internal set
5050
/// <inheritdoc />
5151
public bool? IsSampled { get; internal set; }
5252

53+
private Dictionary<string, object?> _data = new();
54+
55+
/// <summary>
56+
/// Get the metadata
57+
/// </summary>
58+
public IReadOnlyDictionary<string, object?> Data => _data;
59+
60+
/// <summary>
61+
/// Adds metadata to the trace
62+
/// </summary>
63+
/// <param name="key"></param>
64+
/// <param name="value"></param>
65+
public void SetData(string key, object? value)
66+
=> _data[key] = value;
67+
5368
/// <summary>
5469
/// Clones this instance.
5570
/// </summary>
@@ -63,7 +78,8 @@ internal set
6378
Operation = Operation,
6479
Origin = Origin,
6580
Status = Status,
66-
IsSampled = IsSampled
81+
IsSampled = IsSampled,
82+
_data = _data.ToDict()
6783
};
6884

6985
/// <summary>
@@ -103,6 +119,7 @@ public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger)
103119
writer.WriteString("origin", Origin ?? Internal.OriginHelper.Manual);
104120
writer.WriteStringIfNotWhiteSpace("description", Description);
105121
writer.WriteStringIfNotWhiteSpace("status", Status?.ToString().ToSnakeCase());
122+
writer.WriteDictionaryIfNotEmpty("data", _data, logger);
106123

107124
writer.WriteEndObject();
108125
}
@@ -120,6 +137,7 @@ public static Trace FromJson(JsonElement json)
120137
var description = json.GetPropertyOrNull("description")?.GetString();
121138
var status = json.GetPropertyOrNull("status")?.GetString()?.Replace("_", "").ParseEnum<SpanStatus>();
122139
var isSampled = json.GetPropertyOrNull("sampled")?.GetBoolean();
140+
var data = json.GetPropertyOrNull("data")?.GetDictionaryOrNull() ?? new();
123141

124142
return new Trace
125143
{
@@ -130,7 +148,8 @@ public static Trace FromJson(JsonElement json)
130148
Origin = origin,
131149
Description = description,
132150
Status = status,
133-
IsSampled = isSampled
151+
IsSampled = isSampled,
152+
_data = data
134153
};
135154
}
136155
}

src/Sentry/SentrySpan.cs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,26 @@ public void UnsetTag(string key) =>
6666
(_tags ??= new Dictionary<string, string>()).Remove(key);
6767

6868
// Aka 'data'
69-
private Dictionary<string, object?>? _extra;
7069
private readonly MetricsSummary? _metricsSummary;
7170

71+
72+
private Dictionary<string, object?>? _data;
73+
74+
/// <inheritdoc />
75+
public IReadOnlyDictionary<string, object?> Data =>
76+
_data ??= new Dictionary<string, object?>();
77+
78+
/// <inheritdoc />
79+
public void SetData(string key, object? value) =>
80+
(_data ??= new Dictionary<string, object?>())[key] = value;
81+
7282
/// <inheritdoc />
73-
public IReadOnlyDictionary<string, object?> Extra => _extra ??= new Dictionary<string, object?>();
83+
[Obsolete("Use SetData")]
84+
public IReadOnlyDictionary<string, object?> Extra => Data;
7485

7586
/// <inheritdoc />
76-
public void SetExtra(string key, object? value) =>
77-
(_extra ??= new Dictionary<string, object?>())[key] = value;
87+
[Obsolete("Use Data")]
88+
public void SetExtra(string key, object? value) => SetData(key, value);
7889

7990
/// <summary>
8091
/// Initializes an instance of <see cref="SentrySpan"/>.
@@ -100,7 +111,7 @@ public SentrySpan(ISpan tracer)
100111
Description = tracer.Description;
101112
Status = tracer.Status;
102113
IsSampled = tracer.IsSampled;
103-
_extra = tracer.Extra.ToDict();
114+
_data = tracer.Data.ToDict();
104115

105116
if (tracer is SpanTracer spanTracer)
106117
{
@@ -138,7 +149,7 @@ public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger)
138149
writer.WriteString("start_timestamp", StartTimestamp);
139150
writer.WriteStringIfNotNull("timestamp", EndTimestamp);
140151
writer.WriteStringDictionaryIfNotEmpty("tags", _tags!);
141-
writer.WriteDictionaryIfNotEmpty("data", _extra!, logger);
152+
writer.WriteDictionaryIfNotEmpty("data", _data!, logger);
142153
writer.WriteDictionaryIfNotEmpty("measurements", _measurements, logger);
143154
writer.WriteSerializableIfNotNull("_metrics_summary", _metricsSummary, logger);
144155

@@ -173,7 +184,7 @@ public static SentrySpan FromJson(JsonElement json)
173184
Status = status,
174185
IsSampled = isSampled,
175186
_tags = tags!,
176-
_extra = data!,
187+
_data = data!,
177188
_measurements = measurements,
178189
};
179190
}

src/Sentry/SentryTransaction.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,6 @@ public IReadOnlyList<string> Fingerprint
179179
/// <inheritdoc />
180180
public IReadOnlyCollection<Breadcrumb> Breadcrumbs => _breadcrumbs;
181181

182-
// Not readonly because of deserialization
183-
private Dictionary<string, object?> _extra = new();
184-
185-
/// <inheritdoc />
186-
public IReadOnlyDictionary<string, object?> Extra => _extra;
187-
188182
// Not readonly because of deserialization
189183
private Dictionary<string, string> _tags = new();
190184

@@ -270,7 +264,6 @@ public SentryTransaction(ITransactionTracer tracer)
270264
Sdk = tracer.Sdk;
271265
Fingerprint = tracer.Fingerprint;
272266
_breadcrumbs = tracer.Breadcrumbs.ToList();
273-
_extra = tracer.Extra.ToDict();
274267
_tags = tracer.Tags.ToDict();
275268

276269
_spans = FromTracerSpans(tracer);
@@ -339,8 +332,20 @@ public void AddBreadcrumb(Breadcrumb breadcrumb) =>
339332
_breadcrumbs.Add(breadcrumb);
340333

341334
/// <inheritdoc />
335+
public IReadOnlyDictionary<string, object?> Data => _contexts.Trace.Data;
336+
337+
/// <inheritdoc />
338+
[Obsolete("Use Data")]
339+
public IReadOnlyDictionary<string, object?> Extra => _contexts.Trace.Data;
340+
341+
/// <inheritdoc />
342+
[Obsolete("Use SetData")]
342343
public void SetExtra(string key, object? value) =>
343-
_extra[key] = value;
344+
SetData(key, value);
345+
346+
/// <inheritdoc />
347+
public void SetData(string key, object? value) =>
348+
_contexts.Trace.SetData(key, value);
344349

345350
/// <inheritdoc />
346351
public void SetTag(string key, string value) =>
@@ -401,7 +406,6 @@ public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger)
401406
writer.WriteSerializable("sdk", Sdk, logger);
402407
writer.WriteStringArrayIfNotEmpty("fingerprint", _fingerprint);
403408
writer.WriteArrayIfNotEmpty("breadcrumbs", _breadcrumbs, logger);
404-
writer.WriteDictionaryIfNotEmpty("extra", _extra, logger);
405409
writer.WriteStringDictionaryIfNotEmpty("tags", _tags!);
406410
writer.WriteArrayIfNotEmpty("spans", _spans, logger);
407411
writer.WriteDictionaryIfNotEmpty("measurements", _measurements, logger);
@@ -459,7 +463,6 @@ public static SentryTransaction FromJson(JsonElement json)
459463
Sdk = sdk,
460464
_fingerprint = fingerprint,
461465
_breadcrumbs = breadcrumbs,
462-
_extra = extra,
463466
_tags = tags,
464467
_measurements = measurements,
465468
_spans = spans

src/Sentry/SpanTracer.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,17 @@ public void UnsetTag(string key) =>
8585
private readonly ConcurrentDictionary<string, object?> _data = new();
8686

8787
/// <inheritdoc />
88-
public IReadOnlyDictionary<string, object?> Extra => _data;
88+
public IReadOnlyDictionary<string, object?> Data => _data;
8989

9090
/// <inheritdoc />
91-
public void SetExtra(string key, object? value) => _data[key] = value;
91+
public void SetData(string key, object? value) =>
92+
_data[key] = value;
93+
94+
/// <inheritdoc />
95+
public IReadOnlyDictionary<string, object?> Extra => Data;
96+
97+
/// <inheritdoc />
98+
public void SetExtra(string key, object? value) => SetData(key, value);
9299

93100
internal Func<bool>? IsFiltered { get; set; }
94101

src/Sentry/TransactionTracer.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,15 @@ public IReadOnlyList<string> Fingerprint
157157
/// <inheritdoc />
158158
public IReadOnlyCollection<Breadcrumb> Breadcrumbs => _breadcrumbs;
159159

160-
private readonly ConcurrentDictionary<string, object?> _extra = new();
160+
private readonly ConcurrentDictionary<string, object?> _data = new();
161161

162162
/// <inheritdoc />
163-
public IReadOnlyDictionary<string, object?> Extra => _extra;
163+
[Obsolete("Use Data")]
164+
public IReadOnlyDictionary<string, object?> Extra => _data;
165+
166+
/// <inheritdoc />
167+
public IReadOnlyDictionary<string, object?> Data => _data;
168+
164169

165170
private readonly ConcurrentDictionary<string, string> _tags = new();
166171

@@ -270,7 +275,11 @@ internal TransactionTracer(IHub hub, ITransactionContext context, TimeSpan? idle
270275
public void AddBreadcrumb(Breadcrumb breadcrumb) => _breadcrumbs.Add(breadcrumb);
271276

272277
/// <inheritdoc />
273-
public void SetExtra(string key, object? value) => _extra[key] = value;
278+
[Obsolete("Use SetData")]
279+
public void SetExtra(string key, object? value) => _data[key] = value;
280+
281+
/// <inheritdoc />
282+
public void SetData(string key, object? value) => _data[key] = value;
274283

275284
/// <inheritdoc />
276285
public void SetTag(string key, string value) => _tags[key] = value;

test/Sentry.Tests/Protocol/SentryTransactionTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ public void SerializeObject_AllPropertiesSetToNonDefault_SerializesValidObject()
207207
"category",
208208
BreadcrumbLevel.Warning));
209209

210-
transaction.SetExtra("extra_key", "extra_value");
210+
transaction.SetData("extra_key", "extra_value");
211211
transaction.Fingerprint = new[] { "fingerprint" };
212212
transaction.SetTag("tag_key", "tag_value");
213213
transaction.SetMeasurement("measurement_1", 111);
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System.Text.Json.Nodes;
2+
3+
namespace Sentry.Tests;
4+
5+
public partial class SerializationTests
6+
{
7+
private readonly IDiagnosticLogger _testOutputLogger;
8+
9+
public SerializationTests(ITestOutputHelper output)
10+
{
11+
_testOutputLogger = new TestOutputDiagnosticLogger(output);
12+
}
13+
14+
[Fact]
15+
public void Serialization_TransactionAndSpanData()
16+
{
17+
var hub = Substitute.For<IHub>();
18+
var context = new TransactionContext("name", "operation", new SentryTraceHeader(SentryId.Empty, SpanId.Empty, false));
19+
var transactionTracer = new TransactionTracer(hub, context);
20+
var span = transactionTracer.StartChild("childop");
21+
span.SetData("span1", "value1");
22+
23+
var transaction = new SentryTransaction(transactionTracer)
24+
{
25+
IsSampled = false
26+
};
27+
transaction.SetData("transaction1", "transaction_value");
28+
var json = transaction.ToJsonString(_testOutputLogger);
29+
_testOutputLogger.LogDebug(json);
30+
31+
var node = JsonNode.Parse(json);
32+
var dataNode = node?["contexts"]?["trace"]?["data"]?["transaction1"]?.GetValue<string>();
33+
dataNode.Should().NotBeNull("contexts.trace.data.transaction1 not found");
34+
dataNode.Should().Be("transaction_value");
35+
36+
var spansNode = node?["spans"]?.AsArray();
37+
spansNode.Should().NotBeNull("spans not found");
38+
var spanDataNode = spansNode!.FirstOrDefault()?["data"]?["span1"]?.GetValue<string>();
39+
spanDataNode.Should().NotBeNull("spans.data not found");
40+
spanDataNode.Should().Be("value1");
41+
42+
// verify deserialization
43+
var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json));
44+
var el = JsonElement.ParseValue(ref reader);
45+
var backTransaction = SentryTransaction.FromJson(el);
46+
47+
backTransaction.Spans.First().Data["span1"].Should().Be("value1", "Span value missing");
48+
backTransaction.Contexts.Trace.Data["transaction1"].Should().Be("transaction_value", "Transaction value missing");
49+
}
50+
}

test/Sentry.Tests/SerializationTests.verify.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,8 @@
44

55
namespace Sentry.Tests;
66

7-
public class SerializationTests
7+
public partial class SerializationTests
88
{
9-
private readonly IDiagnosticLogger _testOutputLogger;
10-
11-
public SerializationTests(ITestOutputHelper output)
12-
{
13-
_testOutputLogger = new TestOutputDiagnosticLogger(output);
14-
}
15-
169
[Theory]
1710
[MemberData(nameof(GetData))]
1811
public async Task Serialization(string name, object target)

0 commit comments

Comments
 (0)