public async Task HttpOutCallsAreCollectedSuccessfullyAsync(HttpTestData.HttpOutTestCase tc) { var serverLifeTime = TestHttpServer.RunServer( (ctx) => { ctx.Response.StatusCode = tc.ResponseCode == 0 ? 200 : tc.ResponseCode; ctx.Response.OutputStream.Close(); }, out var host, out var port); var expectedResource = Resources.Resources.CreateServiceResource("test-service"); var processor = new Mock <BaseProcessor <Activity> >(); tc.Url = HttpTestData.NormalizeValues(tc.Url, host, port); using (serverLifeTime) using (Sdk.CreateTracerProviderBuilder() .AddHttpClientInstrumentation((opt) => { opt.SetHttpFlavor = tc.SetHttpFlavor; opt.Enrich = ActivityEnrichment; }) .AddProcessor(processor.Object) .SetResource(expectedResource) .Build()) { try { using var c = new HttpClient(); var request = new HttpRequestMessage { RequestUri = new Uri(tc.Url), Method = new HttpMethod(tc.Method), Version = new Version(2, 0), }; if (tc.Headers != null) { foreach (var header in tc.Headers) { request.Headers.Add(header.Key, header.Value); } } await c.SendAsync(request); } catch (Exception) { // test case can intentionally send request that will result in exception } } Assert.Equal(4, processor.Invocations.Count); // OnStart/OnEnd/OnShutdown/Dispose called. var activity = (Activity)processor.Invocations[1].Arguments[0]; Assert.Equal(ActivityKind.Client, activity.Kind); Assert.Equal(tc.SpanName, activity.DisplayName); var d = new Dictionary <string, string>() { { "Ok", "OK" }, { "Cancelled", "CANCELLED" }, { "Unknown", "UNKNOWN" }, { "InvalidArgument", "INVALID_ARGUMENT" }, { "DeadlineExceeded", "DEADLINE_EXCEEDED" }, { "NotFound", "NOT_FOUND" }, { "AlreadyExists", "ALREADY_EXISTS" }, { "PermissionDenied", "PERMISSION_DENIED" }, { "ResourceExhausted", "RESOURCE_EXHAUSTED" }, { "FailedPrecondition", "FAILED_PRECONDITION" }, { "Aborted", "ABORTED" }, { "OutOfRange", "OUT_OF_RANGE" }, { "Unimplemented", "UNIMPLEMENTED" }, { "Internal", "INTERNAL" }, { "Unavailable", "UNAVAILABLE" }, { "DataLoss", "DATA_LOSS" }, { "Unauthenticated", "UNAUTHENTICATED" }, }; // Assert.Equal(tc.SpanStatus, d[span.Status.CanonicalCode]); Assert.Equal( tc.SpanStatus, d[activity.GetTagValue(SpanAttributeConstants.StatusCodeKey) as string]); if (tc.SpanStatusHasDescription.HasValue) { var desc = activity.GetTagValue(SpanAttributeConstants.StatusDescriptionKey) as string; Assert.Equal(tc.SpanStatusHasDescription.Value, !string.IsNullOrEmpty(desc)); } var normalizedAttributes = activity.TagObjects.Where(kv => !kv.Key.StartsWith("otel.")).ToImmutableSortedDictionary(x => x.Key, x => x.Value.ToString()); var normalizedAttributesTestCase = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, host, port)); Assert.Equal(normalizedAttributesTestCase.Count, normalizedAttributes.Count); foreach (var kv in normalizedAttributesTestCase) { Assert.Contains(activity.TagObjects, i => i.Key == kv.Key && i.Value.ToString().Equals(kv.Value, StringComparison.InvariantCultureIgnoreCase)); } Assert.Equal(expectedResource, activity.GetResource()); }
public async Task HttpOutCallsAreCollectedSuccessfullyAsync(HttpTestData.HttpOutTestCase tc) { var serverLifeTime = TestHttpServer.RunServer( (ctx) => { ctx.Response.StatusCode = tc.ResponseCode == 0 ? 200 : tc.ResponseCode; ctx.Response.OutputStream.Close(); }, out var host, out var port); var processor = new Mock <BaseProcessor <Activity> >(); tc.Url = HttpTestData.NormalizeValues(tc.Url, host, port); using (serverLifeTime) using (Sdk.CreateTracerProviderBuilder() .AddHttpClientInstrumentation((opt) => { opt.SetHttpFlavor = tc.SetHttpFlavor; opt.Enrich = ActivityEnrichment; opt.RecordException = tc.RecordException.HasValue ? tc.RecordException.Value : false; }) .AddProcessor(processor.Object) .Build()) { try { using var c = new HttpClient(); var request = new HttpRequestMessage { RequestUri = new Uri(tc.Url), Method = new HttpMethod(tc.Method), Version = new Version(2, 0), }; if (tc.Headers != null) { foreach (var header in tc.Headers) { request.Headers.Add(header.Key, header.Value); } } await c.SendAsync(request); } catch (Exception) { // test case can intentionally send request that will result in exception } } Assert.Equal(5, processor.Invocations.Count); // SetParentProvider/OnStart/OnEnd/OnShutdown/Dispose called. var activity = (Activity)processor.Invocations[2].Arguments[0]; Assert.Equal(ActivityKind.Client, activity.Kind); Assert.Equal(tc.SpanName, activity.DisplayName); // Assert.Equal(tc.SpanStatus, d[span.Status.CanonicalCode]); Assert.Equal( tc.SpanStatus, activity.GetTagValue(SpanAttributeConstants.StatusCodeKey) as string); if (tc.SpanStatusHasDescription.HasValue) { var desc = activity.GetTagValue(SpanAttributeConstants.StatusDescriptionKey) as string; Assert.Equal(tc.SpanStatusHasDescription.Value, !string.IsNullOrEmpty(desc)); } var normalizedAttributes = activity.TagObjects.Where(kv => !kv.Key.StartsWith("otel.")).ToImmutableSortedDictionary(x => x.Key, x => x.Value.ToString()); var normalizedAttributesTestCase = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, host, port)); Assert.Equal(normalizedAttributesTestCase.Count, normalizedAttributes.Count); foreach (var kv in normalizedAttributesTestCase) { Assert.Contains(activity.TagObjects, i => i.Key == kv.Key && i.Value.ToString().Equals(kv.Value, StringComparison.InvariantCultureIgnoreCase)); } if (tc.RecordException.HasValue && tc.RecordException.Value) { Assert.Single(activity.Events.Where(evt => evt.Name.Equals("exception"))); } }
public void HttpOutCallsAreCollectedSuccessfully(HttpTestData.HttpOutTestCase tc) { using var serverLifeTime = TestHttpServer.RunServer( (ctx) => { ctx.Response.StatusCode = tc.ResponseCode == 0 ? 200 : tc.ResponseCode; ctx.Response.OutputStream.Close(); }, out var host, out var port); var activityProcessor = new Mock <BaseProcessor <Activity> >(); using var shutdownSignal = Sdk.CreateTracerProviderBuilder() .AddProcessor(activityProcessor.Object) .AddHttpWebRequestInstrumentation(options => { options.SetHttpFlavor = tc.SetHttpFlavor; options.Enrich = ActivityEnrichment; }) .Build(); tc.Url = HttpTestData.NormalizeValues(tc.Url, host, port); try { var request = (HttpWebRequest)WebRequest.Create(tc.Url); request.Method = tc.Method; if (tc.Headers != null) { foreach (var header in tc.Headers) { request.Headers.Add(header.Key, header.Value); } } request.ContentLength = 0; using var response = (HttpWebResponse)request.GetResponse(); new StreamReader(response.GetResponseStream()).ReadToEnd(); } catch (Exception) { // test case can intentionally send request that will result in exception tc.ResponseExpected = false; } Assert.Equal(3, activityProcessor.Invocations.Count); // SetParentProvider/Begin/End called var activity = (Activity)activityProcessor.Invocations[2].Arguments[0]; ValidateHttpWebRequestActivity(activity); Assert.Equal(tc.SpanName, activity.DisplayName); tc.SpanAttributes = tc.SpanAttributes.ToDictionary( x => x.Key, x => { if (x.Key == "http.flavor" && x.Value == "2.0") { return("1.1"); } return(HttpTestData.NormalizeValues(x.Value, host, port)); }); foreach (KeyValuePair <string, object> tag in activity.TagObjects) { var tagValue = tag.Value.ToString(); if (!tc.SpanAttributes.TryGetValue(tag.Key, out string value)) { if (tag.Key == SpanAttributeConstants.StatusCodeKey) { Assert.Equal(tc.SpanStatus, tagValue); continue; } if (tag.Key == SpanAttributeConstants.StatusDescriptionKey) { if (tc.SpanStatusHasDescription.HasValue) { Assert.Equal(tc.SpanStatusHasDescription.Value, !string.IsNullOrEmpty(tagValue)); } continue; } Assert.True(false, $"Tag {tag.Key} was not found in test data."); } Assert.Equal(value, tagValue); } }
public void HttpOutCallsAreCollectedSuccessfullyAsync(HttpTestData.HttpOutTestCase tc) { using var serverLifeTime = TestHttpServer.RunServer( (ctx) => { ctx.Response.StatusCode = tc.ResponseCode == 0 ? 200 : tc.ResponseCode; ctx.Response.OutputStream.Close(); }, out var host, out var port); var expectedResource = Resources.Resources.CreateServiceResource("test-service"); var activityProcessor = new Mock <ActivityProcessor>(); using var shutdownSignal = OpenTelemetrySdk.CreateTracerProvider(b => { b.SetResource(expectedResource); b.AddProcessorPipeline(c => c.AddProcessor(ap => activityProcessor.Object)); b.AddHttpWebRequestInstrumentation(options => options.SetHttpFlavor = tc.SetHttpFlavor); }); tc.Url = HttpTestData.NormalizeValues(tc.Url, host, port); try { var request = (HttpWebRequest)WebRequest.Create(tc.Url); request.Method = tc.Method; if (tc.Headers != null) { foreach (var header in tc.Headers) { request.Headers.Add(header.Key, header.Value); } } request.ContentLength = 0; using var response = (HttpWebResponse)request.GetResponse(); new StreamReader(response.GetResponseStream()).ReadToEnd(); } catch (Exception) { // test case can intentionally send request that will result in exception } Assert.Equal(2, activityProcessor.Invocations.Count); // begin and end was called var activity = (Activity)activityProcessor.Invocations[1].Arguments[0]; Assert.Equal(tc.SpanName, activity.DisplayName); Assert.Equal(tc.SpanKind, activity.Kind.ToString()); var d = new Dictionary <string, string>() { { StatusCanonicalCode.Ok.ToString(), "OK" }, { StatusCanonicalCode.Cancelled.ToString(), "CANCELLED" }, { StatusCanonicalCode.Unknown.ToString(), "UNKNOWN" }, { StatusCanonicalCode.InvalidArgument.ToString(), "INVALID_ARGUMENT" }, { StatusCanonicalCode.DeadlineExceeded.ToString(), "DEADLINE_EXCEEDED" }, { StatusCanonicalCode.NotFound.ToString(), "NOT_FOUND" }, { StatusCanonicalCode.AlreadyExists.ToString(), "ALREADY_EXISTS" }, { StatusCanonicalCode.PermissionDenied.ToString(), "PERMISSION_DENIED" }, { StatusCanonicalCode.ResourceExhausted.ToString(), "RESOURCE_EXHAUSTED" }, { StatusCanonicalCode.FailedPrecondition.ToString(), "FAILED_PRECONDITION" }, { StatusCanonicalCode.Aborted.ToString(), "ABORTED" }, { StatusCanonicalCode.OutOfRange.ToString(), "OUT_OF_RANGE" }, { StatusCanonicalCode.Unimplemented.ToString(), "UNIMPLEMENTED" }, { StatusCanonicalCode.Internal.ToString(), "INTERNAL" }, { StatusCanonicalCode.Unavailable.ToString(), "UNAVAILABLE" }, { StatusCanonicalCode.DataLoss.ToString(), "DATA_LOSS" }, { StatusCanonicalCode.Unauthenticated.ToString(), "UNAUTHENTICATED" }, }; tc.SpanAttributes = tc.SpanAttributes.ToDictionary( x => x.Key, x => { if (x.Key == "http.flavor" && x.Value == "2.0") { return("1.1"); } return(HttpTestData.NormalizeValues(x.Value, host, port)); }); foreach (KeyValuePair <string, string> tag in activity.Tags) { if (!tc.SpanAttributes.TryGetValue(tag.Key, out string value)) { if (tag.Key == SpanAttributeConstants.StatusCodeKey) { Assert.Equal(tc.SpanStatus, d[tag.Value]); continue; } if (tag.Key == SpanAttributeConstants.StatusDescriptionKey) { if (tc.SpanStatusHasDescription.HasValue) { Assert.Equal(tc.SpanStatusHasDescription.Value, !string.IsNullOrEmpty(tag.Value)); } continue; } Assert.True(false, $"Tag {tag.Key} was not found in test data."); } Assert.Equal(value, tag.Value); } Assert.Equal(expectedResource, activity.GetResource()); }
public async Task HttpOutCallsAreCollectedSuccessfullyAsync(HttpTestData.HttpOutTestCase tc) { var serverLifeTime = TestHttpServer.RunServer( (ctx) => { ctx.Response.StatusCode = tc.ResponseCode == 0 ? 200 : tc.ResponseCode; ctx.Response.OutputStream.Close(); }, out var host, out var port); var processor = new Mock <BaseProcessor <Activity> >(); tc.Url = HttpTestData.NormalizeValues(tc.Url, host, port); var metricItems = new List <Metric>(); var metricExporter = new InMemoryExporter <Metric>(metricItems); var metricReader = new BaseExportingMetricReader(metricExporter) { Temporality = AggregationTemporality.Cumulative, }; var meterProvider = Sdk.CreateMeterProviderBuilder() .AddHttpClientInstrumentation() .AddReader(metricReader) .Build(); using (serverLifeTime) using (Sdk.CreateTracerProviderBuilder() .AddHttpClientInstrumentation((opt) => { opt.SetHttpFlavor = tc.SetHttpFlavor; opt.Enrich = ActivityEnrichment; opt.RecordException = tc.RecordException.HasValue ? tc.RecordException.Value : false; }) .AddProcessor(processor.Object) .Build()) { try { using var c = new HttpClient(); var request = new HttpRequestMessage { RequestUri = new Uri(tc.Url), Method = new HttpMethod(tc.Method), Version = new Version(2, 0), }; if (tc.Headers != null) { foreach (var header in tc.Headers) { request.Headers.Add(header.Key, header.Value); } } await c.SendAsync(request); } catch (Exception) { // test case can intentionally send request that will result in exception } } meterProvider.Dispose(); var requestMetrics = metricItems .Where(metric => metric.Name == "http.client.duration") .ToArray(); Assert.Equal(5, processor.Invocations.Count); // SetParentProvider/OnStart/OnEnd/OnShutdown/Dispose called. var activity = (Activity)processor.Invocations[2].Arguments[0]; Assert.Equal(ActivityKind.Client, activity.Kind); Assert.Equal(tc.SpanName, activity.DisplayName); // Assert.Equal(tc.SpanStatus, d[span.Status.CanonicalCode]); Assert.Equal( tc.SpanStatus, activity.GetTagValue(SpanAttributeConstants.StatusCodeKey) as string); if (tc.SpanStatusHasDescription.HasValue) { var desc = activity.GetTagValue(SpanAttributeConstants.StatusDescriptionKey) as string; Assert.Equal(tc.SpanStatusHasDescription.Value, !string.IsNullOrEmpty(desc)); } var normalizedAttributes = activity.TagObjects.Where(kv => !kv.Key.StartsWith("otel.")).ToImmutableSortedDictionary(x => x.Key, x => x.Value.ToString()); var normalizedAttributesTestCase = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, host, port)); Assert.Equal(normalizedAttributesTestCase.Count, normalizedAttributes.Count); foreach (var kv in normalizedAttributesTestCase) { Assert.Contains(activity.TagObjects, i => i.Key == kv.Key && i.Value.ToString().Equals(kv.Value, StringComparison.InvariantCultureIgnoreCase)); } if (tc.RecordException.HasValue && tc.RecordException.Value) { Assert.Single(activity.Events.Where(evt => evt.Name.Equals("exception"))); } if (tc.ResponseExpected) { Assert.Single(requestMetrics); var metric = requestMetrics[0]; Assert.NotNull(metric); Assert.True(metric.MetricType == MetricType.Histogram); var metricPoints = new List <MetricPoint>(); foreach (var p in metric.GetMetricPoints()) { metricPoints.Add(p); } Assert.Single(metricPoints); var metricPoint = metricPoints[0]; var count = metricPoint.GetHistogramCount(); var sum = metricPoint.GetHistogramSum(); Assert.Equal(1L, count); Assert.Equal(activity.Duration.TotalMilliseconds, sum); var attributes = new KeyValuePair <string, object> [metricPoint.Tags.Count]; int i = 0; foreach (var tag in metricPoint.Tags) { attributes[i++] = tag; } var method = new KeyValuePair <string, object>(SemanticConventions.AttributeHttpMethod, tc.Method); var scheme = new KeyValuePair <string, object>(SemanticConventions.AttributeHttpScheme, "http"); var statusCode = new KeyValuePair <string, object>(SemanticConventions.AttributeHttpStatusCode, tc.ResponseCode == 0 ? 200 : tc.ResponseCode); var flavor = new KeyValuePair <string, object>(SemanticConventions.AttributeHttpFlavor, "2.0"); Assert.Contains(method, attributes); Assert.Contains(scheme, attributes); Assert.Contains(statusCode, attributes); Assert.Contains(flavor, attributes); Assert.Equal(4, attributes.Length); } else { Assert.Empty(requestMetrics); } }