in src/Elastic.Apm/OpenTelemetry/ElasticActivityListener.cs [300:414]
private static void InferSpanTypeAndSubType(Span span, Activity activity)
{
static string HttpPortFromScheme(string scheme)
{
return scheme switch
{
"http" => "80",
"https" => "443",
_ => string.Empty
};
}
// extracts 'host' or 'host:port' from URL
static string ParseNetName(string url)
{
try
{
var u = new Uri(url); // https://developer.mozilla.org/en-US/docs/Web/API/URL
return u.Host + ':' + u.Port;
}
catch
{
return string.Empty;
}
}
static string ToResourceName(string type, string name)
{
return string.IsNullOrEmpty(name) ? type : $"{type}/{name}";
}
var peerPort = string.Empty;
var netName = string.Empty;
if (TryGetStringValue(activity, ServerPortAttributeKeys, out var netPortValue))
peerPort = netPortValue;
if (TryGetStringValue(activity, ServerAddressAttributeKeys, out var netNameValue))
netName = netNameValue;
if (netName.Length > 0 && peerPort.Length > 0)
{
netName += ':';
netName += peerPort;
}
string serviceTargetType = null;
string serviceTargetName = null;
string resource = null;
if (TryGetStringValue(activity, SemanticConventions.DbSystem, out var dbSystem))
{
span.Type = ApiConstants.TypeDb;
span.Subtype = dbSystem;
serviceTargetType = span.Subtype;
serviceTargetName = TryGetStringValue(activity, SemanticConventions.DbName, out var dbName) ? dbName : null;
resource = ToResourceName(span.Subtype, serviceTargetName);
}
else if (TryGetStringValue(activity, SemanticConventions.MessagingSystem, out var messagingSystem))
{
span.Type = ApiConstants.TypeMessaging;
span.Subtype = messagingSystem;
serviceTargetType = span.Subtype;
serviceTargetName = TryGetStringValue(activity, SemanticConventions.MessagingDestination, out var messagingDestination)
? messagingDestination
: null;
resource = ToResourceName(span.Subtype, serviceTargetName);
}
else if (TryGetStringValue(activity, SemanticConventions.RpcSystem, out var rpcSystem))
{
span.Type = ApiConstants.TypeExternal;
span.Subtype = rpcSystem;
serviceTargetType = span.Subtype;
serviceTargetName = !string.IsNullOrEmpty(netName)
? netName
: TryGetStringValue(activity, SemanticConventions.RpcService, out var rpcService)
? rpcService
: null;
resource = serviceTargetName ?? span.Subtype;
}
else if (activity.TagObjects.Any(n =>
n.Key == SemanticConventions.HttpUrl || n.Key == SemanticConventions.UrlFull || n.Key == SemanticConventions.HttpScheme))
{
var hasHttpHost = TryGetStringValue(activity, SemanticConventions.HttpHost, out var httpHost);
var hasHttpScheme = TryGetStringValue(activity, SemanticConventions.HttpScheme, out var httpScheme);
span.Type = ApiConstants.TypeExternal;
span.Subtype = ApiConstants.SubtypeHttp;
serviceTargetType = span.Subtype;
if (hasHttpHost && hasHttpScheme)
serviceTargetName = $"{httpHost}:{HttpPortFromScheme(httpScheme)}";
else if (TryGetStringValue(activity, HttpUrlAttributeKeys, out var httpUrl))
serviceTargetName = ParseNetName(httpUrl);
else
serviceTargetName = netName;
resource = serviceTargetName;
}
if (serviceTargetType == null)
{
if (activity.Kind == ActivityKind.Internal)
{
span.Type = ApiConstants.TypeApp;
span.Subtype = ApiConstants.SubTypeInternal;
}
else
span.Type = ApiConstants.TypeUnknown;
}
span.Context.Service = new SpanService(new Target(serviceTargetType, serviceTargetName));
if (resource != null)
{
span.Context.Destination ??= new Destination();
span.Context.Destination.Service = new Destination.DestinationService { Resource = resource };
}
}