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 DebugIndividualTestAsync()
        {
            var serializer = new JsonSerializer();
            var input      = serializer.Deserialize <HttpTestData.HttpOutTestCase[]>(new JsonTextReader(new StringReader(@"
[
  {
    ""name"": ""Response code: 399"",
    ""method"": ""GET"",
    ""url"": ""http://{host}:{port}/"",
    ""responseCode"": 399,
    ""spanName"": ""HTTP GET"",
    ""spanStatus"": ""OK"",
    ""spanKind"": ""Client"",
    ""spanAttributes"": {
      ""http.method"": ""GET"",
      ""http.host"": ""{host}:{port}"",
      ""http.status_code"": ""399"",
      ""http.url"": ""http://{host}:{port}/""
    }
  }
]
")));

            var   t = (Task)this.GetType().InvokeMember(nameof(this.HttpOutCallsAreCollectedSuccessfullyAsync), BindingFlags.InvokeMethod, null, this, HttpTestData.GetArgumentsFromTestCaseObject(input).First());
            await t;
        }
Example #3
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")));
            }
        }
Example #4
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.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());
        }
Example #5
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);
            }
        }
        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);
            }
        }