Jaeger: Include net.peer.port in peer.service attribute (#1195)

* Include net.peer.port in peer.service attribute when applicable

* Update changelog

* Small change to if statement

Co-authored-by: Cijo Thomas <cithomas@microsoft.com>
This commit is contained in:
Alan West 2020-08-31 20:09:58 -07:00 committed by GitHub
parent fd7876416f
commit 19f834fc82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 192 additions and 28 deletions

View File

@ -11,6 +11,9 @@ Released 2020-08-28
* Span links will now be sent as `FOLLOWS_FROM` reference type. Previously they
were sent as `CHILD_OF`.
([#970](https://github.com/open-telemetry/opentelemetry-dotnet/pull/970))
* Fixed issue when span has both the `net.peer.name` and `net.peer.port`
attributes but did not include `net.peer.port` in the `peer.service` field
([#1195](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1195)).
* Renamed extension method from `UseJaegerExporter` to `AddJaegerExporter`.

View File

@ -44,12 +44,10 @@ namespace OpenTelemetry.Exporter.Jaeger.Implementation
private static readonly Dictionary<string, int> PeerServiceKeyResolutionDictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
{
[SemanticConventions.AttributePeerService] = 0, // priority 0 (highest).
[SemanticConventions.AttributeNetPeerName] = 1,
[SemanticConventions.AttributeNetPeerIp] = 2,
["peer.hostname"] = 2,
["peer.address"] = 2,
[SemanticConventions.AttributeHttpHost] = 3, // peer.service for Http.
[SemanticConventions.AttributeDbInstance] = 3, // peer.service for Redis.
["peer.hostname"] = 1,
["peer.address"] = 1,
[SemanticConventions.AttributeHttpHost] = 2, // peer.service for Http.
[SemanticConventions.AttributeDbInstance] = 2, // peer.service for Redis.
};
private static readonly DictionaryEnumerator<string, object, TagState>.ForEachDelegate ProcessActivityTagRef = ProcessActivityTag;
@ -70,13 +68,31 @@ namespace OpenTelemetry.Exporter.Jaeger.Implementation
ProcessActivityTagRef);
string peerServiceName = null;
if ((activity.Kind == ActivityKind.Client || activity.Kind == ActivityKind.Producer) && jaegerTags.PeerService != null)
if (activity.Kind == ActivityKind.Client || activity.Kind == ActivityKind.Producer)
{
// Send peer.service for remote calls.
peerServiceName = jaegerTags.PeerService;
// If priority = 0 that means peer.service may have already been included in tags
var addPeerServiceTag = jaegerTags.PeerServicePriority > 0;
// If priority = 0 that means peer.service was already included in tags.
if (jaegerTags.PeerServicePriority > 0)
var hostNameOrIpAddress = jaegerTags.HostName ?? jaegerTags.IpAddress;
// peer.service has not already been included, but net.peer.name/ip and optionally net.peer.port are present
if ((jaegerTags.PeerService == null || addPeerServiceTag)
&& hostNameOrIpAddress != null)
{
peerServiceName = jaegerTags.Port == default
? hostNameOrIpAddress
: $"{hostNameOrIpAddress}:{jaegerTags.Port}";
// Add the peer.service tag
addPeerServiceTag = true;
}
if (peerServiceName == null && jaegerTags.PeerService != null)
{
peerServiceName = jaegerTags.PeerService;
}
if (peerServiceName != null && addPeerServiceTag)
{
PooledList<JaegerTag>.Add(ref jaegerTags.Tags, new JaegerTag(SemanticConventions.AttributePeerService, JaegerTagType.STRING, vStr: peerServiceName));
}
@ -291,12 +307,30 @@ namespace OpenTelemetry.Exporter.Jaeger.Implementation
private static void ProcessJaegerTag(ref TagState state, string key, JaegerTag jaegerTag)
{
if (jaegerTag.VStr != null
&& PeerServiceKeyResolutionDictionary.TryGetValue(key, out int priority)
&& (state.PeerService == null || priority < state.PeerServicePriority))
if (jaegerTag.VStr != null)
{
state.PeerService = jaegerTag.VStr;
state.PeerServicePriority = priority;
if (PeerServiceKeyResolutionDictionary.TryGetValue(key, out int priority)
&& (state.PeerService == null || priority < state.PeerServicePriority))
{
state.PeerService = jaegerTag.VStr;
state.PeerServicePriority = priority;
}
else if (key == SemanticConventions.AttributeNetPeerName)
{
state.HostName = jaegerTag.VStr;
}
else if (key == SemanticConventions.AttributeNetPeerIp)
{
state.IpAddress = jaegerTag.VStr;
}
else if (key == SemanticConventions.AttributeNetPeerPort && long.TryParse(jaegerTag.VStr, out var port))
{
state.Port = port;
}
}
else if (jaegerTag.VLong.HasValue && key == SemanticConventions.AttributeNetPeerPort)
{
state.Port = jaegerTag.VLong.Value;
}
PooledList<JaegerTag>.Add(ref state.Tags, jaegerTag);
@ -340,6 +374,12 @@ namespace OpenTelemetry.Exporter.Jaeger.Implementation
public string PeerService;
public int PeerServicePriority;
public string HostName;
public string IpAddress;
public long Port;
}
private struct PooledListState<T>

View File

@ -20,7 +20,7 @@ using System.Linq;
using OpenTelemetry.Exporter.Jaeger.Implementation;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using Xunit;
namespace OpenTelemetry.Exporter.Jaeger.Tests.Implementation
@ -396,26 +396,21 @@ namespace OpenTelemetry.Exporter.Jaeger.Tests.Implementation
Assert.Empty(jaegerSpan.Tags.Where(t => t.Key == "peer.service"));
}
[Fact]
public void JaegerActivityConverterTest_GenerateJaegerSpan_RemoteEndpointResolutionPriority()
[Theory]
[MemberData(nameof(RemoteEndpointPriorityTestCase.GetTestCases), MemberType = typeof(RemoteEndpointPriorityTestCase))]
public void JaegerActivityConverterTest_GenerateJaegerSpan_RemoteEndpointResolutionPriority(RemoteEndpointPriorityTestCase testCase)
{
// Arrange
var span = CreateTestActivity(
additionalAttributes: new Dictionary<string, object>
{
["http.host"] = "DiscardedRemoteServiceName",
["peer.service"] = "RemoteServiceName",
["peer.hostname"] = "DiscardedRemoteServiceName",
});
var activity = CreateTestActivity(additionalAttributes: testCase.RemoteEndpointAttributes);
// Act
var jaegerSpan = span.ToJaegerSpan();
var jaegerSpan = activity.ToJaegerSpan();
// Assert
var tags = jaegerSpan.Tags.Where(t => t.Key == "peer.service");
Assert.Single(tags);
var tag = tags.First();
Assert.Equal("RemoteServiceName", tag.VStr);
Assert.Equal(testCase.ExpectedResult, tag.VStr);
}
internal static Activity CreateTestActivity(
@ -510,5 +505,131 @@ namespace OpenTelemetry.Exporter.Jaeger.Tests.Implementation
return activity;
}
public class RemoteEndpointPriorityTestCase
{
public string Name { get; set; }
public string ExpectedResult { get; set; }
public Dictionary<string, object> RemoteEndpointAttributes { get; set; }
public static IEnumerable<object[]> GetTestCases()
{
yield return new object[]
{
new RemoteEndpointPriorityTestCase
{
Name = "Highest priority name = net.peer.name",
ExpectedResult = "RemoteServiceName",
RemoteEndpointAttributes = new Dictionary<string, object>
{
["http.host"] = "DiscardedRemoteServiceName",
["net.peer.name"] = "RemoteServiceName",
["peer.hostname"] = "DiscardedRemoteServiceName",
},
},
};
yield return new object[]
{
new RemoteEndpointPriorityTestCase
{
Name = "Highest priority name = SemanticConventions.AttributePeerService",
ExpectedResult = "RemoteServiceName",
RemoteEndpointAttributes = new Dictionary<string, object>
{
[SemanticConventions.AttributePeerService] = "RemoteServiceName",
["http.host"] = "DiscardedRemoteServiceName",
["net.peer.name"] = "DiscardedRemoteServiceName",
["net.peer.port"] = "1234",
["peer.hostname"] = "DiscardedRemoteServiceName",
},
},
};
yield return new object[]
{
new RemoteEndpointPriorityTestCase
{
Name = "Only has net.peer.name and net.peer.port",
ExpectedResult = "RemoteServiceName:1234",
RemoteEndpointAttributes = new Dictionary<string, object>
{
["net.peer.name"] = "RemoteServiceName",
["net.peer.port"] = "1234",
},
},
};
yield return new object[]
{
new RemoteEndpointPriorityTestCase
{
Name = "net.peer.port is an int",
ExpectedResult = "RemoteServiceName:1234",
RemoteEndpointAttributes = new Dictionary<string, object>
{
["net.peer.name"] = "RemoteServiceName",
["net.peer.port"] = 1234,
},
},
};
yield return new object[]
{
new RemoteEndpointPriorityTestCase
{
Name = "Has net.peer.name and net.peer.port",
ExpectedResult = "RemoteServiceName:1234",
RemoteEndpointAttributes = new Dictionary<string, object>
{
["http.host"] = "DiscardedRemoteServiceName",
["net.peer.name"] = "RemoteServiceName",
["net.peer.port"] = "1234",
["peer.hostname"] = "DiscardedRemoteServiceName",
},
},
};
yield return new object[]
{
new RemoteEndpointPriorityTestCase
{
Name = "Has net.peer.ip and net.peer.port",
ExpectedResult = "1.2.3.4:1234",
RemoteEndpointAttributes = new Dictionary<string, object>
{
["http.host"] = "DiscardedRemoteServiceName",
["net.peer.ip"] = "1.2.3.4",
["net.peer.port"] = "1234",
["peer.hostname"] = "DiscardedRemoteServiceName",
},
},
};
yield return new object[]
{
new RemoteEndpointPriorityTestCase
{
Name = "Has net.peer.name, net.peer.ip, and net.peer.port",
ExpectedResult = "RemoteServiceName:1234",
RemoteEndpointAttributes = new Dictionary<string, object>
{
["http.host"] = "DiscardedRemoteServiceName",
["net.peer.name"] = "RemoteServiceName",
["net.peer.ip"] = "1.2.3.4",
["net.peer.port"] = "1234",
["peer.hostname"] = "DiscardedRemoteServiceName",
},
},
};
}
public override string ToString()
{
return this.Name;
}
}
}
}