/// <summary> /// Converts Activity Links to custom property with key as _MS.links. /// Value will be a JSON string formatted as [{"operation_Id":"{TraceId}","id":"{SpanId}"}]. /// </summary> private static void AddActivityLinksToPartCTags(IEnumerable <ActivityLink> links, ref AzMonList PartCTags) { string msLinks = "_MS.links"; // max number of links that can fit in this json formatted string is 107. it is based on assumption that traceid and spanid will be of fixed length. // Keeping max at 100 for now. int maxLinks = MaxlinksAllowed; if (links != null && links.Any()) { var linksJson = new StringBuilder(); linksJson.Append('['); foreach (var link in links) { linksJson .Append('{') .Append("\"operation_Id\":") .Append('\"') .Append(link.Context.TraceId.ToHexString()) .Append('\"') .Append(','); linksJson .Append("\"id\":") .Append('\"') .Append(link.Context.SpanId.ToHexString()) .Append('\"'); linksJson.Append("},"); maxLinks--; if (maxLinks == 0) { if (MaxlinksAllowed < links.Count()) { AzureMonitorExporterEventSource.Log.Write($"ActivityLinksIgnored{EventLevelSuffix.Informational}", $"Max count of {MaxlinksAllowed} has reached."); } break; } } if (linksJson.Length > 0) { // trim trailing comma - json does not support it linksJson.Remove(linksJson.Length - 1, 1); } linksJson.Append(']'); AzMonList.Add(ref PartCTags, new KeyValuePair <string, object>(msLinks, linksJson.ToString())); } }
internal static RemoteDependencyData GetRemoteDependencyData(Activity activity, ref TagEnumerationState monitorTags) { AddActivityLinksToPartCTags(activity.Links, ref monitorTags.PartCTags); string httpUrl = null; string dependencyName; if (monitorTags.activityType == PartBType.Http) { httpUrl = monitorTags.PartBTags.GetDependencyUrl(); dependencyName = monitorTags.PartBTags.GetHttpDependencyName(httpUrl) ?? activity.DisplayName; } else { dependencyName = activity.DisplayName; } var dependency = new RemoteDependencyData(2, dependencyName, activity.Duration.ToString("c", CultureInfo.InvariantCulture)) { Id = activity.Context.SpanId.ToHexString(), Success = activity.GetStatus().StatusCode != StatusCode.Error }; switch (monitorTags.activityType) { case PartBType.Http: dependency.Data = httpUrl; dependency.Target = monitorTags.PartBTags.GetDependencyTarget(PartBType.Http); dependency.Type = "Http"; dependency.ResultCode = AzMonList.GetTagValue(ref monitorTags.PartBTags, SemanticConventions.AttributeHttpStatusCode)?.ToString() ?? "0"; break; case PartBType.Db: var depDataAndType = AzMonList.GetTagValues(ref monitorTags.PartBTags, SemanticConventions.AttributeDbStatement, SemanticConventions.AttributeDbSystem); dependency.Data = depDataAndType[0]?.ToString(); dependency.Target = monitorTags.PartBTags.GetDbDependencyTarget(); dependency.Type = SqlDbs.Contains(depDataAndType[1]?.ToString()) ? "SQL" : depDataAndType[1]?.ToString(); break; case PartBType.Rpc: var depInfo = AzMonList.GetTagValues(ref monitorTags.PartBTags, SemanticConventions.AttributeRpcService, SemanticConventions.AttributeRpcSystem, SemanticConventions.AttributeRpcStatus); dependency.Data = depInfo[0]?.ToString(); dependency.Type = depInfo[1]?.ToString(); dependency.ResultCode = depInfo[2]?.ToString(); break; case PartBType.Messaging: depDataAndType = AzMonList.GetTagValues(ref monitorTags.PartBTags, SemanticConventions.AttributeMessagingUrl, SemanticConventions.AttributeMessagingSystem); dependency.Data = depDataAndType[0]?.ToString(); dependency.Type = depDataAndType[1]?.ToString(); break; } if (activity.Kind == ActivityKind.Internal && activity.Parent != null) { dependency.Type = "InProc"; } AddPropertiesToTelemetry(dependency.Properties, ref monitorTags.PartCTags); return(dependency); }
private static void AddPropertiesToTelemetry(IDictionary <string, string> destination, ref AzMonList PartCTags) { // TODO: Iterate only interested fields. Ref: https://github.com/Azure/azure-sdk-for-net/pull/14254#discussion_r470907560 for (int i = 0; i < PartCTags.Length; i++) { destination.Add(PartCTags[i].Key, PartCTags[i].Value?.ToString()); } }