Don't throw when checking if an outbound HttpRequestMessage request is injected with the traceparent header (#869)
* If an outbound HttpRequestMessage does not include the traceparent header then don't throw. Made the header values getters and setter static to avoid the Func and Action allocations per invocation. * Missed a getter usage in HttpInListener * Extending an existing unit test to validate the new HttpRequestMessage header value getter. Co-authored-by: Cijo Thomas <cithomas@microsoft.com>
This commit is contained in:
parent
609c57c31f
commit
c1dedf0589
|
|
@ -15,6 +15,7 @@
|
|||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Web;
|
||||
using System.Web.Routing;
|
||||
|
|
@ -26,6 +27,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
|
|||
internal class HttpInListener : ListenerHandler
|
||||
{
|
||||
private static readonly string ActivityNameByHttpInListener = "ActivityCreatedByHttpInListener";
|
||||
private static readonly Func<HttpRequest, string, IEnumerable<string>> HttpRequestHeaderValuesGetter = (request, name) => request.Headers.GetValues(name);
|
||||
private readonly PropertyFetcher routeFetcher = new PropertyFetcher("Route");
|
||||
private readonly PropertyFetcher routeTemplateFetcher = new PropertyFetcher("RouteTemplate");
|
||||
private readonly AspNetInstrumentationOptions options;
|
||||
|
|
@ -61,9 +63,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
|
|||
{
|
||||
// This requires to ignore the current activity and create a new one
|
||||
// using the context extracted using the format TextFormat supports.
|
||||
var ctx = this.options.TextFormat.Extract<HttpRequest>(
|
||||
request,
|
||||
(r, name) => requestValues.Headers.GetValues(name));
|
||||
var ctx = this.options.TextFormat.Extract(request, HttpRequestHeaderValuesGetter);
|
||||
|
||||
// Create a new activity with its parent set from the extracted context.
|
||||
// This makes the new activity as a "sibling" of the activity created by
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
|
@ -29,6 +30,7 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
|
|||
{
|
||||
private static readonly string UnknownHostName = "UNKNOWN-HOST";
|
||||
private static readonly string ActivityNameByHttpInListener = "ActivityCreatedByHttpInListener";
|
||||
private static readonly Func<HttpRequest, string, IEnumerable<string>> HttpRequestHeaderValuesGetter = (request, name) => request.Headers[name];
|
||||
private readonly PropertyFetcher startContextFetcher = new PropertyFetcher("HttpContext");
|
||||
private readonly PropertyFetcher stopContextFetcher = new PropertyFetcher("HttpContext");
|
||||
private readonly PropertyFetcher beforeActionActionDescriptorFetcher = new PropertyFetcher("actionDescriptor");
|
||||
|
|
@ -70,9 +72,7 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
|
|||
// using the context extracted from w3ctraceparent header or
|
||||
// using the format TextFormat supports.
|
||||
|
||||
var ctx = this.options.TextFormat.Extract<HttpRequest>(
|
||||
request,
|
||||
(r, name) => r.Headers[name]);
|
||||
var ctx = this.options.TextFormat.Extract(request, HttpRequestHeaderValuesGetter);
|
||||
|
||||
// Create a new activity with its parent set from the extracted context.
|
||||
// This makes the new activity as a "sibling" of the activity created by
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
// limitations under the License.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using System.Net.Sockets;
|
||||
|
|
@ -28,6 +29,18 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
{
|
||||
internal class HttpHandlerDiagnosticListener : ListenerHandler
|
||||
{
|
||||
private static readonly Func<HttpRequestMessage, string, IEnumerable<string>> HttpRequestMessageHeaderValuesGetter = (request, name) =>
|
||||
{
|
||||
if (request.Headers.TryGetValues(name, out var values))
|
||||
{
|
||||
return values;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
private static readonly Action<HttpRequestMessage, string, string> HttpRequestMessageHeaderValueSetter = (request, name, value) => request.Headers.Add(name, value);
|
||||
|
||||
private static readonly Regex CoreAppMajorVersionCheckRegex = new Regex("^\\.NETCoreApp,Version=v(\\d+)\\.", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private readonly ActivitySourceAdapter activitySource;
|
||||
|
|
@ -68,7 +81,7 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
return;
|
||||
}
|
||||
|
||||
if (this.options.TextFormat.IsInjected(request, (r, h) => r.Headers.GetValues(h)))
|
||||
if (this.options.TextFormat.IsInjected(request, HttpRequestMessageHeaderValuesGetter))
|
||||
{
|
||||
// this request is already instrumented, we should back off
|
||||
activity.IsAllDataRequested = false;
|
||||
|
|
@ -94,7 +107,7 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
|
||||
if (!(this.httpClientSupportsW3C && this.options.TextFormat is TraceContextFormatActivity))
|
||||
{
|
||||
this.options.TextFormat.Inject(activity.Context, request, (r, k, v) => r.Headers.Add(k, v));
|
||||
this.options.TextFormat.Inject(activity.Context, request, HttpRequestMessageHeaderValueSetter);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
private static readonly Func<string, string> ConvertMethodToOperationNameRef = ConvertMethodToOperationName;
|
||||
private static readonly Func<HttpMethod, string> ConvertHttpMethodToOperationNameRef = ConvertHttpMethodToOperationName;
|
||||
private static readonly Func<HttpMethod, string> ConvertHttpMethodToNameRef = ConvertHttpMethodToName;
|
||||
private static readonly Func<Version, string> ConvertConvertProtcolVersionToStringRef = ConvertProtcolVersionToString;
|
||||
private static readonly Func<Version, string> ConvertProtocolVersionToStringRef = ConvertProtocolVersionToString;
|
||||
private static readonly Func<HttpStatusCode, string> ConvertHttpStatusCodeToStringRef = ConvertHttpStatusCodeToString;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -64,7 +64,7 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
/// </summary>
|
||||
/// <param name="protocolVersion"><see cref="Version"/>.</param>
|
||||
/// <returns>Span flavor value.</returns>
|
||||
public static string GetFlavorTagValueFromProtocolVersion(Version protocolVersion) => ProtocolVersionToStringCache.GetOrAdd(protocolVersion, ConvertConvertProtcolVersionToStringRef);
|
||||
public static string GetFlavorTagValueFromProtocolVersion(Version protocolVersion) => ProtocolVersionToStringCache.GetOrAdd(protocolVersion, ConvertProtocolVersionToStringRef);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the OpenTelemetry standard status code tag value for a span based on its protocol <see cref="HttpStatusCode"/>.
|
||||
|
|
@ -112,6 +112,6 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
|
||||
private static string ConvertHttpStatusCodeToString(HttpStatusCode statusCode) => ((int)statusCode).ToString();
|
||||
|
||||
private static string ConvertProtcolVersionToString(Version protocolVersion) => protocolVersion.ToString();
|
||||
private static string ConvertProtocolVersionToString(Version protocolVersion) => protocolVersion.ToString();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ using System;
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
|
@ -39,6 +40,9 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
internal const string ActivitySourceName = "OpenTelemetry.HttpWebRequest";
|
||||
internal const string ActivityName = ActivitySourceName + ".HttpRequestOut";
|
||||
|
||||
internal static readonly Func<HttpWebRequest, string, IEnumerable<string>> HttpWebRequestHeaderValuesGetter = (request, name) => request.Headers.GetValues(name);
|
||||
internal static readonly Action<HttpWebRequest, string, string> HttpWebRequestHeaderValuesSetter = (request, name, value) => request.Headers.Add(name, value);
|
||||
|
||||
internal static HttpWebRequestInstrumentationOptions Options = new HttpWebRequestInstrumentationOptions();
|
||||
|
||||
private const string CorrelationContextHeaderName = "Correlation-Context";
|
||||
|
|
@ -186,7 +190,7 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void InstrumentRequest(HttpWebRequest request, Activity activity)
|
||||
{
|
||||
Options.TextFormat.Inject(activity.Context, request, (r, k, v) => r.Headers.Add(k, v));
|
||||
Options.TextFormat.Inject(activity.Context, request, HttpWebRequestHeaderValuesSetter);
|
||||
|
||||
if (request.Headers.Get(CorrelationContextHeaderName) == null)
|
||||
{
|
||||
|
|
@ -210,7 +214,7 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsRequestInstrumented(HttpWebRequest request)
|
||||
=> Options.TextFormat.IsInjected(request, (r, h) => r.Headers.GetValues(h));
|
||||
=> Options.TextFormat.IsInjected(request, HttpWebRequestHeaderValuesGetter);
|
||||
|
||||
private static void ProcessRequest(HttpWebRequest request)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
// </copyright>
|
||||
#if NETCOREAPP3_1
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
|
|
@ -23,7 +24,6 @@ using System.Threading.Tasks;
|
|||
using Moq;
|
||||
using OpenTelemetry.Context.Propagation;
|
||||
using OpenTelemetry.Internal.Test;
|
||||
using OpenTelemetry.Trace;
|
||||
using OpenTelemetry.Trace.Configuration;
|
||||
using OpenTelemetry.Trace.Export;
|
||||
using Xunit;
|
||||
|
|
@ -72,8 +72,27 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Tests
|
|||
parent.TraceStateString = "k1=v1,k2=v2";
|
||||
parent.ActivityTraceFlags = ActivityTraceFlags.Recorded;
|
||||
|
||||
// Ensure that the header value func does not throw if the header key can't be found
|
||||
var mockTextFormat = new Mock<ITextFormatActivity>();
|
||||
var isInjectedHeaderValueGetterThrows = false;
|
||||
mockTextFormat
|
||||
.Setup(x => x.IsInjected(It.IsAny<HttpRequestMessage>(), It.IsAny<Func<HttpRequestMessage, string, IEnumerable<string>>>()))
|
||||
.Callback<HttpRequestMessage, Func<HttpRequestMessage, string, IEnumerable<string>>>(
|
||||
(carrier, getter) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// traceparent doesn't exist
|
||||
getter(carrier, "traceparent");
|
||||
}
|
||||
catch
|
||||
{
|
||||
isInjectedHeaderValueGetterThrows = true;
|
||||
}
|
||||
});
|
||||
|
||||
using (OpenTelemetrySdk.EnableOpenTelemetry(
|
||||
(builder) => builder.AddHttpClientDependencyInstrumentation()
|
||||
(builder) => builder.AddHttpClientDependencyInstrumentation(o => o.TextFormat = mockTextFormat.Object)
|
||||
.AddProcessorPipeline(p => p.AddProcessor(n => spanProcessor.Object))))
|
||||
{
|
||||
using var c = new HttpClient();
|
||||
|
|
@ -94,7 +113,10 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Tests
|
|||
Assert.Single(tracestates);
|
||||
|
||||
Assert.Equal($"00-{span.Context.TraceId}-{span.Context.SpanId}-01", traceparents.Single());
|
||||
Assert.Equal("k1=v1,k2=v2", tracestates.Single());
|
||||
Assert.Equal("k1=v1,k2=v2", tracestates.Single());
|
||||
|
||||
mockTextFormat.Verify(x => x.IsInjected(It.IsAny<HttpRequestMessage>(), It.IsAny<Func<HttpRequestMessage, string, IEnumerable<string>>>()), Times.Once);
|
||||
Assert.False(isInjectedHeaderValueGetterThrows);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Reference in New Issue