예제 #1
0
        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);
 }
예제 #4
0
        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);
예제 #10
0
        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());
        }
예제 #11
0
        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")));
            }
        }
예제 #12
0
        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);
            }
        }
예제 #13
0
        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);
            }
        }
예제 #15
0
        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());
        }