-
-
Notifications
You must be signed in to change notification settings - Fork 203
/
Span.cs
171 lines (141 loc) · 5.95 KB
/
Span.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
using Sentry.Extensibility;
using Sentry.Internal;
using Sentry.Internal.Extensions;
using Sentry.Protocol;
namespace Sentry;
// https://develop.sentry.dev/sdk/event-payloads/span
/// <summary>
/// Transaction span.
/// </summary>
public class Span : ISpanData, IJsonSerializable
{
/// <inheritdoc />
public SpanId SpanId { get; private set; }
/// <inheritdoc />
public SpanId? ParentSpanId { get; private set; }
/// <inheritdoc />
public SentryId TraceId { get; private set; }
/// <inheritdoc />
public DateTimeOffset StartTimestamp { get; private set; } = DateTimeOffset.UtcNow;
/// <inheritdoc />
public DateTimeOffset? EndTimestamp { get; private set; }
/// <inheritdoc />
public bool IsFinished => EndTimestamp is not null;
// Not readonly because of deserialization
private Dictionary<string, Measurement>? _measurements;
private Dictionary<string, Measurement> LazyMeasurements => _measurements ??= new();
/// <inheritdoc />
public IReadOnlyDictionary<string, Measurement> Measurements => LazyMeasurements;
/// <inheritdoc />
public void SetMeasurement(string name, Measurement measurement) =>
LazyMeasurements[name] = measurement;
/// <inheritdoc />
public string Operation { get; set; }
/// <inheritdoc />
public string? Description { get; set; }
/// <inheritdoc />
public SpanStatus? Status { get; set; }
/// <inheritdoc />
public bool? IsSampled { get; internal set; }
private Dictionary<string, string>? _tags;
/// <inheritdoc />
public IReadOnlyDictionary<string, string> Tags => _tags ??= new Dictionary<string, string>();
/// <inheritdoc />
public void SetTag(string key, string value) =>
(_tags ??= new Dictionary<string, string>())[key] = value;
/// <inheritdoc />
public void UnsetTag(string key) =>
(_tags ??= new Dictionary<string, string>()).Remove(key);
// Aka 'data'
private Dictionary<string, object?>? _extra;
/// <inheritdoc />
public IReadOnlyDictionary<string, object?> Extra => _extra ??= new Dictionary<string, object?>();
/// <inheritdoc />
public void SetExtra(string key, object? value) =>
(_extra ??= new Dictionary<string, object?>())[key] = value;
/// <summary>
/// Initializes an instance of <see cref="Span"/>.
/// </summary>
public Span(SpanId? parentSpanId, string operation)
{
SpanId = SpanId.Create();
ParentSpanId = parentSpanId;
TraceId = SentryId.Create();
Operation = operation;
}
/// <summary>
/// Initializes an instance of <see cref="Span"/>.
/// </summary>
public Span(ISpan tracer)
: this(tracer.ParentSpanId, tracer.Operation)
{
SpanId = tracer.SpanId;
TraceId = tracer.TraceId;
StartTimestamp = tracer.StartTimestamp;
EndTimestamp = tracer.EndTimestamp;
Description = tracer.Description;
Status = tracer.Status;
IsSampled = tracer.IsSampled;
_extra = tracer.Extra.ToDictionary();
_measurements = tracer.Measurements.ToDictionary();
_tags = tracer is SpanTracer s ? s.InternalTags?.ToDictionary() : tracer.Tags.ToDictionary();
}
/// <inheritdoc />
public SentryTraceHeader GetTraceHeader() => new(
TraceId,
SpanId,
IsSampled);
/// <inheritdoc />
public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger)
{
writer.WriteStartObject();
writer.WriteSerializable("span_id", SpanId, logger);
writer.WriteSerializableIfNotNull("parent_span_id", ParentSpanId, logger);
writer.WriteSerializable("trace_id", TraceId, logger);
writer.WriteStringIfNotWhiteSpace("op", Operation);
writer.WriteStringIfNotWhiteSpace("description", Description);
writer.WriteStringIfNotWhiteSpace("status", Status?.ToString().ToSnakeCase());
writer.WriteString("start_timestamp", StartTimestamp);
writer.WriteStringIfNotNull("timestamp", EndTimestamp);
writer.WriteStringDictionaryIfNotEmpty("tags", _tags!);
writer.WriteDictionaryIfNotEmpty("data", _extra!, logger);
writer.WriteDictionaryIfNotEmpty("measurements", _measurements, logger);
writer.WriteEndObject();
}
/// <summary>
/// Parses a span from JSON.
/// </summary>
public static Span FromJson(JsonElement json)
{
var spanId = json.GetPropertyOrNull("span_id")?.Pipe(SpanId.FromJson) ?? SpanId.Empty;
var parentSpanId = json.GetPropertyOrNull("parent_span_id")?.Pipe(SpanId.FromJson);
var traceId = json.GetPropertyOrNull("trace_id")?.Pipe(SentryId.FromJson) ?? SentryId.Empty;
var startTimestamp = json.GetProperty("start_timestamp").GetDateTimeOffset();
var endTimestamp = json.GetProperty("timestamp").GetDateTimeOffset();
var operation = json.GetPropertyOrNull("op")?.GetString() ?? "unknown";
var description = json.GetPropertyOrNull("description")?.GetString();
var status = json.GetPropertyOrNull("status")?.GetString()?.Replace("_", "").ParseEnum<SpanStatus>();
var isSampled = json.GetPropertyOrNull("sampled")?.GetBoolean();
var tags = json.GetPropertyOrNull("tags")?.GetStringDictionaryOrNull()?.ToDictionary();
var measurements = json.GetPropertyOrNull("measurements")?
.GetDictionaryOrNull(Measurement.FromJson) ?? new();
var data = json.GetPropertyOrNull("data")?.GetDictionaryOrNull()?.ToDictionary();
return new Span(parentSpanId, operation)
{
SpanId = spanId,
TraceId = traceId,
StartTimestamp = startTimestamp,
EndTimestamp = endTimestamp,
Description = description,
Status = status,
IsSampled = isSampled,
_tags = tags!,
_extra = data!,
_measurements = measurements
};
}
internal void Redact()
{
Description = Description?.RedactUrl();
}
}