public void GlobalSetup() { this.server = TestHttpServer.RunServer( (ctx) => { using (Stream receiveStream = ctx.Request.InputStream) { while (true) { if (receiveStream.Read(this.buffer, 0, this.buffer.Length) == 0) { break; } } } ctx.Response.StatusCode = 200; ctx.Response.OutputStream.Close(); }, out this.serverHost, out this.serverPort); var options = new OtlpExporterOptions { Endpoint = new Uri($"http://{this.serverHost}:{this.serverPort}"), }; this.exporter = new OtlpTraceExporter( options, new OtlpHttpTraceExportClient(options, options.HttpClientFactory())); this.activity = ActivityHelper.CreateTestActivity(); this.activityBatch = new CircularBuffer <Activity>(this.NumberOfSpans); }
public HttpWebRequestActivitySourceTests() { Assert.Null(Activity.Current); Activity.DefaultIdFormat = ActivityIdFormat.W3C; Activity.ForceDefaultIdFormat = false; this.testServer = TestHttpServer.RunServer( ctx => ProcessServerRequest(ctx), out this.testServerHost, out this.testServerPort); this.hostNameAndPort = $"{this.testServerHost}:{this.testServerPort}"; void ProcessServerRequest(HttpListenerContext context) { string redirects = context.Request.QueryString["redirects"]; if (!string.IsNullOrWhiteSpace(redirects) && int.TryParse(redirects, out int parsedRedirects) && parsedRedirects > 0) { context.Response.Redirect(this.BuildRequestUrl(queryString: $"redirects={--parsedRedirects}")); context.Response.OutputStream.Close(); return; } string responseContent; if (context.Request.QueryString["skipRequestContent"] == null) { using StreamReader readStream = new StreamReader(context.Request.InputStream); responseContent = readStream.ReadToEnd(); } else { responseContent = $"{{\"Id\":\"{Guid.NewGuid()}\"}}"; } string responseCode = context.Request.QueryString["responseCode"]; if (!string.IsNullOrWhiteSpace(responseCode)) { context.Response.StatusCode = int.Parse(responseCode); } else { context.Response.StatusCode = 200; } if (context.Response.StatusCode != 204) { using StreamWriter writeStream = new StreamWriter(context.Response.OutputStream); writeStream.Write(responseContent); } else { context.Response.OutputStream.Close(); } } }
public void GlobalSetup() { this.testSpan = this.CreateTestSpan(); this.server = TestHttpServer.RunServer( (ctx) => { ctx.Response.StatusCode = 200; ctx.Response.OutputStream.Close(); }, out this.serverHost, out this.serverPort); }
public HttpClientTests() { this.serverLifeTime = TestHttpServer.RunServer( (ctx) => { ctx.Response.StatusCode = 200; ctx.Response.OutputStream.Close(); }, out var host, out var port); this.url = $"http://{host}:{port}/"; }
public HttpWebRequestTests() { Assert.Null(Activity.Current); Activity.DefaultIdFormat = ActivityIdFormat.W3C; Activity.ForceDefaultIdFormat = false; this.serverLifeTime = TestHttpServer.RunServer( (ctx) => { ctx.Response.StatusCode = 200; ctx.Response.OutputStream.Close(); }, out var host, out var port); this.url = $"http://{host}:{port}/"; }
public void GlobalSetup() { this.serverLifeTime = TestHttpServer.RunServer( (ctx) => { ctx.Response.StatusCode = 200; ctx.Response.OutputStream.Close(); }, out var host, out var port); this.tracerProvider = OpenTelemetrySdk.CreateTracerProviderBuilder() .AddHttpClientInstrumentation() .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(ServiceName)) .AddSource(SourceName) .Build(); this.url = $"http://{host}:{port}/"; this.httpClient = new HttpClient(); this.source = new ActivitySource(SourceName); }
public void GlobalSetup() { this.activity = ActivityHelper.CreateTestActivity(); this.activityBatch = new CircularBuffer <Activity>(this.NumberOfSpans); this.server = TestHttpServer.RunServer( (ctx) => { using (Stream receiveStream = ctx.Request.InputStream) { while (true) { if (receiveStream.Read(this.buffer, 0, this.buffer.Length) == 0) { break; } } } ctx.Response.StatusCode = 200; ctx.Response.OutputStream.Close(); }, out this.serverHost, out this.serverPort); }
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 ZipkinExporterTests() { this.testServer = TestHttpServer.RunServer( ctx => ProcessServerRequest(ctx), out this.testServerHost, out this.testServerPort);
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.EnableOpenTelemetry(b => { b.SetResource(expectedResource); b.AddProcessorPipeline(c => c.AddProcessor(ap => activityProcessor.Object)); b.AddHttpWebRequestDependencyInstrumentation(); }); 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 == "http.flavor") { // http.flavor is optional in .NET Core instrumentation but there is no way to pass that option to the new ActivitySource model so it always shows up here. if (tc.SetHttpFlavor) { Assert.Equal(value, tag.Value); } continue; } 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); 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 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 spanProcessor = new Mock <SpanProcessor>(); var tracer = TracerFactory.Create(b => b .AddProcessorPipeline(p => p.AddProcessor(_ => spanProcessor.Object))) .GetTracer(null); tc.Url = HttpTestData.NormalizeValues(tc.Url, host, port); using (serverLifeTime) using (new HttpClientInstrumentation(tracer, new HttpClientInstrumentationOptions() { SetHttpFlavor = tc.SetHttpFlavor })) { 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(2, spanProcessor.Invocations.Count); // begin and end was called var span = (SpanData)spanProcessor.Invocations[1].Arguments[0]; Assert.Equal(tc.SpanName, span.Name); Assert.Equal(tc.SpanKind, span.Kind.ToString()); var d = new Dictionary <StatusCanonicalCode, string>() { { StatusCanonicalCode.Ok, "OK" }, { StatusCanonicalCode.Cancelled, "CANCELLED" }, { StatusCanonicalCode.Unknown, "UNKNOWN" }, { StatusCanonicalCode.InvalidArgument, "INVALID_ARGUMENT" }, { StatusCanonicalCode.DeadlineExceeded, "DEADLINE_EXCEEDED" }, { StatusCanonicalCode.NotFound, "NOT_FOUND" }, { StatusCanonicalCode.AlreadyExists, "ALREADY_EXISTS" }, { StatusCanonicalCode.PermissionDenied, "PERMISSION_DENIED" }, { StatusCanonicalCode.ResourceExhausted, "RESOURCE_EXHAUSTED" }, { StatusCanonicalCode.FailedPrecondition, "FAILED_PRECONDITION" }, { StatusCanonicalCode.Aborted, "ABORTED" }, { StatusCanonicalCode.OutOfRange, "OUT_OF_RANGE" }, { StatusCanonicalCode.Unimplemented, "UNIMPLEMENTED" }, { StatusCanonicalCode.Internal, "INTERNAL" }, { StatusCanonicalCode.Unavailable, "UNAVAILABLE" }, { StatusCanonicalCode.DataLoss, "DATA_LOSS" }, { StatusCanonicalCode.Unauthenticated, "UNAUTHENTICATED" }, }; Assert.Equal(tc.SpanStatus, d[span.Status.CanonicalCode]); if (tc.SpanStatusHasDescription.HasValue) { Assert.Equal(tc.SpanStatusHasDescription.Value, !string.IsNullOrEmpty(span.Status.Description)); } var normalizedAttributes = span.Attributes.ToDictionary(x => x.Key, x => x.Value.ToString()); tc.SpanAttributes = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, host, port)); Assert.Equal(tc.SpanAttributes, normalizedAttributes); }
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); } }
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 spanProcessor = new Mock <ActivityProcessor>(); tc.Url = HttpTestData.NormalizeValues(tc.Url, host, port); using (serverLifeTime) using (OpenTelemetrySdk.EnableOpenTelemetry( (builder) => builder.AddHttpClientDependencyInstrumentation((opt) => opt.SetHttpFlavor = tc.SetHttpFlavor) .SetResource(expectedResource) .AddProcessorPipeline(p => p.AddProcessor(n => spanProcessor.Object)))) { 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(2, spanProcessor.Invocations.Count); // begin and end was called var span = (Activity)spanProcessor.Invocations[1].Arguments[0]; Assert.Equal(tc.SpanName, span.DisplayName); Assert.Equal(tc.SpanKind, span.Kind.ToString()); 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[span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.StatusCodeKey).Value]); if (tc.SpanStatusHasDescription.HasValue) { var desc = span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.StatusDescriptionKey).Value; Assert.Equal(tc.SpanStatusHasDescription.Value, !string.IsNullOrEmpty(desc)); } var normalizedAttributes = span.Tags.Where(kv => !kv.Key.StartsWith("ot")).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) { // TODO: Fix this test. This is mostly broken because Status is stored in tags. // Assert.Contains(span.Tags, i => i.Key == kv.Key && i.Value.Equals(kv.Value, StringComparison.InvariantCultureIgnoreCase)); } Assert.Equal(expectedResource, span.GetResource()); }