protected override void HandleOnNext(KeyValuePair <string, object> kv) { Logger.Trace() ?.Log("{currentClassName} received diagnosticsource event: {eventKey}", nameof(GrpcClientDiagnosticListener), kv.Key); var currentActivity = Activity.Current; if (kv.Key == "Grpc.Net.Client.GrpcOut.Start") { if (kv.Value.GetType().GetTypeInfo().GetDeclaredProperty("Request")?.GetValue(kv.Value) is HttpRequestMessage requestObject) { var currentTransaction = ApmAgent?.Tracer.CurrentTransaction; if (currentTransaction != null) { var grpcMethodName = currentActivity?.Tags.FirstOrDefault(n => n.Key == "grpc.method").Value; if (string.IsNullOrEmpty(grpcMethodName)) { grpcMethodName = "unknown"; } Logger.Trace()?.Log("Starting span for gRPC call, method:{methodName}", grpcMethodName); var newSpan = currentTransaction.StartSpan(grpcMethodName, ApiConstants.TypeExternal, ApiConstants.SubTypeGrpc, isExitSpan: true); ProcessingRequests.TryAdd(requestObject, newSpan); } } } if (kv.Key == "Grpc.Net.Client.GrpcOut.Stop") { if (kv.Value.GetType().GetTypeInfo().GetDeclaredProperty("Request")?.GetValue(kv.Value) is not HttpRequestMessage requestObject) { return; } if (!ProcessingRequests.TryRemove(requestObject, out var span)) { return; } Logger.Trace()?.Log("Ending span for gRPC call, span:{span}", span); var grpcStatusCode = currentActivity?.Tags?.Where(n => n.Key == "grpc.status_code").FirstOrDefault().Value; if (grpcStatusCode != null) { span.Outcome = GrpcHelper.GrpcClientReturnCodeToOutcome(GrpcHelper.GrpcReturnCodeToString(grpcStatusCode)); } span.Context.Destination = UrlUtils.ExtractDestination(requestObject.RequestUri, Logger); span.Context.Destination.Service = new() { Resource = UrlUtils.ExtractService(requestObject.RequestUri, span) }; span.End(); } } }
private void DeduceDestination() { if (!_context.IsValueCreated) { return; } if (Context.Http != null) { var destination = DeduceHttpDestination(); if (destination == null) { // In case of invalid destination just return return; } CopyMissingProperties(destination); } FillDestinationService(); // Fills Context.Destination.Service void FillDestinationService() { // Context.Destination must be set by the instrumentation part - otherwise we won't fill Context.Destination.Service if (Context.Destination == null) { return; } // Context.Destination.Service can be set by the instrumentation part - only fill it if needed. if (Context.Destination.Service != null) { return; } Context.Destination.Service = new Destination.DestinationService { Type = Type }; if (_context.Value.Http != null) { if (!_context.Value.Http.OriginalUrl.IsAbsoluteUri) { // Can't fill Destination.Service - we just set it to null and return Context.Destination.Service = null; return; } Context.Destination.Service = UrlUtils.ExtractService(_context.Value.Http.OriginalUrl, this); } else { // Once messaging is added, for messaging, we'll additionally need to add the queue name here Context.Destination.Service.Resource = Subtype; Context.Destination.Service.Name = Subtype; } } void CopyMissingProperties(Destination src) { if (src == null) { return; } if (Context.Destination == null) { Context.Destination = src; } else { Context.Destination.CopyMissingPropertiesFrom(src); } } }