private void ProcessStartEvent(TRequest request, Uri requestUrl)
        {
            Logger.Trace()?.Log("Processing start event... Request URL: {RequestUrl}", Http.Sanitize(requestUrl));

            var transaction = ApmAgent.Tracer.CurrentTransaction;

            if (transaction == null)
            {
                Logger.Debug()?.Log("No current transaction, skip creating span for outgoing HTTP request");
                return;
            }

            var span = ExecutionSegmentCommon.StartSpanOnCurrentExecutionSegment(ApmAgent, $"{RequestGetMethod(request)} {requestUrl.Host}",
                                                                                 ApiConstants.TypeExternal, ApiConstants.SubtypeHttp, InstrumentationFlag.HttpClient, true);

            if (!ProcessingRequests.TryAdd(request, span))
            {
                // Consider improving error reporting - see https://github.com/elastic/apm-agent-dotnet/issues/280
                Logger.Error()?.Log("Failed to add to ProcessingRequests - ???");
                return;
            }

            if (!RequestHeadersContain(request, TraceContext.TraceParentHeaderName))
            {
                // We call TraceParent.BuildTraceparent explicitly instead of DistributedTracingData.SerializeToString because
                // in the future we might change DistributedTracingData.SerializeToString to use some other internal format
                // but here we want the string to be in W3C 'traceparent' header format.
                RequestHeadersAdd(request, TraceContext.TraceParentHeaderName, TraceContext.BuildTraceparent(span.OutgoingDistributedTracingData));
            }

            if (transaction is Transaction t)
            {
                if (t.ConfigSnapshot.UseElasticTraceparentHeader)
                {
                    if (!RequestHeadersContain(request, TraceContext.TraceParentHeaderNamePrefixed))
                    {
                        RequestHeadersAdd(request, TraceContext.TraceParentHeaderNamePrefixed,
                                          TraceContext.BuildTraceparent(span.OutgoingDistributedTracingData));
                    }
                }
            }

            if (!RequestHeadersContain(request, TraceContext.TraceStateHeaderName) && span.OutgoingDistributedTracingData != null && span.OutgoingDistributedTracingData.HasTraceState)
            {
                RequestHeadersAdd(request, TraceContext.TraceStateHeaderName, span.OutgoingDistributedTracingData.TraceState.ToTextHeader());
            }

            if (span is Span spanInstance)
            {
                if (!spanInstance.ShouldBeSentToApmServer)
                {
                    return;
                }
            }

            span.Context.Http = new Http {
                Method = RequestGetMethod(request)
            };
            span.Context.Http.SetUrl(requestUrl);
        }
        private void ProcessStartEvent(TRequest request, Uri requestUrl)
        {
            if (_realAgent?.TracerInternal.CurrentSpan is Span currentSpan)
            {
                // if the current span is an exit span, don't create a span for the current request
                if (currentSpan.InstrumentationFlag == InstrumentationFlag.Azure || currentSpan.InstrumentationFlag == InstrumentationFlag.Elasticsearch)
                {
                    return;
                }
            }

            Logger.Trace()?.Log("Processing start event... Request URL: {RequestUrl}", Http.Sanitize(requestUrl));

            var transaction = ApmAgent.Tracer.CurrentTransaction;

            if (transaction == null)
            {
                Logger.Debug()?.Log("No current transaction, skip creating span for outgoing HTTP request");
                return;
            }

            var method = RequestGetMethod(request);

            string HeaderGetter(string header) => RequestTryGetHeader(request, header, out var value) ? value : null;

            ISpan span = null;

            if (_configuration?.HasTracers ?? false)
            {
                using (var httpTracers = _configuration.GetTracers())
                {
                    foreach (var httpSpanTracer in httpTracers)
                    {
                        if (httpSpanTracer.IsMatch(method, requestUrl, HeaderGetter))
                        {
                            span = httpSpanTracer.StartSpan(ApmAgent, method, requestUrl, HeaderGetter);
                            if (span != null)
                            {
                                break;
                            }
                        }
                    }
                }
            }

            if (span is null)
            {
                if (_configuration?.CaptureSpan ?? false)
                {
                    span = ExecutionSegmentCommon.StartSpanOnCurrentExecutionSegment(ApmAgent, $"{method} {requestUrl.Host}",
                                                                                     ApiConstants.TypeExternal, ApiConstants.SubtypeHttp, InstrumentationFlag.HttpClient, true);

                    if (span is null)
                    {
                        Logger.Trace()?.Log("Could not create span for outgoing HTTP request to {RequestUrl}", Http.Sanitize(requestUrl));
                        return;
                    }
                }
                else
                {
                    Logger.Trace()?.Log("Skip creating span for outgoing HTTP request to {RequestUrl} as not to known service", Http.Sanitize(requestUrl));
                    return;
                }
            }

            if (!ProcessingRequests.TryAdd(request, span))
            {
                // Consider improving error reporting - see https://github.com/elastic/apm-agent-dotnet/issues/280
                Logger.Error()?.Log("Failed to add to ProcessingRequests - ???");
                return;
            }

            if (!RequestHeadersContain(request, TraceContext.TraceParentHeaderName))
            {
                // We call TraceParent.BuildTraceparent explicitly instead of DistributedTracingData.SerializeToString because
                // in the future we might change DistributedTracingData.SerializeToString to use some other internal format
                // but here we want the string to be in W3C 'traceparent' header format.
                RequestHeadersAdd(request, TraceContext.TraceParentHeaderName, TraceContext.BuildTraceparent(span.OutgoingDistributedTracingData));
            }

            if (transaction is Transaction t)
            {
                if (t.ConfigSnapshot.UseElasticTraceparentHeader)
                {
                    if (!RequestHeadersContain(request, TraceContext.TraceParentHeaderNamePrefixed))
                    {
                        RequestHeadersAdd(request, TraceContext.TraceParentHeaderNamePrefixed,
                                          TraceContext.BuildTraceparent(span.OutgoingDistributedTracingData));
                    }
                }
            }

            if (!RequestHeadersContain(request, TraceContext.TraceStateHeaderName) && span.OutgoingDistributedTracingData != null && span.OutgoingDistributedTracingData.HasTraceState)
            {
                RequestHeadersAdd(request, TraceContext.TraceStateHeaderName, span.OutgoingDistributedTracingData.TraceState.ToTextHeader());
            }

            if (span is Span realSpan)
            {
                if (!realSpan.ShouldBeSentToApmServer)
                {
                    return;
                }
            }

            span.Context.Http = new Http {
                Method = method
            };
            span.Context.Http.SetUrl(requestUrl);
        }
Пример #3
0
 /// <summary>
 /// Serializes this instance to a string.
 /// This method should be used at the caller side and the return value should be passed to the (possibly remote) callee
 /// side.
 /// <see cref="TryDeserializeFromString" /> should be used to deserialize the instance at the callee side.
 /// </summary>
 /// <returns>
 /// String containing the instance in serialized form.
 /// </returns>
 public string SerializeToString() => TraceContext.BuildTraceparent(this);