Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public TagAttribute(string tagName) =>
/// Gets the name of the datadog tag the property corresponds to

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the case where a tag is only used by Otel, we'd still set TagName. I'm wondering if it will confuse anyone 🤔 Maybe we could/should call it out explicitly in doc comments for TagName? WDYT?

/// </summary>
public string TagName { get; }

/// <summary>
/// Gets or sets the OpenTelemetry semantic convention name that aliases <see cref=""TagName""/>.
/// </summary>
public string? OtelName { get; set; }
}

/// <summary>
Expand Down Expand Up @@ -118,6 +123,27 @@ partial class ")
.Append(@"Bytes => [")
.Append(tagByteArray)
.AppendLine(@"];");

if (property.OtelTagValue is not null)
{
var oTelTagByteArray = string.Join(", ", MessagePackHelper.GetValueInRawMessagePackIEnumerable(property.OtelTagValue));

sb.Append(
@"
// ")
.Append(property.PropertyName)
.Append(@"OtelBytes = MessagePack.Serialize(""")
.Append(property.OtelTagValue)
.Append(@""");");

sb.Append(
@"
private static ReadOnlySpan<byte> ")
.Append(property.PropertyName)
.Append(@"OtelBytes => [")
.Append(oTelTagByteArray)
.AppendLine(@"];");
}
}

sb.Append(
Expand Down Expand Up @@ -148,6 +174,27 @@ partial class ")
sb.Append(
@",
");

if (property.OtelTagValue is not null)
{
sb.Append('"')
.Append(property.OtelTagValue)
.Append(@""" => ")
.Append(property.PropertyName);

switch (property.PropertyType)
{
case TagListGenerator.PropertyType.NullableInt:
sb.Append(" is null ? null : Datadog.Trace.Util.IntStringCache.ToInvariantString(")
.Append(property.PropertyName)
.Append(".Value)");
break;
}

sb.Append(
@",
");
}
}

sb.Append(
Expand All @@ -169,11 +216,26 @@ public override void SetTag(string key, string? value)
continue;
}

sb.Append(@"case """)
.Append(property.TagValue)
.Append(
@""":
if (property.OtelTagValue is not null)
{
sb.Append(@"case """)
.Append(property.TagValue)
.Append(@""":")
.Append(@"
case """)
.Append(property.OtelTagValue)
.Append(
@""":
");
}
else
{
sb.Append(@"case """)
.Append(property.TagValue)
.Append(
@""":
");
}

if (property.PropertyType is TagListGenerator.PropertyType.NullableInt)
{
Expand Down Expand Up @@ -223,6 +285,15 @@ public override void SetTag(string key, string? value)
.Append(
@""":
");

if (property.OtelTagValue is not null)
{
sb.Append(@"case """)
.Append(property.OtelTagValue)
.Append(
@""":
");
}
}
}

Expand All @@ -244,7 +315,7 @@ public override void SetTag(string key, string? value)
}
}

public override void EnumerateTags<TProcessor>(ref TProcessor processor)
public override void EnumerateTags<TProcessor>(ref TProcessor processor, bool openTelemetrySemanticsEnabled)
{
");
foreach (var property in tagList.TagProperties)
Expand All @@ -256,13 +327,40 @@ public override void EnumerateTags<TProcessor>(ref TProcessor processor)
.Append(property.PropertyName)
.Append(@" is not null)
{
processor.Process(new TagItem<int>(""")
");

if (property.OtelTagValue is not null)
{
sb.Append(@"if (openTelemetrySemanticsEnabled)
{
processor.Process(new TagItem<int>(""")
.Append(property.OtelTagValue)
.Append(@""", ")
.Append(property.PropertyName)
.Append(@".Value, ")
.Append(property.PropertyName)
.Append(@"OtelBytes));
}
else
{
");
}

sb.Append(@"processor.Process(new TagItem<int>(""")
.Append(property.TagValue)
.Append(@""", ")
.Append(property.PropertyName)
.Append(@".Value, ")
.Append(property.PropertyName)
.Append(@"Bytes));
.Append(@"Bytes));");

if (property.OtelTagValue is not null)
{
sb.Append(@"
}");
}

sb.Append(@"
}

");
Expand All @@ -272,13 +370,40 @@ public override void EnumerateTags<TProcessor>(ref TProcessor processor)
.Append(property.PropertyName)
.Append(@" is not null)
{
processor.Process(new TagItem<string>(""")
");

if (property.OtelTagValue is not null)
{
sb.Append(@"if (openTelemetrySemanticsEnabled)
{
processor.Process(new TagItem<string>(""")
.Append(property.OtelTagValue)
.Append(@""", ")
.Append(property.PropertyName)
.Append(@", ")
.Append(property.PropertyName)
.Append(@"OtelBytes));
}
else
{
");
}

sb.Append(@"processor.Process(new TagItem<string>(""")
.Append(property.TagValue)
.Append(@""", ")
.Append(property.PropertyName)
.Append(@", ")
.Append(property.PropertyName)
.Append(@"Bytes));
.Append(@"Bytes));");

if (property.OtelTagValue is not null)
{
sb.Append(@"
}");
}

sb.Append(@"
}

");
Expand All @@ -287,7 +412,7 @@ public override void EnumerateTags<TProcessor>(ref TProcessor processor)
}

sb.Append(
@"base.EnumerateTags(ref processor);
@"base.EnumerateTags(ref processor, openTelemetrySemanticsEnabled);
}

protected override void WriteAdditionalTags(System.Text.StringBuilder sb)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ private static IEnumerable<TagList> GetTagLists(ImmutableArray<PropertyTag> tagP
List<DiagnosticInfo>? diagnostics = null;
bool hasMisconfiguredInput = false;
string? key = null;
string? otelKey = null;

foreach (AttributeData attributeData in propertySymbol!.GetAttributes())
{
Expand Down Expand Up @@ -191,6 +192,48 @@ private static IEnumerable<TagList> GetTagLists(ImmutableArray<PropertyTag> tagP
hasMisconfiguredInput = true;
break;
}

if (isTag)
{
foreach (var namedArgument in attributeData.NamedArguments)
{
if (namedArgument.Key != "OtelName")
{
continue;
}

if (namedArgument.Value.Kind == TypedConstantKind.Error)
{
hasMisconfiguredInput = true;
break;
}

otelKey = (string?)namedArgument.Value.Value;
if (string.IsNullOrEmpty(otelKey))
{
diagnostics ??= new List<DiagnosticInfo>();
diagnostics.Add(InvalidKeyDiagnostic.CreateInfo(attributeData.ApplicationSyntaxReference?.GetSyntax()));
hasMisconfiguredInput = true;
break;
}

if (otelKey == "_dd.origin")
{
diagnostics ??= new List<DiagnosticInfo>();
diagnostics.Add(InvalidUseOfOriginDiagnostic.CreateInfo(attributeData.ApplicationSyntaxReference?.GetSyntax()));
hasMisconfiguredInput = true;
break;
}

if (otelKey == "language")
{
diagnostics ??= new List<DiagnosticInfo>();
diagnostics.Add(InvalidUseOfLanguageDiagnostic.CreateInfo(attributeData.ApplicationSyntaxReference?.GetSyntax()));
hasMisconfiguredInput = true;
break;
}
}
}
}
}

Expand Down Expand Up @@ -242,6 +285,7 @@ private static IEnumerable<TagList> GetTagLists(ImmutableArray<PropertyTag> tagP
isReadOnly: propertySymbol!.IsReadOnly,
propertyName: propertySymbol.Name,
tagValue: key!,
otelTagValue: otelKey,
isTag: isTag,
propertyType: propertyType);

Expand Down Expand Up @@ -304,14 +348,16 @@ internal readonly record struct PropertyTag
public readonly bool IsReadOnly;
public readonly string PropertyName;
public readonly string TagValue;
public readonly string? OtelTagValue;
public readonly bool IsTag;
public readonly PropertyType PropertyType;

public PropertyTag(string nameSpace, string className, bool isReadOnly, string propertyName, string tagValue, bool isTag, PropertyType propertyType)
public PropertyTag(string nameSpace, string className, bool isReadOnly, string propertyName, string tagValue, string? otelTagValue, bool isTag, PropertyType propertyType)
{
IsReadOnly = isReadOnly;
PropertyName = propertyName;
TagValue = tagValue;
OtelTagValue = otelTagValue;
IsTag = isTag;
Namespace = nameSpace;
ClassName = className;
Expand Down
12 changes: 6 additions & 6 deletions tracer/src/Datadog.Trace/Agent/AgentWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ internal sealed class AgentWriter : IAgentWriter
private bool _traceMetricsEnabled;

public AgentWriter(IApi api, IStatsAggregator? statsAggregator, IStatsdManager statsd, TracerSettings settings)
: this(api, statsAggregator, statsd, maxBufferSize: settings.TraceBufferSize, batchInterval: settings.TraceBatchInterval, apmTracingEnabled: settings.ApmTracingEnabled, initialTracerMetricsEnabled: settings.Manager.InitialMutableSettings.TracerMetricsEnabled, openTelemetrySemanticsEnabled: settings.OtelSemanticsEnabled)
: this(api, statsAggregator, statsd, maxBufferSize: settings.TraceBufferSize, batchInterval: settings.TraceBatchInterval, apmTracingEnabled: settings.ApmTracingEnabled, initialTracerMetricsEnabled: settings.Manager.InitialMutableSettings.TracerMetricsEnabled)
{
settings.Manager.SubscribeToChanges(changes =>
{
Expand All @@ -82,12 +82,12 @@ public AgentWriter(IApi api, IStatsAggregator? statsAggregator, IStatsdManager s
});
}

public AgentWriter(IApi api, IStatsAggregator? statsAggregator, IStatsdManager statsd, bool automaticFlush = true, int maxBufferSize = 1024 * 1024 * 10, int batchInterval = 100, bool apmTracingEnabled = true, bool initialTracerMetricsEnabled = false, bool openTelemetrySemanticsEnabled = false)
: this(api, statsAggregator, statsd, MovingAverageKeepRateCalculator.CreateDefaultKeepRateCalculator(), automaticFlush, maxBufferSize, batchInterval, apmTracingEnabled, initialTracerMetricsEnabled, openTelemetrySemanticsEnabled)
public AgentWriter(IApi api, IStatsAggregator? statsAggregator, IStatsdManager statsd, bool automaticFlush = true, int maxBufferSize = 1024 * 1024 * 10, int batchInterval = 100, bool apmTracingEnabled = true, bool initialTracerMetricsEnabled = false)
: this(api, statsAggregator, statsd, MovingAverageKeepRateCalculator.CreateDefaultKeepRateCalculator(), automaticFlush, maxBufferSize, batchInterval, apmTracingEnabled, initialTracerMetricsEnabled)
{
}

internal AgentWriter(IApi api, IStatsAggregator? statsAggregator, IStatsdManager statsd, IKeepRateCalculator traceKeepRateCalculator, bool automaticFlush, int maxBufferSize, int batchInterval, bool apmTracingEnabled, bool initialTracerMetricsEnabled, bool openTelemetrySemanticsEnabled)
internal AgentWriter(IApi api, IStatsAggregator? statsAggregator, IStatsdManager statsd, IKeepRateCalculator traceKeepRateCalculator, bool automaticFlush, int maxBufferSize, int batchInterval, bool apmTracingEnabled, bool initialTracerMetricsEnabled)
{
_statsAggregator = statsAggregator ?? new NullStatsAggregator();

Expand All @@ -98,8 +98,8 @@ internal AgentWriter(IApi api, IStatsAggregator? statsAggregator, IStatsdManager

ISpanBufferSerializer CreateSpanSerializer() => api.TracesEncoding switch
{
TracesEncoding.OtlpJson => new OtlpTracesJsonSerializer(openTelemetrySemanticsEnabled),
TracesEncoding.OtlpProtobuf => new OtlpTracesProtobufSerializer(openTelemetrySemanticsEnabled),
TracesEncoding.OtlpJson => new OtlpTracesJsonSerializer(),
TracesEncoding.OtlpProtobuf => new OtlpTracesProtobufSerializer(),
_ => new SpanBufferMessagePackSerializer(SpanFormatterResolver.Instance),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ private int WriteTags(ref byte[] bytes, int offset, in SpanModel model, ITagProc

// Write span tags
var tagWriter = new TagWriter(this, tagProcessors, bytes, offset);
span.Tags.EnumerateTags(ref tagWriter);
span.Tags.EnumerateTags(ref tagWriter, span.OpenTelemetrySemanticsEnabled);
bytes = tagWriter.Bytes;
offset = tagWriter.Offset;
count += tagWriter.Count;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ static void BuildFilterTags(List<string>? filters, out List<string> keyFilters,
/// <summary>
/// Returns true if the trace should be kept, false if it should be rejected.
/// Evaluation is based on the root span only.
/// Note: When a tag has both a DD and OTel name and is strongly typed in our ITags implementations,
/// there are two diverging behaviors:
/// - When applying a filter tag, the search checks both DD and OTel tag keys (and the singular tag value)
/// - When applying a filter tag regex, the search only checks one tag key (DD or Otel) based on the span's configured semantics setting.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We could tweak that, so that it applies to both cases, at the expense of an additional enumeration for every span, regardless of the setting. Given the performance impact for everyone, and the generally low usage, I think the current approach makes total sense, even though it is potentially a bit confusing.

/// </summary>
public bool ShouldKeepTrace(Span rootSpan)
{
Expand All @@ -95,6 +99,7 @@ public bool ShouldKeepTrace(Span rootSpan)
}

// 2a. Reject filtering: reject if any tag matches reject filters
// With DD vs OTel semantics: Simple tag names check both key names and the (singular) value.
foreach (var filter in _filterTagKeysReject)
{
// Key-only filter: matches if tag key exists with any value
Expand Down Expand Up @@ -159,7 +164,7 @@ public bool ShouldKeepTrace(Span rootSpan)
private static bool MatchesRegexTagFilter(Span span, RegexTagFilter filter)
{
var processor = new RegexTagFilterProcessor(filter);
span.Tags.EnumerateTags(ref processor);
span.Tags.EnumerateTags(ref processor, span.OpenTelemetrySemanticsEnabled);
return processor.Matched;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ private int WriteTags(ref byte[] bytes, int offset, Span span, ITags tags, ITagP

// Write span tags
var tagWriter = new TagWriter(this, tagProcessors, bytes, offset);
tags.EnumerateTags(ref tagWriter);
tags.EnumerateTags(ref tagWriter, span.OpenTelemetrySemanticsEnabled);
bytes = tagWriter.Bytes;
offset = tagWriter.Offset;
count += tagWriter.Count;
Expand Down
Loading
Loading