Skip to content

Commit

Permalink
[Part2] Support Activity Status and status description in Jaeger Expo…
Browse files Browse the repository at this point in the history
…rter. (#3073)
  • Loading branch information
Yun-Ting authored Mar 24, 2022
1 parent f531391 commit 1962073
Show file tree
Hide file tree
Showing 3 changed files with 227 additions and 45 deletions.
6 changes: 6 additions & 0 deletions src/OpenTelemetry.Exporter.Jaeger/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Unreleased

* Added support for Activity Status and StatusDescription which were
added to Activity from version 6.0. To maintain backward
compatibility, the exporter falls back to checking status inside
the tag "otel.status_code".
([#3073](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3073))

## 1.2.0-rc3

Released 2022-Mar-04
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,50 @@ public static JaegerSpan ToJaegerSpan(this Activity activity)

activity.EnumerateTags(ref jaegerTags);

if (activity.Status != ActivityStatusCode.Unset)
{
if (activity.Status == ActivityStatusCode.Ok)
{
PooledList<JaegerTag>.Add(
ref jaegerTags.Tags,
new JaegerTag(SpanAttributeConstants.StatusCodeKey, JaegerTagType.STRING, vStr: "OK"));
}
else
{
PooledList<JaegerTag>.Add(
ref jaegerTags.Tags,
new JaegerTag(SpanAttributeConstants.StatusCodeKey, JaegerTagType.STRING, vStr: "ERROR"));

PooledList<JaegerTag>.Add(
ref jaegerTags.Tags,
new JaegerTag(JaegerErrorFlagTagName, JaegerTagType.BOOL, vBool: true));

PooledList<JaegerTag>.Add(
ref jaegerTags.Tags,
new JaegerTag(SpanAttributeConstants.StatusDescriptionKey, JaegerTagType.STRING, vStr: activity.StatusDescription ?? string.Empty));
}
}
else if (jaegerTags.StatusCode.HasValue && jaegerTags.StatusCode != StatusCode.Unset)
{
PooledList<JaegerTag>.Add(
ref jaegerTags.Tags,
new JaegerTag(
SpanAttributeConstants.StatusCodeKey,
JaegerTagType.STRING,
vStr: StatusHelper.GetTagValueForStatusCode(jaegerTags.StatusCode.Value)));

if (jaegerTags.StatusCode == StatusCode.Error)
{
PooledList<JaegerTag>.Add(
ref jaegerTags.Tags,
new JaegerTag(JaegerErrorFlagTagName, JaegerTagType.BOOL, vBool: true));

PooledList<JaegerTag>.Add(
ref jaegerTags.Tags,
new JaegerTag(SpanAttributeConstants.StatusDescriptionKey, JaegerTagType.STRING, vStr: jaegerTags.StatusDescription ?? string.Empty));
}
}

string peerServiceName = null;
if (activity.Kind == ActivityKind.Client || activity.Kind == ActivityKind.Producer)
{
Expand Down Expand Up @@ -252,44 +296,6 @@ private static void ProcessJaegerTagArray(ref PooledList<JaegerTag> tags, KeyVal
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ProcessJaegerTag(ref TagEnumerationState state, string key, JaegerTag jaegerTag)
{
if (jaegerTag.VStr != null)
{
PeerServiceResolver.InspectTag(ref state, key, jaegerTag.VStr);

if (key == SpanAttributeConstants.StatusCodeKey)
{
StatusCode? statusCode = StatusHelper.GetStatusCodeForTagValue(jaegerTag.VStr);
if (statusCode == StatusCode.Error)
{
// Error flag: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/jaeger.md#error-flag
PooledList<JaegerTag>.Add(ref state.Tags, new JaegerTag(JaegerErrorFlagTagName, JaegerTagType.BOOL, vBool: true));
}
else if (!statusCode.HasValue || statusCode == StatusCode.Unset)
{
// Unset Status is not sent: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/jaeger.md#status
return;
}

// Normalize status since it is user-driven.
jaegerTag = new JaegerTag(key, JaegerTagType.STRING, vStr: StatusHelper.GetTagValueForStatusCode(statusCode.Value));
}
else if (key == JaegerErrorFlagTagName)
{
// Ignore `error` tag if it exists, it will be added based on StatusCode + StatusDescription.
return;
}
}
else if (jaegerTag.VLong.HasValue)
{
PeerServiceResolver.InspectTag(ref state, key, jaegerTag.VLong.Value);
}

PooledList<JaegerTag>.Add(ref state.Tags, jaegerTag);
}

private struct TagEnumerationState : IActivityEnumerator<KeyValuePair<string, object>>, PeerServiceResolver.IPeerServiceState
{
public PooledList<JaegerTag> Tags;
Expand All @@ -304,6 +310,10 @@ private struct TagEnumerationState : IActivityEnumerator<KeyValuePair<string, ob

public long Port { get; set; }

public StatusCode? StatusCode { get; set; }

public string StatusDescription { get; set; }

public bool ForEach(KeyValuePair<string, object> activityTag)
{
if (activityTag.Value is Array)
Expand All @@ -312,7 +322,30 @@ public bool ForEach(KeyValuePair<string, object> activityTag)
}
else if (activityTag.Value != null)
{
ProcessJaegerTag(ref this, activityTag.Key, activityTag.ToJaegerTag());
var key = activityTag.Key;
var jaegerTag = activityTag.ToJaegerTag();
if (jaegerTag.VStr != null)
{
PeerServiceResolver.InspectTag(ref this, key, jaegerTag.VStr);

if (key == SpanAttributeConstants.StatusCodeKey)
{
StatusCode? statusCode = StatusHelper.GetStatusCodeForTagValue(jaegerTag.VStr);
this.StatusCode = statusCode;
return true;
}
else if (key == SpanAttributeConstants.StatusDescriptionKey)
{
this.StatusDescription = jaegerTag.VStr;
return true;
}
}
else if (jaegerTag.VLong.HasValue)
{
PeerServiceResolver.InspectTag(ref this, key, jaegerTag.VLong.Value);
}

PooledList<JaegerTag>.Add(ref this.Tags, jaegerTag);
}

return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -434,15 +434,16 @@ public void JaegerActivityConverterTest_NullTagValueTest()
}

[Theory]
[InlineData(StatusCode.Unset, "unset")]
[InlineData(StatusCode.Ok, "Ok")]
[InlineData(StatusCode.Error, "ERROR")]
[InlineData(StatusCode.Unset, "iNvAlId")]
public void JaegerActivityConverterTest_Status_ErrorFlagTest(StatusCode expectedStatusCode, string statusCodeTagValue)
[InlineData(StatusCode.Unset, "unset", "")]
[InlineData(StatusCode.Ok, "Ok", "")]
[InlineData(StatusCode.Error, "ERROR", "error description")]
[InlineData(StatusCode.Unset, "iNvAlId", "")]
public void JaegerActivityConverterTest_Status_ErrorFlagTest(StatusCode expectedStatusCode, string statusCodeTagValue, string statusDescription)
{
// Arrange
var activity = CreateTestActivity();
activity.SetTag(SpanAttributeConstants.StatusCodeKey, statusCodeTagValue);
activity.SetTag(SpanAttributeConstants.StatusDescriptionKey, statusDescription);

// Act
var jaegerSpan = activity.ToJaegerSpan();
Expand All @@ -464,14 +465,156 @@ public void JaegerActivityConverterTest_Status_ErrorFlagTest(StatusCode expected

if (expectedStatusCode == StatusCode.Error)
{
Assert.Contains(jaegerSpan.Tags, t => t.Key == JaegerActivityExtensions.JaegerErrorFlagTagName && t.VType == JaegerTagType.BOOL && (t.VBool ?? false));
Assert.Contains(
jaegerSpan.Tags, t =>
t.Key == JaegerActivityExtensions.JaegerErrorFlagTagName &&
t.VType == JaegerTagType.BOOL && (t.VBool ?? false));
Assert.Contains(
jaegerSpan.Tags, t =>
t.Key == SpanAttributeConstants.StatusDescriptionKey &&
t.VType == JaegerTagType.STRING && t.VStr.Equals(statusDescription));
}
else
{
Assert.DoesNotContain(jaegerSpan.Tags, t => t.Key == JaegerActivityExtensions.JaegerErrorFlagTagName);
}
}

[Theory]
[InlineData(ActivityStatusCode.Unset)]
[InlineData(ActivityStatusCode.Ok)]
[InlineData(ActivityStatusCode.Error)]
public void ToJaegerSpan_Activity_Status_And_StatusDescription_is_Set(ActivityStatusCode expectedStatusCode)
{
// Arrange
var activity = CreateTestActivity();
activity.SetStatus(expectedStatusCode);

// Act
var jaegerSpan = activity.ToJaegerSpan();

// Assert
if (expectedStatusCode == ActivityStatusCode.Unset)
{
Assert.DoesNotContain(jaegerSpan.Tags, t => t.Key == SpanAttributeConstants.StatusCodeKey);
}
else if (expectedStatusCode == ActivityStatusCode.Ok)
{
Assert.Equal("OK", jaegerSpan.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.StatusCodeKey).VStr);
}

// expectedStatusCode is Error
else
{
Assert.Equal("ERROR", jaegerSpan.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.StatusCodeKey).VStr);
}

if (expectedStatusCode == ActivityStatusCode.Error)
{
Assert.Contains(
jaegerSpan.Tags, t =>
t.Key == JaegerActivityExtensions.JaegerErrorFlagTagName &&
t.VType == JaegerTagType.BOOL && (t.VBool ?? false));
}
else
{
Assert.DoesNotContain(
jaegerSpan.Tags, t =>
t.Key == JaegerActivityExtensions.JaegerErrorFlagTagName);
}
}

[Fact]
public void ActivityStatus_Takes_precedence_Over_Status_Tags_ActivityStatusCodeIsOk()
{
// Arrange.
var activity = CreateTestActivity();
const string TagDescriptionOnError = "Description when TagStatusCode is Error.";
activity.SetStatus(ActivityStatusCode.Ok);
activity.SetTag(SpanAttributeConstants.StatusCodeKey, "ERROR");
activity.SetTag(SpanAttributeConstants.StatusDescriptionKey, TagDescriptionOnError);

// Enrich activity with additional tags.
activity.SetTag("myCustomTag", "myCustomTagValue");

// Act.
var jaegerSpan = activity.ToJaegerSpan();

// Assert.
Assert.Equal("OK", jaegerSpan.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.StatusCodeKey).VStr);

Assert.Contains(jaegerSpan.Tags, t => t.Key == "otel.status_code" && t.VStr == "OK");
Assert.DoesNotContain(jaegerSpan.Tags, t => t.Key == "otel.status_code" && t.VStr == "ERROR");
Assert.DoesNotContain(jaegerSpan.Tags, t => t.Key == JaegerActivityExtensions.JaegerErrorFlagTagName);
Assert.DoesNotContain(jaegerSpan.Tags, t => t.Key == SpanAttributeConstants.StatusDescriptionKey &&
t.VType == JaegerTagType.STRING && t.VStr.Equals(TagDescriptionOnError));

// Ensure additional Activity tags were being converted.
Assert.Contains(jaegerSpan.Tags, t => t.Key == "myCustomTag" && t.VStr == "myCustomTagValue");
}

[Fact]
public void ActivityStatus_Takes_precedence_Over_Status_Tags_ActivityStatusCodeIsError()
{
// Arrange.
var activity = CreateTestActivity();
const string StatusDescriptionOnError = "Description when ActivityStatusCode is Error.";
activity.SetStatus(ActivityStatusCode.Error, StatusDescriptionOnError);
activity.SetTag(SpanAttributeConstants.StatusCodeKey, "OK");

// Enrich activity with additional tags.
activity.SetTag("myCustomTag", "myCustomTagValue");

// Act.
var jaegerSpan = activity.ToJaegerSpan();

// Assert.
Assert.Contains(jaegerSpan.Tags, t => t.Key == "otel.status_code" && t.VStr == "ERROR");
Assert.Contains(jaegerSpan.Tags, t => t.Key == JaegerActivityExtensions.JaegerErrorFlagTagName);
Assert.Contains(jaegerSpan.Tags, t => t.Key == SpanAttributeConstants.StatusDescriptionKey &&
t.VType == JaegerTagType.STRING && t.VStr.Equals(StatusDescriptionOnError));

Assert.DoesNotContain(jaegerSpan.Tags, t => t.Key == "otel.status_code" && t.VStr == "OK");

// Ensure additional Activity tags were being converted.
Assert.Contains(jaegerSpan.Tags, t => t.Key == "myCustomTag" && t.VStr == "myCustomTagValue");
}

[Fact]
public void ActivityDescription_Takes_precedence_Over_Status_Tags_When_ActivityStatusCodeIsError()
{
// Arrange.
var activity = CreateTestActivity();

const string StatusDescriptionOnError = "Description when ActivityStatusCode is Error.";
const string TagDescriptionOnError = "Description when TagStatusCode is Error.";
activity.SetStatus(ActivityStatusCode.Error, StatusDescriptionOnError);
activity.SetTag(SpanAttributeConstants.StatusCodeKey, "ERROR");
activity.SetTag(SpanAttributeConstants.StatusDescriptionKey, TagDescriptionOnError);

// Enrich activity with additional tags.
activity.SetTag("myCustomTag", "myCustomTagValue");

// Act.
var jaegerSpan = activity.ToJaegerSpan();

// Assert.
Assert.Equal("ERROR", jaegerSpan.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.StatusCodeKey).VStr);

Assert.Contains(
jaegerSpan.Tags, t =>
t.Key == JaegerActivityExtensions.JaegerErrorFlagTagName &&
t.VType == JaegerTagType.BOOL && (t.VBool ?? false));

Assert.Contains(
jaegerSpan.Tags, t =>
t.Key == SpanAttributeConstants.StatusDescriptionKey &&
t.VType == JaegerTagType.STRING && t.VStr.Equals(StatusDescriptionOnError));

// Ensure additional Activity tags were being converted.
Assert.Contains(jaegerSpan.Tags, t => t.Key == "myCustomTag" && t.VStr == "myCustomTagValue");
}

internal static Activity CreateTestActivity(
bool setAttributes = true,
Dictionary<string, object> additionalAttributes = null,
Expand Down

0 comments on commit 1962073

Please sign in to comment.