Skip to content

Commit

Permalink
Exporting tags consistently (#3281)
Browse files Browse the repository at this point in the history
  • Loading branch information
alanwest authored May 27, 2022
1 parent b9839f6 commit 4e6d073
Show file tree
Hide file tree
Showing 25 changed files with 527 additions and 310 deletions.
19 changes: 19 additions & 0 deletions src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@

## Unreleased

* Improve the conversion and formatting of attribute values.
The list of data types that must be supported per the
[OpenTelemetry specification](https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/common#attribute)
is more narrow than what the .NET OpenTelemetry SDK supports. Numeric
[built-in value types](https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/built-in-types)
are supported by converting to a `long` or `double` as appropriate except for
numeric types that could cause overflow (`ulong`) or rounding (`decimal`)
which are converted to strings. Non-numeric built-in types - `string`,
`char`, `bool` are supported. All other types are converted to a `string`.
Array values are also supported.
([#3281](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3281))
* Fix conversion of array-valued resource attributes. They were previously
converted to a string like "System.String[]".
([#3281](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3281))
* Fix exporting of array-valued attributes on an `Activity`. Previously, each
item in the array would result in a new tag on an exported `Activity`. Now,
array-valued attributes are serialzed to a JSON-array representation.
([#3281](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3281))

## 1.3.0-beta.2

Released 2022-May-16
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,20 +230,6 @@ public static JaegerSpanRef ToJaegerSpanRef(this in ActivityLink link)
return new JaegerSpanRef(refType, traceId.Low, traceId.High, spanId.Low);
}

public static JaegerTag ToJaegerTag(this KeyValuePair<string, object> attribute)
{
return attribute.Value switch
{
string s => new JaegerTag(attribute.Key, JaegerTagType.STRING, vStr: s),
int i => new JaegerTag(attribute.Key, JaegerTagType.LONG, vLong: Convert.ToInt64(i)),
long l => new JaegerTag(attribute.Key, JaegerTagType.LONG, vLong: l),
float f => new JaegerTag(attribute.Key, JaegerTagType.DOUBLE, vDouble: Convert.ToDouble(f)),
double d => new JaegerTag(attribute.Key, JaegerTagType.DOUBLE, vDouble: d),
bool b => new JaegerTag(attribute.Key, JaegerTagType.BOOL, vBool: b),
_ => new JaegerTag(attribute.Key, JaegerTagType.STRING, vStr: attribute.Value.ToString()),
};
}

public static long ToEpochMicroseconds(this DateTime utcDateTime)
{
// Truncate sub-microsecond precision before offsetting by the Unix Epoch to avoid
Expand All @@ -260,42 +246,6 @@ public static long ToEpochMicroseconds(this DateTimeOffset timestamp)
return microseconds - UnixEpochMicroseconds;
}

private static void ProcessJaegerTagArray(ref PooledList<JaegerTag> tags, KeyValuePair<string, object> activityTag)
{
if (activityTag.Value is int[] intArray)
{
foreach (var item in intArray)
{
JaegerTag jaegerTag = new JaegerTag(activityTag.Key, JaegerTagType.LONG, vLong: Convert.ToInt64(item));
PooledList<JaegerTag>.Add(ref tags, jaegerTag);
}
}
else if (activityTag.Value is string[] stringArray)
{
foreach (var item in stringArray)
{
JaegerTag jaegerTag = new JaegerTag(activityTag.Key, JaegerTagType.STRING, vStr: item);
PooledList<JaegerTag>.Add(ref tags, jaegerTag);
}
}
else if (activityTag.Value is bool[] boolArray)
{
foreach (var item in boolArray)
{
JaegerTag jaegerTag = new JaegerTag(activityTag.Key, JaegerTagType.BOOL, vBool: item);
PooledList<JaegerTag>.Add(ref tags, jaegerTag);
}
}
else if (activityTag.Value is double[] doubleArray)
{
foreach (var item in doubleArray)
{
JaegerTag jaegerTag = new JaegerTag(activityTag.Key, JaegerTagType.DOUBLE, vDouble: item);
PooledList<JaegerTag>.Add(ref tags, jaegerTag);
}
}
}

private struct TagEnumerationState : IActivityEnumerator<KeyValuePair<string, object>>, PeerServiceResolver.IPeerServiceState
{
public PooledList<JaegerTag> Tags;
Expand All @@ -316,14 +266,15 @@ private struct TagEnumerationState : IActivityEnumerator<KeyValuePair<string, ob

public bool ForEach(KeyValuePair<string, object> activityTag)
{
if (activityTag.Value is Array)
{
ProcessJaegerTagArray(ref this.Tags, activityTag);
}
else if (activityTag.Value != null)
if (activityTag.Value != null)
{
var key = activityTag.Key;
var jaegerTag = activityTag.ToJaegerTag();

if (!JaegerTagTransformer.Instance.TryTransformTag(activityTag, out var jaegerTag))
{
return true;
}

if (jaegerTag.VStr != null)
{
PeerServiceResolver.InspectTag(ref this, key, jaegerTag.VStr);
Expand Down Expand Up @@ -400,18 +351,14 @@ private struct EventTagsEnumerationState : IActivityEnumerator<KeyValuePair<stri

public bool ForEach(KeyValuePair<string, object> tag)
{
if (tag.Value is Array)
{
ProcessJaegerTagArray(ref this.Tags, tag);
}
else if (tag.Value != null)
if (JaegerTagTransformer.Instance.TryTransformTag(tag, out var result))
{
PooledList<JaegerTag>.Add(ref this.Tags, tag.ToJaegerTag());
}
PooledList<JaegerTag>.Add(ref this.Tags, result);

if (tag.Key == "event")
{
this.HasEvent = true;
if (tag.Key == "event")
{
this.HasEvent = true;
}
}

return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// <copyright file="JaegerTagTransformer.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System;
using OpenTelemetry.Internal;

namespace OpenTelemetry.Exporter.Jaeger.Implementation;

internal sealed class JaegerTagTransformer : TagTransformer<JaegerTag>
{
private JaegerTagTransformer()
{
}

public static JaegerTagTransformer Instance { get; } = new();

protected override JaegerTag TransformIntegralTag(string key, long value)
{
return new JaegerTag(key, JaegerTagType.LONG, vLong: value);
}

protected override JaegerTag TransformFloatingPointTag(string key, double value)
{
return new JaegerTag(key, JaegerTagType.DOUBLE, vDouble: value);
}

protected override JaegerTag TransformBooleanTag(string key, bool value)
{
return new JaegerTag(key, JaegerTagType.BOOL, vBool: value);
}

protected override JaegerTag TransformStringTag(string key, string value)
{
return new JaegerTag(key, JaegerTagType.STRING, vStr: value);
}

protected override JaegerTag TransformArrayTag(string key, Array array)
=> this.TransformStringTag(key, System.Text.Json.JsonSerializer.Serialize(array));
}
15 changes: 0 additions & 15 deletions src/OpenTelemetry.Exporter.Jaeger/Implementation/Process.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
// </copyright>

using System.Collections.Generic;
using System.Linq;
using System.Text;
using Thrift.Protocol;
using Thrift.Protocol.Entities;
Expand All @@ -29,20 +28,6 @@ public Process(string serviceName)
this.ServiceName = serviceName;
}

public Process(string serviceName, IEnumerable<KeyValuePair<string, object>> processTags)
: this(serviceName, processTags?.Select(pt => pt.ToJaegerTag()).ToDictionary(pt => pt.Key, pt => pt))
{
}

internal Process(string serviceName, Dictionary<string, JaegerTag> processTags)
: this(serviceName)
{
if (processTags != null)
{
this.Tags = processTags;
}
}

public string ServiceName { get; internal set; }

internal Dictionary<string, JaegerTag> Tags { get; set; }
Expand Down
11 changes: 7 additions & 4 deletions src/OpenTelemetry.Exporter.Jaeger/JaegerExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,15 @@ internal void SetResourceAndInitializeBatch(Resource resource)
}
}

if (process.Tags == null)
if (JaegerTagTransformer.Instance.TryTransformTag(label, out var result))
{
process.Tags = new Dictionary<string, JaegerTag>();
}
if (process.Tags == null)
{
process.Tags = new Dictionary<string, JaegerTag>();
}

process.Tags[key] = label.ToJaegerTag();
process.Tags[key] = result;
}
}

if (serviceName != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PeerServiceResolver.cs" Link="Includes\PeerServiceResolver.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\ResourceSemanticConventions.cs" Link="Includes\ResourceSemanticConventions.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\ServiceProviderExtensions.cs" Link="Includes\ServiceProviderExtensions.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\TagTransformer.cs" Link="Includes\TagTransformer.cs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="System.Threading.Tasks.Extensions" Version="$(SystemThreadingTasksExtensionsPkgVer)" Condition="'$(TargetFramework)' != 'netstandard2.1'" />
<PackageReference Include="System.Text.Json" Version="$(SystemTextJsonPkgVer)" Condition="'$(TargetFramework)' != 'net6.0'" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,7 @@ public bool ForEach(KeyValuePair<string, object> activityTag)
this.Created = true;
}

var attribute = activityTag.ToOtlpAttribute();
if (attribute != null)
if (OtlpKeyValueTransformer.Instance.TryTransformTag(activityTag, out var attribute))
{
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, attribute);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,9 @@ internal static OtlpLogs.LogRecord ToOtlpLog(this LogRecord logRecord)
{
otlpLogRecord.Body = new OtlpCommon.AnyValue { StringValue = stateValue.Value as string };
}
else
else if (OtlpKeyValueTransformer.Instance.TryTransformTag(stateValue, out var result))
{
var otlpAttribute = stateValue.ToOtlpAttribute();
otlpLogRecord.Attributes.Add(otlpAttribute);
otlpLogRecord.Attributes.Add(result);
}
}
}
Expand Down Expand Up @@ -150,8 +149,10 @@ void ProcessScope(LogRecordScope scope, OtlpLogs.LogRecord otlpLog)
foreach (var scopeItem in scope)
{
var scopeItemWithDepthInfo = new KeyValuePair<string, object>($"[Scope.{scopeDepth}]:{scopeItem.Key}", scopeItem.Value);
var otlpAttribute = scopeItemWithDepthInfo.ToOtlpAttribute();
otlpLog.Attributes.Add(otlpAttribute);
if (OtlpKeyValueTransformer.Instance.TryTransformTag(scopeItemWithDepthInfo, out var result))
{
otlpLog.Attributes.Add(result);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,10 @@ private static void AddAttributes(ReadOnlyTagCollection tags, RepeatedField<Otlp
{
foreach (var tag in tags)
{
attributes.Add(tag.ToOtlpAttribute());
if (OtlpKeyValueTransformer.Instance.TryTransformTag(tag, out var result))
{
attributes.Add(result);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,5 @@ public void CouldNotTranslateLogRecord(string exceptionMessage)
{
this.WriteEvent(9, exceptionMessage);
}

[Event(10, Message = "Unsupported attribute type '{0}' for '{1}'. Attribute will not be exported.", Level = EventLevel.Warning)]
public void UnsupportedAttributeType(string type, string key)
{
this.WriteEvent(10, type.ToString(), key);
}
}
}
Loading

0 comments on commit 4e6d073

Please sign in to comment.