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); }
/// <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);