Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add new span attributes to more closely match OTel spec #1976

Merged
merged 7 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -43,7 +43,11 @@ public interface IAttributeDefinitions
AttributeDefinition<float, double> DatabaseDuration { get; }
AttributeDefinition<string, string> DbCollection { get; }
AttributeDefinition<string, string> DbInstance { get; }
AttributeDefinition<string, string> DbOperation { get; }
AttributeDefinition<string, string> DbServerAddress { get; }
AttributeDefinition<long, long> DbServerPort { get; }
AttributeDefinition<string, string> DbStatement { get; }
AttributeDefinition<string, string> DbSystem { get; }
AttributeDefinition<string, string> DistributedTraceId { get; }
AttributeDefinition<TimeSpan, double> Duration { get; }
AttributeDefinition<bool, bool> IsErrorExpected { get; }
Expand Down Expand Up @@ -91,6 +95,8 @@ public interface IAttributeDefinitions
AttributeDefinition<string, string> RequestUri { get; }
AttributeDefinition<int?, string> ResponseStatus { get; }
AttributeDefinition<bool, bool> Sampled { get; }
AttributeDefinition<string, string> ServerAddress { get; }
AttributeDefinition<long, long> ServerPort { get; }
AttributeDefinition<SpanCategory, string> SpanCategory { get; }
AttributeDefinition<string, string> SpanErrorClass { get; }
AttributeDefinition<string, string> SpanErrorMessage { get; }
Expand All @@ -102,6 +108,7 @@ public interface IAttributeDefinitions
AttributeDefinition<string, string> SyntheticsMonitorIdForTraces { get; }
AttributeDefinition<string, string> SyntheticsResourceId { get; }
AttributeDefinition<string, string> SyntheticsResourceIdForTraces { get; }
AttributeDefinition<long, long> ThreadId { get; }
AttributeDefinition<DateTime, long> Timestamp { get; }
AttributeDefinition<DateTime, long> TimestampForError { get; }
AttributeDefinition<TimeSpan, double> TotalTime { get; }
Expand Down Expand Up @@ -476,6 +483,12 @@ public AttributeDefinition<TypeAttributeValue, string> GetTypeAttribute(TypeAttr
.AppliesTo(AttributeDestinations.SpanEvent)
.Build(_attribFilter));

private AttributeDefinition<long, long> _threadId;
public AttributeDefinition<long, long> ThreadId => _threadId ?? (_threadId =
AttributeDefinitionBuilder.CreateLong("thread.id", AttributeClassification.Intrinsics)
nrcventura marked this conversation as resolved.
Show resolved Hide resolved
.AppliesTo(AttributeDestinations.SpanEvent)
.Build(_attribFilter));

private AttributeDefinition<string, string> _component;
public AttributeDefinition<string, string> Component => _component ?? (_component =
AttributeDefinitionBuilder.CreateString("component", AttributeClassification.Intrinsics)
Expand Down Expand Up @@ -513,6 +526,18 @@ public AttributeDefinition<TypeAttributeValue, string> GetTypeAttribute(TypeAttr
.AppliesTo(AttributeDestinations.SpanEvent)
.Build(_attribFilter));

private AttributeDefinition<string, string> _dbSystem;
public AttributeDefinition<string, string> DbSystem => _dbSystem ?? (_dbSystem =
AttributeDefinitionBuilder.CreateString("db.system", AttributeClassification.AgentAttributes)
.AppliesTo(AttributeDestinations.SpanEvent)
.Build(_attribFilter));

private AttributeDefinition<string, string> _dbOperation;
public AttributeDefinition<string, string> DbOperation => _dbOperation ?? (_dbOperation =
AttributeDefinitionBuilder.CreateString("db.operation", AttributeClassification.AgentAttributes)
.AppliesTo(AttributeDestinations.SpanEvent)
.Build(_attribFilter));

private AttributeDefinition<string, string> _dbCollection;
public AttributeDefinition<string, string> DbCollection => _dbCollection ?? (_dbCollection =
AttributeDefinitionBuilder.CreateString("db.collection", AttributeClassification.AgentAttributes)
Expand Down Expand Up @@ -546,7 +571,31 @@ public AttributeDefinition<TypeAttributeValue, string> GetTypeAttribute(TypeAttr

private AttributeDefinition<string, string> _httpMethod;
public AttributeDefinition<string, string> HttpMethod => _httpMethod ?? (_httpMethod =
AttributeDefinitionBuilder.CreateString("http.method", AttributeClassification.AgentAttributes)
AttributeDefinitionBuilder.CreateString("http.request.method", AttributeClassification.AgentAttributes)
.AppliesTo(AttributeDestinations.SpanEvent)
.Build(_attribFilter));

private AttributeDefinition<string, string> _serverAddress;
public AttributeDefinition<string, string> ServerAddress => _serverAddress ?? (_serverAddress =
AttributeDefinitionBuilder.CreateString("server.address", AttributeClassification.Intrinsics)
.AppliesTo(AttributeDestinations.SpanEvent)
.Build(_attribFilter));

private AttributeDefinition<long, long> _serverPort;
public AttributeDefinition<long, long> ServerPort => _serverPort ?? (_serverPort =
AttributeDefinitionBuilder.CreateLong("server.port", AttributeClassification.Intrinsics)
.AppliesTo(AttributeDestinations.SpanEvent)
.Build(_attribFilter));

private AttributeDefinition<string, string> _dbServerAddress;
public AttributeDefinition<string, string> DbServerAddress => _dbServerAddress ?? (_dbServerAddress =
AttributeDefinitionBuilder.CreateString("server.address", AttributeClassification.AgentAttributes)
.AppliesTo(AttributeDestinations.SpanEvent)
.Build(_attribFilter));

private AttributeDefinition<long, long> _dbServerPort;
public AttributeDefinition<long, long> DbServerPort => _dbServerPort ?? (_dbServerPort =
AttributeDefinitionBuilder.CreateLong("server.port", AttributeClassification.AgentAttributes)
.AppliesTo(AttributeDestinations.SpanEvent)
.Build(_attribFilter));

Expand Down
15 changes: 13 additions & 2 deletions src/Agent/NewRelic/Agent/Core/Segments/DatastoreSegmentData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@ namespace NewRelic.Agent.Core.Segments
{
public class DatastoreSegmentData : AbstractSegmentData, IDatastoreSegmentData
{
private readonly static ConnectionInfo EmptyConnectionInfo = new ConnectionInfo(null, null, null);
private readonly static ConnectionInfo EmptyConnectionInfo = new ConnectionInfo(null, null, null, null);

public override SpanCategory SpanCategory => SpanCategory.Datastore;

public string Operation => _parsedSqlStatement.Operation;
public DatastoreVendor DatastoreVendorName => _parsedSqlStatement.DatastoreVendor;
public string Model => _parsedSqlStatement.Model;
public string CommandText { get; set; }
public string Vendor => _connectionInfo.Vendor;
public string Host => _connectionInfo.Host;
public int? Port => _connectionInfo.Port;
public string PathOrId => _connectionInfo.PathOrId;
public string PortPathOrId => _connectionInfo.PortPathOrId;
public string DatabaseName => _connectionInfo.DatabaseName;
public Func<object> GetExplainPlanResources { get; set; }
Expand Down Expand Up @@ -219,10 +222,18 @@ public override void SetSpanTypeSpecificAttributes(SpanAttributeValueCollection
AttribDefs.DbCollection.TrySetValue(attribVals, _parsedSqlStatement.Model);
}

AttribDefs.DbSystem.TrySetValue(attribVals, Vendor);
AttribDefs.DbInstance.TrySetValue(attribVals, DatabaseName);
AttribDefs.DbOperation.TrySetValue(attribVals, Operation);
AttribDefs.PeerAddress.TrySetValue(attribVals, $"{Host}:{PortPathOrId}");
AttribDefs.PeerHostname.TrySetValue(attribVals, Host);
AttribDefs.SpanKind.TrySetDefault(attribVals);
// peer.hostname and server.address must match
AttribDefs.PeerHostname.TrySetValue(attribVals, Host);
AttribDefs.DbServerAddress.TrySetValue(attribVals, Host);
if (Port != null)
{
AttribDefs.DbServerPort.TrySetValue(attribVals, Port.Value);
}
}

public void SetConnectionInfo(ConnectionInfo connInfo)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ public override void SetSpanTypeSpecificAttributes(SpanAttributeValueCollection
AttribDefs.Component.TrySetValue(attribVals, _segmentState.TypeName);
AttribDefs.SpanKind.TrySetDefault(attribVals);
AttribDefs.HttpStatusCode.TrySetValue(attribVals, _httpStatusCode); //Attrib handles null
AttribDefs.ServerAddress.TrySetValue(attribVals, Uri.Host);
AttribDefs.ServerPort.TrySetValue(attribVals, Uri.Port);
}

public override string GetTransactionTraceName()
Expand Down
21 changes: 21 additions & 0 deletions src/Agent/NewRelic/Agent/Core/Segments/Segment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -441,5 +441,26 @@ public string GetCategory()
{
return EnumNameCache<SpanCategory>.GetName(Data.SpanCategory);
}

public static int? CheckAllSameThread(ICollection<Segment> segments)
chynesNR marked this conversation as resolved.
Show resolved Hide resolved
{
if (segments == null)
return null;

int? threadId = null;

foreach (Segment segment in segments)
{
if (threadId == null)
{
threadId = segment.ThreadId;
}
else if (threadId != segment.ThreadId)
{
return null;
}
}
return threadId;
}
}
}
6 changes: 6 additions & 0 deletions src/Agent/NewRelic/Agent/Core/Spans/SpanEventMaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ private SpanAttributeValueCollection GenerateRootSpan(string rootSpanId, Immutab
_attribDefs.SpanCategory.TrySetValue(spanAttributes, SpanCategory.Generic);
_attribDefs.NrEntryPoint.TrySetValue(spanAttributes, true);

var threadId = Segment.CheckAllSameThread(immutableTransaction.Segments);
if (threadId != null)
{
_attribDefs.ThreadId.TrySetValue(spanAttributes, threadId.Value);
}

spanAttributes.AddRange(transactionAttribValues.GetAttributeValues(AttributeClassification.UserAttributes));

spanAttributes.MakeImmutable();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,25 @@ namespace NewRelic.Agent.Extensions.Parsing
{
public class ConnectionInfo
{
public ConnectionInfo(string host, string portPathOrId, string databaseName, string instanceName = null)
public ConnectionInfo(string vendor, string host, int port, string databaseName, string instanceName = null)
{
Vendor = vendor;
Host = ValueOrUnknown(host);
PortPathOrId = ValueOrUnknown(portPathOrId);
if (port >= 0)
{
Port = port;
}
PathOrId = string.Empty;
DatabaseName = ValueOrUnknown(databaseName);
InstanceName = instanceName;
}

public ConnectionInfo(string vendor, string host, string pathOrId, string databaseName, string instanceName = null)
{
Vendor = vendor;
Host = ValueOrUnknown(host);
Port = null;
PathOrId = ValueOrUnknown(pathOrId);
DatabaseName = ValueOrUnknown(databaseName);
InstanceName = instanceName;
}
Expand All @@ -18,8 +33,11 @@ private static string ValueOrUnknown(string value)
return string.IsNullOrEmpty(value) ? "unknown" : value;
}

public string Vendor { get; private set; }
public string Host { get; private set; }
public string PortPathOrId { get; private set; }
public string PortPathOrId { get => (Port != null) ? Port.ToString() : PathOrId; }
public int? Port { get; private set; } = null;
public string PathOrId { get; private set; } = string.Empty;
public string DatabaseName { get; private set; }
public string InstanceName { get; private set; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
tippmar-nr marked this conversation as resolved.
Show resolved Hide resolved
using System.Text;

namespace NewRelic.Agent.Extensions.Providers.Wrapper
Expand Down Expand Up @@ -82,6 +83,24 @@ public enum DatastoreVendor
Other
}

public static class DatastoreVendorExtensions
{
// Convert our internal enum to the matching OTel "known" name for a database provider
public static string ToKnownName(this DatastoreVendor vendor)
{
switch (vendor)
{
case DatastoreVendor.Other:
return "other_sql";
case DatastoreVendor.IBMDB2:
return "db2";
// The others match our enum name
default:
return vendor.ToString().ToLower();
nrcventura marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

public static class EnumNameCache<TEnum> // c# 7.3: where TEnum : System.Enum
{
private static readonly ConcurrentDictionary<TEnum, string> Cache = new ConcurrentDictionary<TEnum, string>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall ins
var segment = transaction.StartDatastoreSegment(
instrumentedMethodCall.MethodCall,
new ParsedSqlStatement(DatastoreVendor.CosmosDB, model, operation),
connectionInfo: endpoint != null ? new ConnectionInfo(endpoint.Host, endpoint.Port.ToString(), databaseName) : new ConnectionInfo(string.Empty, string.Empty, databaseName),
connectionInfo: endpoint != null ? new ConnectionInfo(DatastoreVendor.CosmosDB.ToKnownName(), endpoint.Host, endpoint.Port, databaseName) : new ConnectionInfo(string.Empty, string.Empty, string.Empty, databaseName),
commandText : querySpec != null ? _queryGetter.Invoke(querySpec) : string.Empty,
isLeaf: true);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall ins
var segment = transaction.StartDatastoreSegment(
instrumentedMethodCall.MethodCall,
new ParsedSqlStatement(DatastoreVendor.CosmosDB, model, operation),
connectionInfo: endpoint != null ? new ConnectionInfo(endpoint.Host, endpoint.Port.ToString(), databaseName) : new ConnectionInfo(string.Empty, string.Empty, databaseName),
connectionInfo: endpoint != null ? new ConnectionInfo(DatastoreVendor.CosmosDB.ToKnownName(), endpoint.Host, endpoint.Port, databaseName) : new ConnectionInfo(string.Empty, string.Empty, string.Empty, databaseName),
isLeaf: true);

return Delegates.GetAsyncDelegateFor<Task>(agent, segment);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall ins
}

var transactionExperimental = transaction.GetExperimentalApi();
var datastoreSegmentData = transactionExperimental.CreateDatastoreSegmentData(new ParsedSqlStatement(DatastoreVendor.Elasticsearch, model, operation), new ConnectionInfo(string.Empty, string.Empty, string.Empty), string.Empty, null);
var datastoreSegmentData = transactionExperimental.CreateDatastoreSegmentData(new ParsedSqlStatement(DatastoreVendor.Elasticsearch, model, operation), new ConnectionInfo(DatastoreVendor.Elasticsearch.ToKnownName(), string.Empty, string.Empty, string.Empty), string.Empty, null);
var segment = transactionExperimental.StartSegment(instrumentedMethodCall.MethodCall);
segment.GetExperimentalApi().SetSegmentData(datastoreSegmentData).MakeLeaf();

Expand Down Expand Up @@ -269,7 +269,7 @@ private static void SetUriOnDatastoreSegment(ISegment segment, Uri uri)
{
var segmentExperimentalApi = segment.GetExperimentalApi();
var data = segmentExperimentalApi.SegmentData as IDatastoreSegmentData;
data.SetConnectionInfo(new ConnectionInfo(uri.Host, uri.Port.ToString(), string.Empty));
data.SetConnectionInfo(new ConnectionInfo(DatastoreVendor.Elasticsearch.ToKnownName(), uri.Host, uri.Port, string.Empty));
segmentExperimentalApi.SetSegmentData(data);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Collections.Concurrent;
using System.Net;
using NewRelic.Agent.Extensions.Parsing;
using NewRelic.Agent.Extensions.Providers.Wrapper;
using NewRelic.Parsing.ConnectionString;
using NewRelic.Reflection;

Expand Down Expand Up @@ -104,7 +105,7 @@ private static object GetServerFromFromInterface(object owner)
public static ConnectionInfo GetConnectionInfoFromCursor(object asyncCursor, object collectionNamespace, string utilizationHostName)
{
string host = null;
string port = null;
int port = -1;

var channelSource = GetChannelSourceFieldFromGeneric(asyncCursor);
var server = GetServerFromFromInterface(channelSource);
Expand All @@ -115,37 +116,37 @@ public static ConnectionInfo GetConnectionInfoFromCursor(object asyncCursor, obj

if (dnsEndpoint != null)
{
port = dnsEndpoint.Port.ToString();
port = dnsEndpoint.Port;
host = ConnectionStringParserHelper.NormalizeHostname(dnsEndpoint.Host, utilizationHostName);
}

if (ipEndpoint != null)
{
port = ipEndpoint.Port.ToString();
port = ipEndpoint.Port;
host = ConnectionStringParserHelper.NormalizeHostname(ipEndpoint.Address.ToString(), utilizationHostName);
}

var databaseName = GetDatabaseNameFromCollectionNamespace(collectionNamespace);

return new ConnectionInfo(host, port, databaseName);
return new ConnectionInfo(DatastoreVendor.MongoDB.ToKnownName(), host, port, databaseName);
}

public static ConnectionInfo GetConnectionInfoFromDatabase(object database, string utilizationHostName)
{
var databaseName = GetDatabaseNameFromDatabase(database);
var servers = GetServersFromDatabase(database);

string port = null;
int port = -1;
string host = null;

if (servers.Count == 1)
{
GetHostAndPortFromServer(servers[0], out var rawHost, out var rawPort);
port = rawPort.ToString();
port = rawPort;
host = ConnectionStringParserHelper.NormalizeHostname(rawHost, utilizationHostName);
}

return new ConnectionInfo(host, port, databaseName);
return new ConnectionInfo(DatastoreVendor.MongoDB.ToKnownName(), host, port, databaseName);
}

private static IList GetServersFromDatabase(object database)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,13 @@ public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall ins

var host = TryGetPropertyName(PropertyHost, contextObject) ?? "unknown";
host = ConnectionStringParserHelper.NormalizeHostname(host, agent.Configuration.UtilizationHostName);
var portPathOrId = TryGetPropertyName(PropertyPortPathOrId, contextObject);
var port = TryGetPropertyName(PropertyPortPathOrId, contextObject);
if (!int.TryParse(port, out int portNum))
{
portNum = -1;
}
var databaseName = TryGetPropertyName(PropertyDatabaseName, contextObject);
var connectionInfo = new ConnectionInfo(host, portPathOrId, databaseName);
var connectionInfo = new ConnectionInfo(DatastoreVendor.Redis.ToKnownName(), host, portNum, databaseName);

var segment = transaction.StartDatastoreSegment(instrumentedMethodCall.MethodCall, ParsedSqlStatement.FromOperation(DatastoreVendor.Redis, operation), connectionInfo);

Expand Down
Loading
Loading