Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Sentry/BindableSentryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ internal partial class BindableSentryOptions
public TimeSpan? InitCacheFlushTimeout { get; set; }
public Dictionary<string, string>? DefaultTags { get; set; }
public bool? EnableTracing { get; set; }
public TraceLifeCycle? TraceLifeCycle { get; set; }
public double? TracesSampleRate { get; set; }
public List<string>? TracePropagationTargets { get; set; }
public bool? PropagateTraceparent { get; set; }
Expand Down Expand Up @@ -93,6 +94,7 @@ public void ApplyTo(SentryOptions options)
options.DisableFileWrite = DisableFileWrite ?? options.DisableFileWrite;
options.InitCacheFlushTimeout = InitCacheFlushTimeout ?? options.InitCacheFlushTimeout;
options.DefaultTags = DefaultTags ?? options.DefaultTags;
options.TraceLifeCycle = TraceLifeCycle ?? options.TraceLifeCycle;
options.TracesSampleRate = TracesSampleRate ?? options.TracesSampleRate;
options.ProfilesSampleRate = ProfilesSampleRate ?? options.ProfilesSampleRate;
options.TracePropagationTargets = TracePropagationTargets?.Select(s => new StringOrRegex(s)).ToList() ?? options.TracePropagationTargets;
Expand Down
20 changes: 20 additions & 0 deletions src/Sentry/Internal/Extensions/SentryTransactionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Sentry.Protocol.Spans;

namespace Sentry.Internal.Extensions;

internal static class SentryTransactionExtensions
{
/// <summary>
/// Allows us to convert a Transaction and its chiled spans to the new SpanV2 format during the transition period.
/// This is temporary - we can remove it once transactions have been deprecated.
/// </summary>
public static IEnumerable<SpanV2> ToSpanV2Spans(this SentryTransaction transaction)
{
// Collect spans: transaction span + child spans.
yield return new SpanV2(transaction);
foreach (var span in transaction.Spans)
{
yield return new SpanV2(span);
}
}
}
17 changes: 17 additions & 0 deletions src/Sentry/Protocol/Envelopes/Envelope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Sentry.Internal;
using Sentry.Internal.Extensions;
using Sentry.Protocol.Metrics;
using Sentry.Protocol.Spans;

namespace Sentry.Protocol.Envelopes;

Expand Down Expand Up @@ -504,4 +505,20 @@ internal Envelope WithItem(EnvelopeItem item)
items.Add(item);
return new Envelope(_eventId, Header, items);
}

/// <summary>
/// Creates an envelope that contains one or more Span v2 items.
/// </summary>
internal static Envelope FromSpans(IReadOnlyCollection<SpanV2> spans)
{
var header = DefaultHeader;

var spanItems = new SpanV2Items(spans);

var items = spanItems.Length > 0
? new List<EnvelopeItem>(1) { EnvelopeItem.FromSpans(spanItems) }
: new List<EnvelopeItem>(0);

return new Envelope(header, items);
}
}
16 changes: 16 additions & 0 deletions src/Sentry/Protocol/Envelopes/EnvelopeItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Sentry.Internal;
using Sentry.Internal.Extensions;
using Sentry.Protocol.Metrics;
using Sentry.Protocol.Spans;

namespace Sentry.Protocol.Envelopes;

Expand Down Expand Up @@ -299,6 +300,21 @@ public static EnvelopeItem FromSession(SessionUpdate sessionUpdate)
return new EnvelopeItem(header, new JsonSerializable(sessionUpdate));
}

/// <summary>
/// Creates an <see cref="EnvelopeItem"/> from one or more <paramref name="spans"/>.
/// </summary>
internal static EnvelopeItem FromSpans(SpanV2Items spans)
{
var header = new Dictionary<string, object?>(3, StringComparer.Ordinal)
{
[TypeKey] = TypeValueSpan,
["item_count"] = spans.Length,
["content_type"] = "application/vnd.sentry.items.span+json",
};

return new EnvelopeItem(header, new JsonSerializable(spans));
}

/// <summary>
/// Creates an <see cref="EnvelopeItem"/> from <paramref name="checkIn"/>.
/// </summary>
Expand Down
176 changes: 176 additions & 0 deletions src/Sentry/Protocol/SentryAttributes.cs
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@Flash0ver just a heads up, I needed Attributes for the span streaming stuff so extracted this functionality out from the SentryMetrics class... just in case you might change any of this in another PR.

Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
using Sentry.Extensibility;

namespace Sentry.Protocol;

internal class SentryAttributes : Dictionary<string, SentryAttribute>, ISentryJsonSerializable
{
public SentryAttributes() : base(StringComparer.Ordinal)
{
}

public SentryAttributes(int capacity) : base(capacity, StringComparer.Ordinal)
{
}

/// <summary>
/// Gets the attribute value associated with the specified key.
/// </summary>
/// <remarks>
/// Returns <see langword="true"/> if this <see cref="SentryMetric"/> contains an attribute with the specified key which is of type <typeparamref name="TAttribute"/> and it's value is not <see langword="null"/>.
/// Otherwise <see langword="false"/>.
/// Supported types:
/// <list type="table">
/// <listheader>
/// <term>Type</term>
/// <description>Range</description>
/// </listheader>
/// <item>
/// <term>string</term>
/// <description><see langword="string"/> and <see langword="char"/></description>
/// </item>
/// <item>
/// <term>boolean</term>
/// <description><see langword="false"/> and <see langword="true"/></description>
/// </item>
/// <item>
/// <term>integer</term>
/// <description>64-bit signed integral numeric types</description>
/// </item>
/// <item>
/// <term>double</term>
/// <description>64-bit floating-point numeric types</description>
/// </item>
/// </list>
/// Unsupported types:
/// <list type="table">
/// <listheader>
/// <term>Type</term>
/// <description>Result</description>
/// </listheader>
/// <item>
/// <term><see langword="object"/></term>
/// <description><c>ToString</c> as <c>"type": "string"</c></description>
/// </item>
/// <item>
/// <term>Collections</term>
/// <description><c>ToString</c> as <c>"type": "string"</c></description>
/// </item>
/// <item>
/// <term><see langword="null"/></term>
/// <description>ignored</description>
/// </item>
/// </list>
/// </remarks>
/// <seealso href="https://develop.sentry.dev/sdk/telemetry/metrics/"/>
public bool TryGetAttribute<TAttribute>(string key, [MaybeNullWhen(false)] out TAttribute value)
{
if (TryGetValue(key, out var attribute) && attribute.Value is TAttribute attributeValue)
{
value = attributeValue;
return true;
}

value = default;
return false;
}

/// <summary>
/// Set a key-value pair of data attached to the metric.
/// </summary>
public void SetAttribute<TAttribute>(string key, TAttribute value) where TAttribute : notnull
{
if (value is null)
{
return;
}

this[key] = new SentryAttribute(value);
}

internal void SetAttribute(string key, string value)
{
this[key] = new SentryAttribute(value, "string");
}

internal void SetAttribute(string key, char value)
{
this[key] = new SentryAttribute(value.ToString(), "string");
}

internal void SetAttribute(string key, int value)
{
this[key] = new SentryAttribute(value, "integer");
}

internal void SetDefaultAttributes(SentryOptions options, SdkVersion sdk)
{
var environment = options.SettingLocator.GetEnvironment();
SetAttribute("sentry.environment", environment);

var release = options.SettingLocator.GetRelease();
if (release is not null)
{
SetAttribute("sentry.release", release);
}

if (sdk.Name is { } name)
{
SetAttribute("sentry.sdk.name", name);
}
if (sdk.Version is { } version)
{
SetAttribute("sentry.sdk.version", version);
}
}

internal void SetAttributes(IEnumerable<KeyValuePair<string, object>>? attributes)
{
if (attributes is null)
{
return;
}

#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
if (attributes.TryGetNonEnumeratedCount(out var count))
{
_ = EnsureCapacity(Count + count);
}
#endif

foreach (var attribute in attributes)
{
this[attribute.Key] = new SentryAttribute(attribute.Value);
}
}

internal void SetAttributes(ReadOnlySpan<KeyValuePair<string, object>> attributes)
{
if (attributes.IsEmpty)
{
return;
}

#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
_ = EnsureCapacity(Count + attributes.Length);
#endif

foreach (var attribute in attributes)
{
this[attribute.Key] = new SentryAttribute(attribute.Value);
}
}

/// <inheritdoc cref="ISentryJsonSerializable.WriteTo(Utf8JsonWriter, IDiagnosticLogger)" />
public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger)
{
writer.WritePropertyName("attributes");
writer.WriteStartObject();

foreach (var attribute in this)
{
SentryAttributeSerializer.WriteAttribute(writer, attribute.Key, attribute.Value, logger);
}

writer.WriteEndObject();
}
}
31 changes: 31 additions & 0 deletions src/Sentry/Protocol/Spans/SentryLink.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Sentry.Extensibility;
using Sentry.Internal.Extensions;

namespace Sentry.Protocol.Spans;

/// <summary>
/// Links connect spans to other spans or traces, enabling distributed tracing
/// </summary>
internal readonly struct SentryLink(SentryId traceId, SpanId spanId, bool sampled) : ISentryJsonSerializable
{
private readonly SentryAttributes _attributes = new();

public SpanId SpanId { get; } = spanId;
public SentryId TraceId { get; } = traceId;
public bool Sampled { get; } = sampled;
public IReadOnlyDictionary<string, SentryAttribute> Attributes => _attributes;

/// <inheritdoc cref="ISentryJsonSerializable.WriteTo(Utf8JsonWriter, IDiagnosticLogger)" />
public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger)
{
writer.WriteStartObject();

writer.WriteSerializableIfNotNull("span_id", SpanId.NullIfDefault(), logger);
writer.WriteSerializableIfNotNull("trace_id", TraceId.NullIfDefault(), logger);
writer.WriteBoolean("sampled", Sampled);

_attributes.WriteTo(writer, logger);

writer.WriteEndObject();
}
}
Loading