Beispiel #1
0
        public void SubmitsTraces()
        {
            // In .NET Framework, the MySQL client injects
            // a few extra queries the first time it connects to a database
            int          expectedSpanCount     = EnvironmentHelper.IsCoreClr() ? 21 : 24;
            const string dbType                = "mysql";
            const string expectedOperationName = dbType + ".query";
            const string expectedServiceName   = "Samples.MySql";

            int agentPort = TcpPortProvider.GetOpenPort();

            using (var agent = new MockZipkinCollector(agentPort))
                using (ProcessResult processResult = RunSampleAndWaitForExit(agent.Port, envVars: ZipkinEnvVars))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode}");

                    var spans = agent.WaitForSpans(expectedSpanCount, operationName: expectedOperationName);
                    Assert.Equal(expectedSpanCount, spans.Count);

                    foreach (var span in spans)
                    {
                        Assert.Equal(expectedOperationName, span.Name);
                        Assert.Equal(expectedServiceName, span.Service);
                        Assert.Null(span.Type);
                        Assert.Equal(dbType, span.Tags[Tags.DbType]);
                        Assert.NotNull(span.Tags[Tags.DbStatement]);
                    }
                }
        }
Beispiel #2
0
        public void SubmitsTraces(string packageVersion)
        {
            int agentPort = TcpPortProvider.GetOpenPort();

            using (var agent = new MockZipkinCollector(agentPort))
                using (var processResult = RunSampleAndWaitForExit(agent.Port, packageVersion: packageVersion, envVars: ZipkinEnvVars))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode} and exception: {processResult.StandardError}");

                    var spans = agent.WaitForSpans(1, 500);
                    Assert.True(spans.Count >= 1, $"Expecting at least 1 spans, only received {spans.Count}");

                    var span = (MockZipkinCollector.Span)spans[0];
                    Assert.Equal("MySpan", span.Name);
                    Assert.Equal("Samples.OpenTracing", span.Service);
                    Assert.Null(span.Type);

                    span.Tags.TryGetValue("MyTag", out string spanValue);
                    Assert.Equal("MyValue", spanValue);

                    var logs = span.Logs.Values;
                    Assert.Single(logs);
                    Assert.Equal("My Log Statement", logs.First()["event"]);
                }
        }
Beispiel #3
0
        public void SubmitsTraces(string packageVersion)
        {
            const int    expectedSpanCount     = 35;
            const string dbType                = "sql-server";
            const string expectedOperationName = dbType + ".query";
            const string expectedServiceName   = "Samples.SqlServer";

            int agentPort = TcpPortProvider.GetOpenPort();

            using (var agent = new MockZipkinCollector(agentPort))
                using (ProcessResult processResult = RunSampleAndWaitForExit(agent.Port, packageVersion: packageVersion, envVars: ZipkinEnvVars))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode}");

                    var spans = agent.WaitForSpans(expectedSpanCount, operationName: expectedOperationName);
                    Assert.Equal(expectedSpanCount, spans.Count);

                    foreach (var span in spans)
                    {
                        Assert.Equal(expectedOperationName, span.Name);
                        Assert.Equal(expectedServiceName, span.Service);
                        Assert.Null(span.Type);
                        Assert.Equal(dbType, span.Tags[Tags.DbType]);
                        Assert.NotNull(span.Tags[Tags.DbStatement]);
                    }
                }
        }
        public void SubmitTraces()
        {
            var agentPort = TcpPortProvider.GetOpenPort();

            using var agent = new MockZipkinCollector(Output, agentPort);

            const int expectedSpanCount = 8;

            using var processResult = RunTestApplicationAndWaitForExit(agent.Port, arguments: $"{_sqlClientFixture.Password} {_sqlClientFixture.Port}", enableStartupHook: true);
            Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode} and exception: {processResult.StandardError}");
            var spans = agent.WaitForSpans(expectedSpanCount, TimeSpan.FromSeconds(5));

            using (new AssertionScope())
            {
                spans.Count.Should().Be(expectedSpanCount);

                foreach (var span in spans)
                {
                    span.Service.Should().Be(ServiceName);
                    span.Name.Should().Be("master");
                    span.Tags["db.system"].Should().Be("mssql");
                    span.Tags["db.name"].Should().Be("master");
                    span.Tags["peer.service"].Should().Contain($"{_sqlClientFixture.Port}");
                    span.Tags["db.statement_type"].Should().Be("Text");
                    span.Tags["span.kind"].Should().Be("client");
                }
            }
        }
Beispiel #5
0
        public async void SubmitsOutOfOrderSpans()
        {
            using (var agent = new MockZipkinCollector(collectorPort))
            {
                var scope1 = _tracer.StartActive("op1");
                var scope2 = _tracer.StartActive("op2");
                scope1.Close();
                scope2.Close();

                await _httpRecorder.WaitForCompletion(1);

                Assert.Single(_httpRecorder.Requests);
                Assert.Single(_httpRecorder.Responses);
                Assert.All(_httpRecorder.Responses, (x) => Assert.Equal(HttpStatusCode.OK, x.StatusCode));

                var trace = _httpRecorder.ZipkinTraces;
                ZipkinHelpers.AssertSpanEqual(scope1.Span, trace[0][0]);
                ZipkinHelpers.AssertSpanEqual(scope2.Span, trace[0][1]);

                // Check root span for mandatory tags.
                Assert.Contains(scope1.Span.Tags, kvp => kvp.Key == Tags.Language && kvp.Value == TracerConstants.Language);
                Assert.Contains(scope1.Span.Tags, kvp => kvp.Key == Tags.Version && kvp.Value == TracerConstants.AssemblyVersion);

                // Child spans should not have root spans tags.
                Assert.Null(scope2.Span.Tags);
            }
        }
Beispiel #6
0
    public async Task SubmitsTraces()
    {
        var agentPort = TcpPortProvider.GetOpenPort();
        var webPort   = TcpPortProvider.GetOpenPort();

        using (var fwPort = FirewallHelper.OpenWinPort(agentPort, Output))
            using (var agent = new MockZipkinCollector(Output, agentPort))
                using (var container = await StartContainerAsync(agentPort, webPort))
                {
                    var client = new HttpClient();

                    var response = await client.GetAsync($"http://localhost:{webPort}");

                    var content = await response.Content.ReadAsStringAsync();

                    Output.WriteLine("Sample response:");
                    Output.WriteLine(content);

                    agent.SpanFilters.Add(x => x.Name != "healthz");

                    var spans = agent.WaitForSpans(1);

                    Assert.True(spans.Count >= 1, $"Expecting at least 1 span, only received {spans.Count}");
                }
    }
Beispiel #7
0
        public void WebClient()
        {
            int agentPort = TcpPortProvider.GetOpenPort();
            int httpPort  = TcpPortProvider.GetOpenPort();

            using (var agent = new MockZipkinCollector(agentPort))
                using (ProcessResult processResult = RunSampleAndWaitForExit(agent.Port, arguments: $"WebClient Port={httpPort}", envVars: ZipkinEnvVars))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode}");

                    var spans = agent.WaitForSpans(1);
                    Assert.True(spans.Count > 0, "expected at least one span." + System.Environment.NewLine + "IMPORTANT: Make sure SignalFx.Tracing.ClrProfiler.Managed.dll and its dependencies are in the GAC.");

                    var traceId      = GetHeader(processResult.StandardOutput, HttpHeaderNames.B3TraceId);
                    var parentSpanId = GetHeader(processResult.StandardOutput, HttpHeaderNames.B3SpanId);

                    // inspect the top-level span, underlying spans can be HttpMessageHandler in .NET Core
                    var firstSpan = spans.First();
                    Assert.Equal("GET", firstSpan.Name);
                    Assert.Equal("Samples.HttpMessageHandler", firstSpan.Service);
                    Assert.Null(firstSpan.Type);
                    Assert.Equal(nameof(WebRequest), firstSpan.Tags[Tags.InstrumentationName]);

                    var lastSpan = spans.Last();
                    Assert.Equal(lastSpan.TraceId.ToString("x16", CultureInfo.InvariantCulture), traceId);
                    Assert.Equal(lastSpan.SpanId.ToString("x16", CultureInfo.InvariantCulture), parentSpanId);
                }
        }
Beispiel #8
0
        public SendTracesToZipkinCollector()
        {
            int collectorPort  = 9411;
            var agentUri       = new Uri($"http://localhost:{collectorPort}/api/v2/spans");
            var exporter       = new ZipkinExporter(agentUri);
            var exporterWriter = new ExporterWriter(exporter, new NullMetrics());

            _tracer          = new Tracer(new TracerSettings(), plugins: null, exporterWriter, sampler: null, scopeManager: null, statsd: null);
            _zipkinCollector = new MockZipkinCollector(collectorPort);
        }
Beispiel #9
0
    private IImmutableList <IMockSpan> RunTestApplication(bool enableStartupHook)
    {
        int agentPort = TcpPortProvider.GetOpenPort();

        using (var agent = new MockZipkinCollector(Output, agentPort))
            using (var processResult = RunTestApplicationAndWaitForExit(agent.Port, enableStartupHook: enableStartupHook))
            {
                Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode} and exception: {processResult.StandardError}");
                return(agent.WaitForSpans(2, TimeSpan.FromSeconds(5)));
            }
    }
        public void DoesNotSubmitTraces()
        {
            int agentPort = TcpPortProvider.GetOpenPort();

            using (var agent = new MockZipkinCollector(agentPort))
                using (var processResult = RunSampleAndWaitForExit(agent.Port))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode}");
                    Thread.Sleep(1000); // Give any traces a bit to come through
                    Assert.Empty(agent.Spans);
                }
        }
Beispiel #11
0
        public void TryStartIis(TestHelper helper, bool addClientIp = false)
        {
            lock (this)
            {
                if (_iisExpress == null)
                {
                    var initialAgentPort = TcpPortProvider.GetOpenPort();
                    Agent = new MockZipkinCollector(initialAgentPort);

                    HttpPort = TcpPortProvider.GetOpenPort();

                    _iisExpress = helper.StartIISExpress(Agent.Port, HttpPort, addClientIp);
                }
            }
        }
        public void SubmitTraces()
        {
            var agentPort = TcpPortProvider.GetOpenPort();

            using var agent = new MockZipkinCollector(Output, agentPort);

            const int expectedSpanCount = 3;

            using var processResult = RunTestApplicationAndWaitForExit(agent.Port, enableStartupHook: true);
            Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode} and exception: {processResult.StandardError}");
            var spans = agent.WaitForSpans(expectedSpanCount, TimeSpan.FromSeconds(5));

            using (new AssertionScope())
            {
                spans.Count.Should().Be(expectedSpanCount);

                // ASP.NET Core auto-instrumentation is generating spans
                var httpClientSpan = spans.FirstOrDefault(span => span.Name.Equals("HTTP GET"));
                var httpServerSpan = spans.FirstOrDefault(span => span.Name.Equals("/test"));
                var manualSpan     = spans.FirstOrDefault(span => span.Name.Equals("manual span"));

                httpClientSpan.Should().NotBeNull();
                httpServerSpan.Should().NotBeNull();
                manualSpan.Should().NotBeNull();

                // checking trace hierarchy
                httpClientSpan.ParentId.HasValue.Should().BeFalse();
                httpServerSpan.ParentId.Should().Be(httpClientSpan.SpanId);
                manualSpan.ParentId.Should().Be(httpServerSpan.SpanId);

                httpClientSpan.Service.Should().Be(ServiceName);
                httpServerSpan.Service.Should().Be(ServiceName);
                manualSpan.Service.Should().Be(ServiceName);

                var httpClientTags = httpClientSpan.Tags;
                var httpServerTags = httpServerSpan.Tags;

                httpClientTags.Count.Should().Be(8);
                httpClientTags["http.method"].Should().Be("GET");
                httpClientTags["http.host"].Should().Be(httpServerTags["http.host"]);
                httpClientTags["http.url"].Should().Be(httpServerTags["http.url"]);
                httpClientTags["http.status_code"].Should().Be("200");
                httpClientTags["peer.service"].Should().Be(httpServerTags["http.host"]);
                httpClientTags["span.kind"].Should().Be("client");
                httpServerTags["span.kind"].Should().Be("server");
            }
        }
        public void SubmitsTraces()
        {
            int agentPort = TcpPortProvider.GetOpenPort();

            using (var agent = new MockZipkinCollector(Output, agentPort))
                using (var processResult = RunTestApplicationAndWaitForExit(agent.Port, arguments: $"--mongo-db {_mongoDb.Port}", enableStartupHook: true))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode} and exception: {processResult.StandardError}");

                    var spans = agent.WaitForSpans(3, TimeSpan.FromSeconds(5));
                    Assert.True(spans.Count >= 3, $"Expecting at least 3 spans, only received {spans.Count}");

                    var rootSpan = spans.Single(s => s.ParentId == null);

                    // Check for manual trace
                    Assert.Equal("Main()", rootSpan.Name);
                    Assert.Null(rootSpan.Type);

                    int spansWithStatement = 0;

                    foreach (var span in spans)
                    {
                        Assert.Equal("TestApplication.MongoDB", span.Service);

                        if (Regex.IsMatch(span.Name, "employees\\.*"))
                        {
                            Assert.Equal("mongodb", span.Tags?.GetValueOrDefault("db.system"));
                            Assert.Equal("test-db", span.Tags?.GetValueOrDefault("db.name"));
                            Assert.True("1.0.0.0" == span.Tags?.GetValueOrDefault("otel.library.version"), span.ToString());

                            if (span.Tags?.ContainsKey("db.statement") ?? false)
                            {
                                spansWithStatement++;
                                Assert.True(span.Tags?.ContainsKey("db.statement"), $"No db.statement found on span {span}");
                            }
                        }
                        else
                        {
                            // These are manual (DiagnosticSource) traces
                            Assert.True("1.0.0" == span.Tags?.GetValueOrDefault("otel.library.version"), span.ToString());
                        }
                    }

                    Assert.False(spansWithStatement == 0, "Extraction of the command failed on all spans");
                }
        }
        public void DoesStartNestedProcess()
        {
            var testProcess        = EnvironmentHelper.GetSampleApplicationPath();
            var fakeTraceAgentPath = testProcess.Replace($"{SampleName}.exe", "FakeTraceAgent.exe");

            SetEnvironmentVariable("SIGNALFX_TRACE_AGENT_PATH", fakeTraceAgentPath);

            int agentPort = TcpPortProvider.GetOpenPort();

            using (var agent = new MockZipkinCollector(agentPort))
                using (var processResult = RunSampleAndWaitForExit(agent.Port))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode}");
                    var spans = agent.WaitForSpans(4);
                    Assert.Equal(expected: 4, spans.Count);
                }
        }
Beispiel #15
0
        public void HttpClient(bool appendPathToName)
        {
            int          expectedSpanCount     = EnvironmentHelper.IsCoreClr() ? 2 : 1;
            const string expectedServiceName   = "Samples.HttpMessageHandler";
            var          expectedOperationName = appendPathToName
                ? "POST:/Samples.HttpMessageHandler/"
                : "POST";

            int agentPort = TcpPortProvider.GetOpenPort();
            int httpPort  = TcpPortProvider.GetOpenPort();

            Output.WriteLine($"Assigning port {agentPort} for the agentPort.");
            Output.WriteLine($"Assigning port {httpPort} for the httpPort.");

            var envVars = ZipkinEnvVars;

            if (appendPathToName)
            {
                envVars["SIGNALFX_APPEND_URL_PATH_TO_NAME"] = "true";
            }

            using (var agent = new MockZipkinCollector(agentPort))
                using (ProcessResult processResult = RunSampleAndWaitForExit(agent.Port, arguments: $"HttpClient Port={httpPort}", envVars: envVars))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode}");

                    var spans = agent.WaitForSpans(expectedSpanCount, operationName: expectedOperationName);
                    Assert.True(spans.Count >= expectedSpanCount, $"Expected at least {expectedSpanCount} span, only received {spans.Count}" + System.Environment.NewLine + "IMPORTANT: Make sure SignalFx.Tracing.ClrProfiler.Managed.dll and its dependencies are in the GAC.");

                    foreach (var span in spans)
                    {
                        Assert.Equal(expectedOperationName, span.Name);
                        Assert.Equal(expectedServiceName, span.Service);
                        Assert.Null(span.Type);
                        Assert.Equal(nameof(HttpMessageHandler), span.Tags[Tags.InstrumentationName]);
                    }

                    var firstSpan    = spans.First();
                    var traceId      = GetHeader(processResult.StandardOutput, HttpHeaderNames.B3TraceId);
                    var parentSpanId = GetHeader(processResult.StandardOutput, HttpHeaderNames.B3SpanId);

                    Assert.Equal(firstSpan.TraceId.ToString("x16", CultureInfo.InvariantCulture), traceId);
                    Assert.Equal(firstSpan.SpanId.ToString("x16", CultureInfo.InvariantCulture), parentSpanId);
                }
        }
Beispiel #16
0
        public async void MinimalSpan()
        {
            using (var agent = new MockZipkinCollector(collectorPort))
            {
                var scope = _tracer.StartActive("Operation");
                scope.Span.SetTag(Tags.SpanKind, SpanKinds.Client);
                scope.Dispose();

                await _httpRecorder.WaitForCompletion(1);

                Assert.Single(_httpRecorder.Requests);
                Assert.Single(_httpRecorder.Responses);
                Assert.All(_httpRecorder.Responses, (x) => Assert.Equal(HttpStatusCode.OK, x.StatusCode));

                var trace = _httpRecorder.ZipkinTraces.Single();
                ZipkinHelpers.AssertSpanEqual(scope.Span, trace);
            }
        }
        public void SubmitsTraces(string sanitizeStatements)
        {
            var          expectedSpanCount     = EnvironmentHelper.IsCoreClr() ? 4 : 7;
            const string dbType                = "postgres";
            const string expectedOperationName = dbType + ".query";
            const string expectedServiceName   = "Samples.Dapper";

            int agentPort = TcpPortProvider.GetOpenPort();
            var envVars   = ZipkinEnvVars;

            envVars["SIGNALFX_SANITIZE_SQL_STATEMENTS"] = sanitizeStatements;

            using (var agent = new MockZipkinCollector(agentPort))
                using (ProcessResult processResult = RunSampleAndWaitForExit(agent.Port, envVars: envVars))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode}");

                    var spans = agent.WaitForSpans(expectedSpanCount, operationName: expectedOperationName);
                    Assert.Equal(expectedSpanCount, spans.Count);

                    foreach (var span in spans)
                    {
                        Assert.Equal(expectedOperationName, span.Name);
                        Assert.Equal(expectedServiceName, span.Service);
                        Assert.Equal(dbType, span.Tags[Tags.DbType]);
                        Assert.Null(span.Type);
                        var statement = span.Tags[Tags.DbStatement];
                        Assert.NotNull(statement);
                        if (sanitizeStatements.Equals("true"))
                        {
                            Assert.DoesNotContain(statement, "Id=1");
                            Assert.DoesNotContain(statement, "pg_proc.proname='array_recv'");
                            Assert.True(statement.Contains("Id=?") || statement.Contains("pg_proc.proname=?"));
                        }
                        else
                        {
                            Assert.DoesNotContain(statement, "Id=?");
                            Assert.DoesNotContain(statement, "pg_proc.proname=?");
                            Assert.True(statement.Contains("Id=1") || statement.Contains("pg_proc.proname='array_recv'"));
                        }
                    }
                }
        }
Beispiel #18
0
        public async void MinimalSpan()
        {
            using var mockZipkinCollector = new MockZipkinCollector();

            var span = (OpenTracingSpan)_tracer.BuildSpan("Operation")
                       .Start();

            span.Finish();

            // Check that the HTTP calls went as expected
            await _httpRecorder.WaitForCompletion(1);

            Assert.Single(_httpRecorder.Requests);
            Assert.Single(_httpRecorder.Responses);
            Assert.All(_httpRecorder.Responses, (x) => Assert.Equal(HttpStatusCode.OK, x.StatusCode));

            var trace = _httpRecorder.ZipkinTraces.Single();

            ZipkinHelpers.AssertSpanEqual(span.Span, trace.Single());
        }
        public void SubmitsTraces()
        {
            int          expectedSpanCount        = EnvironmentHelper.IsCoreClr() ? 34 : 30;
            const string expectedServiceName      = "Samples.HttpMessageHandler";
            var          expectedSpanNamePrefixes = EnvironmentHelper.IsCoreClr()
                ? new string[] { "GET", "POST", "PUT", "DELETE", "PATCH" }
                : new string[] { "GET", "POST", "PUT", "DELETE" };

            int agentPort = TcpPortProvider.GetOpenPort();
            int httpPort  = TcpPortProvider.GetOpenPort();

            Output.WriteLine($"Assigning port {agentPort} for the agentPort.");
            Output.WriteLine($"Assigning port {httpPort} for the httpPort.");

            using (var agent = new MockZipkinCollector(agentPort))
                using (ProcessResult processResult = RunSampleAndWaitForExit(agent.Port, arguments: $"Port={httpPort}"))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode}");

                    var spans = agent.WaitForSpans(expectedSpanCount, operationNameContainsAny: expectedSpanNamePrefixes);
                    Assert.Equal(expectedSpanCount, spans.Count);

                    foreach (var span in spans)
                    {
#pragma warning disable xUnit2012 // Do not use Enumerable.Any() to check if a value exists in a collection
                        Assert.True(expectedSpanNamePrefixes.Any(prefix => span.Name.StartsWith(prefix)));
#pragma warning restore xUnit2012 // Do not use Enumerable.Any() to check if a value exists in a collection
                        Assert.Equal(expectedServiceName, span.Service);
                        Assert.Null(span.Type);
                        Assert.Equal("HttpMessageHandler", span.Tags[Tags.InstrumentationName]);
                        Assert.False(span.Tags?.ContainsKey(Tags.Version), "External service span should not have service version tag.");
                    }

                    var firstSpan    = spans.First();
                    var traceId      = StringUtil.GetHeader(processResult.StandardOutput, HttpHeaderNames.B3TraceId);
                    var parentSpanId = StringUtil.GetHeader(processResult.StandardOutput, HttpHeaderNames.B3SpanId);

                    Assert.Equal(firstSpan.TraceId.ToString("x16"), traceId);
                    Assert.Equal(firstSpan.SpanId.ToString("x16"), parentSpanId);
                }
        }
Beispiel #20
0
        public async void Utf8Everywhere()
        {
            using (var agent = new MockZipkinCollector(collectorPort))
            {
                var scope = _tracer.StartActive("Aᛗᚪᚾᚾᚪ", serviceName: "На берегу пустынных волн");
                scope.Span.ResourceName = "η γλώσσα μου έδωσαν ελληνική";
                scope.Span.SetTag("யாமறிந்த", "ნუთუ კვლა");
                scope.Span.Log("யாமறிந்த", "ნუთუ კვლა");
                scope.Span.Log("யாமறிந்த", "ნუთუ კვლა");
                scope.Dispose();

                await _httpRecorder.WaitForCompletion(1);

                Assert.Single(_httpRecorder.Requests);
                Assert.Single(_httpRecorder.Responses);
                Assert.All(_httpRecorder.Responses, (x) => Assert.Equal(HttpStatusCode.OK, x.StatusCode));

                var trace = _httpRecorder.ZipkinTraces.Single();
                ZipkinHelpers.AssertSpanEqual(scope.Span, trace);
            }
        }
Beispiel #21
0
        public void HttpClient_TracingDisabled()
        {
            int agentPort = TcpPortProvider.GetOpenPort();
            int httpPort  = TcpPortProvider.GetOpenPort();

            using (var agent = new MockZipkinCollector(agentPort))
                using (ProcessResult processResult = RunSampleAndWaitForExit(agent.Port, arguments: $"HttpClient TracingDisabled Port={httpPort}", envVars: ZipkinEnvVars))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode}");

                    var spans = agent.WaitForSpans(1, 500);
                    Assert.Equal(0, spans.Count);

                    var traceId        = GetHeader(processResult.StandardOutput, HttpHeaderNames.B3TraceId);
                    var parentSpanId   = GetHeader(processResult.StandardOutput, HttpHeaderNames.B3SpanId);
                    var tracingEnabled = GetHeader(processResult.StandardOutput, HttpHeaderNames.TracingEnabled);

                    Assert.Null(traceId);
                    Assert.Null(parentSpanId);
                    Assert.Equal("false", tracingEnabled);
                }
        }
Beispiel #22
0
        public void MinimalSpan()
        {
            using (var zipkinCollector = new MockZipkinCollector(collectorPort))
            {
                var scope = _tracer.StartActive("Operation");
                scope.Span.SetTag(Tags.SpanKind, SpanKinds.Client);
                scope.Span.SetTag("key", "value");
                scope.Dispose();

                var spans = zipkinCollector.WaitForSpans(1);

                Assert.Single(spans);
                var zspan = spans[0];
                Assert.Equal(scope.Span.OperationName, zspan.Name);
                Assert.True(zspan.Tags.TryGetValue("key", out var tagValue));
                Assert.Equal("value", tagValue);

                // The span.kind has an special treatment.
                Assert.False(zspan.Tags.ContainsKey(Tags.SpanKind));
                Assert.Equal("CLIENT", zspan.Kind);
            }
        }
Beispiel #23
0
        public async void Utf8Everywhere()
        {
            using var mockZipkinCollector = new MockZipkinCollector();

            var span = (OpenTracingSpan)_tracer.BuildSpan("Aᛗᚪᚾᚾᚪ")
                       .WithTag(Tags.ResourceName, "η γλώσσα μου έδωσαν ελληνική")
                       .WithTag(CustomTags.ServiceName, "На берегу пустынных волн")
                       .WithTag("யாமறிந்த", "ნუთუ კვლა")
                       .Start();

            span.Finish();

            // Check that the HTTP calls went as expected
            await _httpRecorder.WaitForCompletion(1);

            Assert.Single(_httpRecorder.Requests);
            Assert.Single(_httpRecorder.Responses);
            Assert.All(_httpRecorder.Responses, (x) => Assert.Equal(HttpStatusCode.OK, x.StatusCode));

            var trace = _httpRecorder.ZipkinTraces.Single();

            ZipkinHelpers.AssertSpanEqual(span.Span, trace.Single());
        }
        public async void CustomServiceName(bool serviceNamePerSpanEnabled)
        {
            var savedServiceNamePerSpanSetting = _tracer.Settings.ServiceNamePerSpanEnabled;

            try
            {
                _tracer.Settings.ServiceNamePerSpanEnabled = serviceNamePerSpanEnabled;

                using (var agent = new MockZipkinCollector(collectorPort))
                {
                    const string serviceName = "MyService";

                    var scope = _tracer.StartActive("Operation-From-SendTracesToZipkinCollector", serviceName: serviceName);
                    scope.Span.ResourceName = "This is a resource";
                    scope.Dispose();

                    agent.WaitForSpans(1);
                    await _httpRecorder.WaitForCompletion(1);

                    Assert.Single(_httpRecorder.Requests);
                    Assert.Single(_httpRecorder.Responses);
                    Assert.All(_httpRecorder.Responses, (x) => Assert.Equal(HttpStatusCode.OK, x.StatusCode));

                    var trace = _httpRecorder.ZipkinTraces.Single();

                    var expectedServiceName = serviceNamePerSpanEnabled
                                                  ? serviceName
                                                  : _tracer.DefaultServiceName;

                    ZipkinHelpers.AssertSpanEqual(scope.Span, trace, expectedServiceName);
                }
            }
            finally
            {
                _tracer.Settings.ServiceNamePerSpanEnabled = savedServiceNamePerSpanSetting;
            }
        }
        public void TracingDisabled_DoesNotSubmitsTraces()
        {
            const string expectedOperationName = "http.request";

            int agentPort = TcpPortProvider.GetOpenPort();
            int httpPort  = TcpPortProvider.GetOpenPort();

            using (var agent = new MockZipkinCollector(agentPort))
                using (ProcessResult processResult = RunSampleAndWaitForExit(agent.Port, arguments: $"TracingDisabled Port={httpPort}"))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode}");

                    var spans = agent.WaitForSpans(1, 3000, operationName: expectedOperationName);
                    Assert.Equal(0, spans.Count);

                    var traceId        = StringUtil.GetHeader(processResult.StandardOutput, HttpHeaderNames.B3TraceId);
                    var parentSpanId   = StringUtil.GetHeader(processResult.StandardOutput, HttpHeaderNames.B3ParentId);
                    var tracingEnabled = StringUtil.GetHeader(processResult.StandardOutput, HttpHeaderNames.TracingEnabled);

                    Assert.Null(traceId);
                    Assert.Null(parentSpanId);
                    Assert.Equal("false", tracingEnabled);
                }
        }
Beispiel #26
0
        public async void CustomServiceName()
        {
            using var mockZipkinCollector = new MockZipkinCollector();

            const string ServiceName = "MyService";

            var span = (OpenTracingSpan)_tracer.BuildSpan("Operation-From-OpenTracingSendTracesToAgent")
                       .WithTag(Tags.ResourceName, "This is a resource")
                       .WithTag(CustomTags.ServiceName, ServiceName)
                       .Start();

            span.Finish();

            // Check that the HTTP calls went as expected
            await _httpRecorder.WaitForCompletion(1);

            Assert.Single(_httpRecorder.Requests);
            Assert.Single(_httpRecorder.Responses);
            Assert.All(_httpRecorder.Responses, (x) => Assert.Equal(HttpStatusCode.OK, x.StatusCode));

            var trace = _httpRecorder.ZipkinTraces.Single();

            ZipkinHelpers.AssertSpanEqual(span.Span, trace.Single());
        }
        public void AdditionalDiagnosticListenerSpan(bool addClientIp)
        {
            var agentPort      = TcpPortProvider.GetOpenPort();
            var aspNetCorePort = TcpPortProvider.GetOpenPort();
            var envVars        = ZipkinEnvVars;

            envVars["SIGNALFX_INSTRUMENTATION_ASPNETCORE_DIAGNOSTIC_LISTENERS"] = "Unused,HotChocolate.Execution,Another.Unused";

            if (addClientIp)
            {
                envVars["SIGNALFX_ADD_CLIENT_IP_TO_SERVER_SPANS"] = "true";
            }

            using (var agent = new MockZipkinCollector(agentPort))
                using (var process = StartSample(agent.Port, arguments: null, packageVersion: string.Empty, aspNetCorePort: aspNetCorePort, envVars: envVars))
                {
                    agent.SpanFilters.Add(IsNotServerLifeCheck);

                    var wh = new EventWaitHandle(false, EventResetMode.AutoReset);

                    process.OutputDataReceived += (sender, args) =>
                    {
                        if (args.Data != null)
                        {
                            if (args.Data.Contains("Now listening on:") || args.Data.Contains("Unable to start Kestrel"))
                            {
                                wh.Set();
                            }

                            Output.WriteLine($"[webserver][stdout] {args.Data}");
                        }
                    };
                    process.BeginOutputReadLine();

                    process.ErrorDataReceived += (sender, args) =>
                    {
                        if (args.Data != null)
                        {
                            Output.WriteLine($"[webserver][stderr] {args.Data}");
                        }
                    };

                    process.BeginErrorReadLine();

                    wh.WaitOne(5000);

                    var maxMillisecondsToWait = 15_000;
                    var intervalMilliseconds  = 500;
                    var intervals             = maxMillisecondsToWait / intervalMilliseconds;
                    var serverReady           = false;

                    // wait for server to be ready to receive requests
                    while (intervals-- > 0)
                    {
                        try
                        {
                            serverReady = SubmitRequest(aspNetCorePort, "/alive-check") == HttpStatusCode.OK;
                        }
                        catch
                        {
                            // ignore
                        }

                        if (serverReady)
                        {
                            break;
                        }

                        Thread.Sleep(intervalMilliseconds);
                    }

                    if (!serverReady)
                    {
                        throw new Exception("Couldn't verify the application is ready to receive requests.");
                    }

                    var testStart = DateTime.Now;

                    var paths = Expectations.Select(e => e.OriginalUri).ToArray();
                    SubmitRequests(aspNetCorePort, paths);

                    var spans =
                        agent.WaitForSpans(
                            Expectations.Count,
                            minDateTime: testStart)
                        .OrderBy(s => s.Start)
                        .ToList();

                    if (!process.HasExited)
                    {
                        process.Kill();
                    }

                    Console.WriteLine($"Spans: {spans}");

                    var expectations = addClientIp ? ExpectationsWithClientIp : Expectations;
                    SpanTestHelpers.AssertExpectationsMet(expectations, spans);
                }
        }
Beispiel #28
0
        public void SubmitsTraces(string packageVersion, string tagCommands)
        {
            int agentPort = TcpPortProvider.GetOpenPort();
            var envVars   = ZipkinEnvVars;

            envVars["SIGNALFX_INSTRUMENTATION_REDIS_TAG_COMMANDS"] = tagCommands;

            using (var agent = new MockZipkinCollector(agentPort))
                using (var processResult = RunSampleAndWaitForExit(agent.Port, arguments: $"{TestPrefix}", packageVersion: packageVersion, envVars: envVars))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode}");

                    // note: ignore the INFO command because it's timing is unpredictable (on Linux?)
                    var spans = agent.WaitForSpans(11)
                                .Where(s => s.Tags.GetValueOrDefault <string>("db.type") == "redis" && s.Name != "INFO")
                                .OrderBy(s => s.Start)
                                .ToList();

                    var host = Environment.GetEnvironmentVariable("SERVICESTACK_REDIS_HOST") ?? "localhost:6379";
                    var port = host.Substring(host.IndexOf(':') + 1);
                    host = host.Substring(0, host.IndexOf(':'));

                    foreach (var span in spans)
                    {
                        Assert.Equal("Samples.ServiceStack.Redis", span.Service);
                        Assert.Equal(SpanTypes.Redis, span.Tags.GetValueOrDefault <string>("db.type"));
                        Assert.Equal("ServiceStack.Redis", span.Tags.GetValueOrDefault <string>("component"));
                        Assert.Equal(host, span.Tags.GetValueOrDefault <string>("peer.hostname"));
                        Assert.Equal(port, span.Tags.GetValueOrDefault <string>("peer.port"));
                        Assert.Equal(SpanKinds.Client, span.Tags.GetValueOrDefault <string>("span.kind"));
                    }

                    var expected = new TupleList <string, string>
                    {
                        { "ROLE", "ROLE" },
                        { "SET", $"SET {TestPrefix}ServiceStack.Redis.INCR 0" },
                        { "PING", "PING" },
                        { "DDCUSTOM", "DDCUSTOM COMMAND" },
                        { "ECHO", "ECHO Hello World" },
                        { "SLOWLOG", "SLOWLOG GET 5" },
                        { "INCR", $"INCR {TestPrefix}ServiceStack.Redis.INCR" },
                        { "INCRBYFLOAT", $"INCRBYFLOAT {TestPrefix}ServiceStack.Redis.INCR 1.25" },
                        { "TIME", "TIME" },
                        { "SELECT", "SELECT 0" },
                    };

                    for (int i = 0; i < expected.Count; i++)
                    {
                        var e1 = expected[i].Item1;

                        // By default all "db.statement" are sanitized - no worries about truncation because
                        // all tag values here are smaller than the maximum recorded length default.
                        var e2 = expected[i].Item2.SanitizeSqlStatement();

                        var a1 = i < spans.Count
                                 ? spans[i].Name
                                 : string.Empty;
                        var a2 = i < spans.Count
                                 ? spans[i].Tags.GetValueOrDefault <string>("db.statement")
                                 : string.Empty;

                        Assert.True(e1 == a1, $@"invalid resource name for span #{i}, expected ""{e1}"", actual ""{a1}""");
                        if (tagCommands.Equals("true"))
                        {
                            Assert.True(e2 == a2, $@"invalid raw command for span #{i}, expected ""{e2}"" != ""{a2}""");
                        }
                        else
                        {
                            Assert.Null(a2);
                        }
                    }
                }
        }
Beispiel #29
0
        public void SubmitsTraces(string packageVersion, string tagCommands)
        {
            int agentPort = TcpPortProvider.GetOpenPort();
            var envVars   = ZipkinEnvVars;

            envVars["SIGNALFX_INSTRUMENTATION_REDIS_TAG_COMMANDS"] = tagCommands;

            using (var agent = new MockZipkinCollector(agentPort))
                using (var processResult = RunSampleAndWaitForExit(agent.Port, arguments: $"{TestPrefix}", packageVersion: packageVersion, envVars: envVars))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode}");

                    var expected = new TupleList <string, string>
                    {
                        { "SET", $"SET {TestPrefix}StackExchange.Redis.INCR" },
                        { "PING", "PING" },
                        { "INCR", $"INCR {TestPrefix}StackExchange.Redis.INCR" },
                        { "INCRBYFLOAT", $"INCRBYFLOAT {TestPrefix}StackExchange.Redis.INCR" },
                        { "GET", $"GET {TestPrefix}StackExchange.Redis.INCR" },
                        { "DDCUSTOM", "DDCUSTOM" },
                        { "ECHO", "ECHO" },
                        { "SLOWLOG", "SLOWLOG" },
                        { "TIME", "TIME" },
                    };

                    if (string.IsNullOrEmpty(packageVersion) || packageVersion.CompareTo("1.2.2") < 0)
                    {
                        expected.Remove(new Tuple <string, string>("DDCUSTOM", "DDCUSTOM"));
                        expected.Remove(new Tuple <string, string>("ECHO", "ECHO"));
                        expected.Remove(new Tuple <string, string>("SLOWLOG", "SLOWLOG"));
                        expected.Remove(new Tuple <string, string>("TIME", "TIME"));
                    }

                    var batchPrefix = $"{TestPrefix}StackExchange.Redis.Batch.";
                    expected.AddRange(new TupleList <string, string>
                    {
                        { "DEBUG", $"DEBUG {batchPrefix}DebugObjectAsync" },
                        { "DDCUSTOM", $"DDCUSTOM" },                                               // Only present on 1.2.2+
                        { "GEOADD", $"GEOADD {batchPrefix}GeoAddAsync" },                          // Only present on 1.2.0+
                        { "GEODIST", $"GEODIST {batchPrefix}GeoDistanceAsync" },                   // Only present on 1.2.0+
                        { "GEOHASH", $"GEOHASH {batchPrefix}GeoHashAsync" },                       // Only present on 1.2.0+
                        { "GEOPOS", $"GEOPOS {batchPrefix}GeoPositionAsync" },                     // Only present on 1.2.0+
                        { "GEORADIUSBYMEMBER", $"GEORADIUSBYMEMBER {batchPrefix}GeoRadiusAsync" }, // Only present on 1.2.0+
                        { "ZREM", $"ZREM {batchPrefix}GeoRemoveAsync" },                           // Only present on 1.2.0+
                        { "HINCRBYFLOAT", $"HINCRBYFLOAT {batchPrefix}HashDecrementAsync" },
                        { "HDEL", $"HDEL {batchPrefix}HashDeleteAsync" },
                        { "HEXISTS", $"HEXISTS {batchPrefix}HashExistsAsync" },
                        { "HGETALL", $"HGETALL {batchPrefix}HashGetAllAsync" },
                        { "HINCRBYFLOAT", $"HINCRBYFLOAT {batchPrefix}HashIncrementAsync" },
                        { "HKEYS", $"HKEYS {batchPrefix}HashKeysAsync" },
                        { "HLEN", $"HLEN {batchPrefix}HashLengthAsync" },
                        { "HMSET", $"HMSET {batchPrefix}HashSetAsync" },
                        { "HVALS", $"HVALS {batchPrefix}HashValuesAsync" },
                        { "PFADD", $"PFADD {batchPrefix}HyperLogLogAddAsync" },        // Only present on 1.0.242+
                        { "PFCOUNT", $"PFCOUNT {batchPrefix}HyperLogLogLengthAsync" }, // Only present on 1.0.242+
                        { "PFMERGE", $"PFMERGE {batchPrefix}HyperLogLogMergeAsync" },  // Only present on 1.0.242+
                        { "PING", $"PING" },
                        // { "DEL", $"DEL key" },
                        { "DUMP", $"DUMP key" },
                        { "EXISTS", $"EXISTS key" },
                        { "PEXPIREAT", $"PEXPIREAT key" },
                        { "MOVE", $"MOVE key" },
                        { "PERSIST", $"PERSIST key" },
                        { "RANDOMKEY", $"RANDOMKEY" },
                        { "RENAME", "RENAME key1" },
                        { "RESTORE", "RESTORE key" },
                        { "TYPE", "TYPE key" },
                        { "LINDEX", "LINDEX listkey" },
                        { "LINSERT", "LINSERT listkey" },
                        { "LINSERT", "LINSERT listkey" },
                        { "LPOP", "LPOP listkey" },
                        { "LPUSH", "LPUSH listkey" },
                        { "LLEN", "LLEN listkey" },
                        { "LRANGE", "LRANGE listkey" },
                        { "LREM", "LREM listkey" },
                        { "RPOP", "RPOP listkey" },
                        { "RPOPLPUSH", "RPOPLPUSH listkey" },
                        { "RPUSH", "RPUSH listkey" },
                        { "LSET", "LSET listkey" },
                        { "LTRIM", "LTRIM listkey" },
                        { "GET", "GET listkey" },
                        { "SET", "SET listkey" },
                        { "PUBLISH", "PUBLISH channel" },
                        { "SADD", "SADD setkey" },
                        { "SUNIONSTORE", "SUNIONSTORE setkey" },
                        { "SUNION", "SUNION setkey1" },
                        { "SISMEMBER", "SISMEMBER setkey" },
                        { "SCARD", "SCARD setkey" },
                        { "SMEMBERS", "SMEMBERS setkey" },
                        { "SMOVE", "SMOVE setkey1" },
                        { "SPOP", "SPOP setkey1" },
                        { "SRANDMEMBER", "SRANDMEMBER setkey" },
                        { "SRANDMEMBER", "SRANDMEMBER setkey" },
                        { "SREM", "SREM setkey" },
                        { "SORT", "SORT setkey" },                 // Only present on 1.0.206+
                        { "SORT", "SORT setkey" },                 // Only present on 1.0.206+
                        { "ZUNIONSTORE", "ZUNIONSTORE ssetkey1" }, // Only present on 1.0.206+
                        { "ZADD", "ZADD ssetkey" },
                        { "ZINCRBY", "ZINCRBY ssetkey" },
                        { "ZINCRBY", "ZINCRBY ssetkey" },
                        { "ZCARD", "ZCARD ssetkey" },
                        { "ZRANGE", "ZRANGE ssetkey" },
                        { "ZRANGE", "ZRANGE ssetkey" },
                        { "ZRANGEBYSCORE", "ZRANGEBYSCORE ssetkey" },
                        { "ZRANGEBYSCORE", "ZRANGEBYSCORE ssetkey" },
                        { "ZRANK", "ZRANK ssetkey" },
                        { "ZREM", "ZREM ssetkey" },
                        { "ZREMRANGEBYRANK", "ZREMRANGEBYRANK ssetkey" },
                        { "ZREMRANGEBYSCORE", "ZREMRANGEBYSCORE ssetkey" },
                        { "ZSCORE", "ZSCORE ssestkey" },
                        { "ZLEXCOUNT", "ZLEXCOUNT ssetkey" },           // Only present on 1.0.273+
                        { "ZRANGEBYLEX", "ZRANGEBYLEX ssetkey" },       // Only present on 1.0.273+
                        { "ZREMRANGEBYLEX", "ZREMRANGEBYLEX ssetkey" }, // Only present on 1.0.273+
                        { "APPEND", "APPEND ssetkey" },
                        { "BITCOUNT", "BITCOUNT ssetkey" },
                        { "BITOP", "BITOP" },
                        { "BITPOS", "BITPOS ssetkey1" },
                        { "INCRBYFLOAT", "INCRBYFLOAT key" },
                        { "GET", "GET key" },
                        { "GETBIT", "GETBIT key" },
                        { "GETRANGE", "GETRANGE key" },
                        { "GETSET", "GETSET key" },
                        { "INCR", "INCR key" },
                        { "STRLEN", "STRLEN key" },
                        { "SET", "SET key" },
                        { "SETBIT", "SETBIT key" },
                        { "SETRANGE", "SETRANGE key" },
                    });

                    FilterExpectedResultsByApiVersion(expected, packageVersion);

                    var dbPrefix = $"{TestPrefix}StackExchange.Redis.Database.";
                    expected.AddRange(new TupleList <string, string>
                    {
                        { "DEBUG", $"DEBUG {dbPrefix}DebugObject" },
                        { "DDCUSTOM", $"DDCUSTOM" },                                 // Only present on 1.2.2+
                        { "GEOADD", $"GEOADD {dbPrefix}Geo" },                       // Only present on 1.2.0+
                        { "GEODIST", $"GEODIST {dbPrefix}Geo" },                     // Only present on 1.2.0+
                        { "GEOHASH", $"GEOHASH {dbPrefix}Geo" },                     // Only present on 1.2.0+
                        { "GEOPOS", $"GEOPOS {dbPrefix}Geo" },                       // Only present on 1.2.0+
                        { "GEORADIUSBYMEMBER", $"GEORADIUSBYMEMBER {dbPrefix}Geo" }, // Only present on 1.2.0+
                        { "ZREM", $"ZREM {dbPrefix}Geo" },                           // Only present on 1.2.0+
                        { "HINCRBYFLOAT", $"HINCRBYFLOAT {dbPrefix}Hash" },
                        { "HDEL", $"HDEL {dbPrefix}Hash" },
                        { "HEXISTS", $"HEXISTS {dbPrefix}Hash" },
                        { "HGET", $"HGET {dbPrefix}Hash" },
                        { "HGETALL", $"HGETALL {dbPrefix}Hash" },
                        { "HINCRBY", $"HINCRBY {dbPrefix}Hash" },
                        { "HKEYS", $"HKEYS {dbPrefix}Hash" },
                        { "HLEN", $"HLEN {dbPrefix}Hash" },
                        // { "HSCAN", $"HSCAN {dbPrefix}Hash" },
                        { "HMSET", $"HMSET {dbPrefix}Hash" },
                        { "HVALS", $"HVALS {dbPrefix}Hash" },
                        { "PFADD", $"PFADD {dbPrefix}HyperLogLog" },      // Only present on 1.0.242+
                        { "PFCOUNT", $"PFCOUNT {dbPrefix}HyperLogLog" },  // Only present on 1.0.242+
                        { "PFMERGE", $"PFMERGE {dbPrefix}HyperLogLog2" }, // Only present on 1.0.242+
                        // { "DEL", $"DEL {dbPrefix}Key" },
                        { "DUMP", $"DUMP {dbPrefix}Key" },
                        { "EXISTS", $"EXISTS {dbPrefix}Key" },
                        { "PEXPIREAT", $"PEXPIREAT {dbPrefix}Key" },
                        { "MIGRATE", $"MIGRATE {dbPrefix}Key" }, // Only present on 1.0.297+
                        { "MOVE", $"MOVE {dbPrefix}Key" },
                        { "PERSIST", $"PERSIST {dbPrefix}Key" },
                        { "RANDOMKEY", $"RANDOMKEY" },
                        { "RENAME", $"RENAME {dbPrefix}Key" },
                        { "RESTORE", $"RESTORE {dbPrefix}Key" },
                        { "PTTL", $"PTTL {dbPrefix}Key" },
                        { "TYPE", $"TYPE {dbPrefix}Key" },
                        { "LINDEX", $"LINDEX {dbPrefix}List" },
                        { "LINSERT", $"LINSERT {dbPrefix}List" },
                        { "LINSERT", $"LINSERT {dbPrefix}List" },
                        { "LPOP", $"LPOP {dbPrefix}List" },
                        { "LPUSH", $"LPUSH {dbPrefix}List" },
                        { "LLEN", $"LLEN {dbPrefix}List" },
                        { "LRANGE", $"LRANGE {dbPrefix}List" },
                        { "LREM", $"LREM {dbPrefix}List" },
                        { "RPOP", $"RPOP {dbPrefix}List" },
                        { "RPOPLPUSH", $"RPOPLPUSH {dbPrefix}List" },
                        { "RPUSH", $"RPUSH {dbPrefix}List" },
                        { "LSET", $"LSET {dbPrefix}List" },
                        { "LTRIM", $"LTRIM {dbPrefix}List" },
                        { "GET", $"GET {dbPrefix}Lock" },
                        { "SET", $"SET {dbPrefix}Lock" },
                        { "PING", $"PING" },
                        { "PUBLISH", $"PUBLISH value" },
                        { "SADD", $"SADD {dbPrefix}Set" },
                        { "SUNION", $"SUNION {dbPrefix}Set" },
                        { "SUNIONSTORE", $"SUNIONSTORE {dbPrefix}Set" },
                        { "SISMEMBER", $"SISMEMBER {dbPrefix}Set" },
                        { "SCARD", $"SCARD {dbPrefix}Set" },
                        { "SMEMBERS", $"SMEMBERS {dbPrefix}Set" },
                        { "SMOVE", $"SMOVE {dbPrefix}Set" },
                        { "SPOP", $"SPOP {dbPrefix}Set" },
                        { "SRANDMEMBER", $"SRANDMEMBER {dbPrefix}Set" },
                        { "SRANDMEMBER", $"SRANDMEMBER {dbPrefix}Set" },
                        { "SREM", $"SREM {dbPrefix}Set" },
                        { "EXEC", $"EXEC" },
                        { "SORT", $"SORT {dbPrefix}Key" },                      // Only present on 1.0.206+
                        { "SORT", $"SORT {dbPrefix}Key" },                      // Only present on 1.0.206+
                        { "ZUNIONSTORE", $"ZUNIONSTORE {dbPrefix}SortedSet2" }, // Only present on 1.0.206+
                        { "ZADD", $"ZADD {dbPrefix}SortedSet" },
                        { "ZINCRBY", $"ZINCRBY {dbPrefix}SortedSet" },
                        { "ZINCRBY", $"ZINCRBY {dbPrefix}SortedSet" },
                        { "ZCARD", $"ZCARD {dbPrefix}SortedSet" },
                        { "ZRANGE", $"ZRANGE {dbPrefix}SortedSet" },
                        { "ZRANGE", $"ZRANGE {dbPrefix}SortedSet" },
                        { "ZRANGEBYSCORE", $"ZRANGEBYSCORE {dbPrefix}SortedSet" },
                        { "ZRANGEBYSCORE", $"ZRANGEBYSCORE {dbPrefix}SortedSet" },
                        { "ZRANK", $"ZRANK {dbPrefix}SortedSet" },
                        { "ZREM", $"ZREM {dbPrefix}SortedSet" },
                        { "ZREMRANGEBYRANK", $"ZREMRANGEBYRANK {dbPrefix}SortedSet" },
                        { "ZREMRANGEBYSCORE", $"ZREMRANGEBYSCORE {dbPrefix}SortedSet" },
                        { "ZSCORE", $"ZSCORE {dbPrefix}SortedSet" },
                        { "ZLEXCOUNT", $"ZLEXCOUNT {dbPrefix}SortedSet" },           // Only present on 1.0.273+
                        { "ZRANGEBYLEX", $"ZRANGEBYLEX {dbPrefix}SortedSet" },       // Only present on 1.0.273+
                        { "ZREMRANGEBYLEX", $"ZREMRANGEBYLEX {dbPrefix}SortedSet" }, // Only present on 1.0.273+
                        { "APPEND", $"APPEND {dbPrefix}Key" },
                        { "BITCOUNT", $"BITCOUNT {dbPrefix}Key" },
                        { "BITOP", $"BITOP" },
                        { "BITPOS", $"BITPOS {dbPrefix}Key" },
                        { "INCRBYFLOAT", $"INCRBYFLOAT {dbPrefix}Key" },
                        { "GET", $"GET {dbPrefix}Key" },
                        { "GETBIT", $"GETBIT {dbPrefix}Key" },
                        { "GETRANGE", $"GETRANGE {dbPrefix}Key" },
                        { "GETSET", $"GETSET {dbPrefix}Key" },
                        { "PTTL+GET", $"PTTL+GET {dbPrefix}Key" },
                        { "STRLEN", $"STRLEN {dbPrefix}Key" },
                        { "SET", $"SET {dbPrefix}Key" },
                        { "SETBIT", $"SETBIT {dbPrefix}Key" },
                        { "SETRANGE", $"SETRANGE {dbPrefix}Key" },
                    });

                    FilterExpectedResultsByApiVersion(expected, packageVersion);

                    // No db.statement tags should exists, so overwrite with null
                    if (!tagCommands.Equals("true"))
                    {
                        var replacement = new TupleList <string, string>();
                        foreach (var item in expected)
                        {
                            replacement.Add(new Tuple <string, string>(item.Item1, null));
                        }

                        expected = replacement;
                    }

                    var spans = agent.WaitForSpans(expected.Count).Where(s => s.Tags.GetValueOrDefault <string>("db.type") == "redis").OrderBy(s => s.Start).ToList();
                    var host  = Environment.GetEnvironmentVariable("STACKEXCHANGE_REDIS_HOST") ?? "localhost:6389";
                    var port  = host.Substring(host.IndexOf(':') + 1);
                    host = host.Substring(0, host.IndexOf(':'));

                    foreach (var span in spans)
                    {
                        Assert.Equal("Samples.StackExchange.Redis", span.Service);
                        Assert.Equal("StackExchange.Redis", span.Tags.GetValueOrDefault <string>("component"));
                        Assert.Equal(SpanKinds.Client, span.Tags.GetValueOrDefault <string>("span.kind"));
                        Assert.Equal(host, span.Tags.GetValueOrDefault <string>("peer.hostname"));
                        Assert.Equal(port, span.Tags.GetValueOrDefault <string>("peer.port"));
                    }

                    var spanLookup = new Dictionary <Tuple <string, string>, int>();
                    foreach (var span in spans)
                    {
                        var key = new Tuple <string, string>(span.Name, span.Tags.GetValueOrDefault <string>("db.statement"));
                        if (spanLookup.ContainsKey(key))
                        {
                            spanLookup[key]++;
                        }
                        else
                        {
                            spanLookup[key] = 1;
                        }
                    }

                    var missing = new List <Tuple <string, string> >();

                    foreach (var e in expected)
                    {
                        var found = spanLookup.ContainsKey(e);
                        if (found)
                        {
                            if (--spanLookup[e] <= 0)
                            {
                                spanLookup.Remove(e);
                            }
                        }
                        else
                        {
                            missing.Add(e);
                        }
                    }

                    foreach (var e in missing)
                    {
                        Assert.True(false, $"no span found for `{e.Item1}`, `{e.Item2}`, remaining spans: `{string.Join(", ", spanLookup.Select(kvp => $"{kvp.Key.Item1}, {kvp.Key.Item2}").ToArray())}`");
                    }
                }
        }
        public void SubmitsTraces(string packageVersion, string tagQueries)
        {
            int agentPort = TcpPortProvider.GetOpenPort();
            var envVars   = ZipkinEnvVars;

            envVars["SIGNALFX_INSTRUMENTATION_ELASTICSEARCH_TAG_QUERIES"] = tagQueries;

            using (var agent = new MockZipkinCollector(agentPort))
                using (var processResult = RunSampleAndWaitForExit(agent.Port, packageVersion: packageVersion, envVars: envVars))
                {
                    Assert.True(processResult.ExitCode >= 0, $"Process exited with code {processResult.ExitCode}");

                    var expected = new List <string>();

                    // commands with sync and async
                    for (var i = 0; i < 2; i++)
                    {
                        expected.AddRange(new List <string>
                        {
                            "Bulk",
                            "Create",
                            "Search",
                            "DeleteByQuery",

                            "CreateIndex",
                            "IndexExists",
                            "UpdateIndexSettings",
                            "BulkAlias",
                            "GetAlias",
                            "PutAlias",
                            // "AliasExists",
                            "DeleteAlias",
                            "DeleteAlias",
                            "CreateIndex",
                            // "SplitIndex",
                            "DeleteIndex",
                            "CloseIndex",
                            "OpenIndex",
                            "PutIndexTemplate",
                            "IndexTemplateExists",
                            "DeleteIndexTemplate",
                            "IndicesShardStores",
                            "IndicesStats",
                            "DeleteIndex",
                            "GetAlias",
                            "ReindexOnServer",

                            "CatAliases",
                            "CatAllocation",
                            "CatCount",
                            "CatFielddata",
                            "CatHealth",
                            "CatHelp",
                            "CatIndices",
                            "CatMaster",
                            "CatNodeAttributes",
                            "CatNodes",
                            "CatPendingTasks",
                            "CatPlugins",
                            "CatRecovery",
                            "CatRepositories",
                            "CatSegments",
                            "CatShards",
                            // "CatSnapshots",
                            "CatTasks",
                            "CatTemplates",
                            "CatThreadPool",

                            // "PutJob",
                            // "ValidateJob",
                            // "GetInfluencers",
                            // "GetJobs",
                            // "GetJobStats",
                            // "GetModelSnapshots",
                            // "GetOverallBuckets",
                            // "FlushJob",
                            // "ForecastJob",
                            // "GetAnomalyRecords",
                            // "GetBuckets",
                            // "GetCategories",
                            // "CloseJob",
                            // "OpenJob",
                            // "DeleteJob",

                            "ClusterAllocationExplain",
                            "ClusterGetSettings",
                            "ClusterHealth",
                            "ClusterPendingTasks",
                            "ClusterPutSettings",
                            "ClusterReroute",
                            "ClusterState",
                            "ClusterStats",

                            "PutRole",
                            // "PutRoleMapping",
                            "GetRole",
                            // "GetRoleMapping",
                            // "DeleteRoleMapping",
                            "DeleteRole",
                            "PutUser",
                            "ChangePassword",
                            "GetUser",
                            // "DisableUser",
                            "DeleteUser",
                        });
                    }

                    var spans = agent.WaitForSpans(expected.Count)
                                .Where(s => DictionaryExtensions.GetValueOrDefault(s.Tags, Tags.InstrumentationName) == "elasticsearch-net")
                                .OrderBy(s => s.Start)
                                .ToList();

                    var statementNames = new List <string>
                    {
                        "Bulk",
                        "BulkAlias",
                        "ChangePassword",
                        "ClusterAllocationExplain",
                        "ClusterPutSettings",
                        "ClusterReroute",
                        "Create",
                        "CreateIndex",
                        "DeleteByQuery",
                        "PutAlias",
                        "PutIndexTemplate",
                        "PutRole",
                        "PutRole",
                        "PutUser",
                        "ReindexOnServer",
                        "Search",
                        "UpdateIndexSettings"
                    };

                    foreach (var span in spans)
                    {
                        Assert.Equal("Samples.Elasticsearch.V5", span.Service);
                        Assert.Equal("elasticsearch", span.Tags["db.type"]);

                        span.Tags.TryGetValue(Tags.DbStatement, out string statement);
                        if (tagQueries.Equals("true") && statementNames.Contains(span.Name))
                        {
                            Assert.NotNull(statement);
                            Assert.NotEqual(string.Empty, statement);
                            Assert.DoesNotContain(statement, "test_user");
                            Assert.DoesNotContain(statement, "supersecret");
                        }
                        else
                        {
                            Assert.Null(statement);
                        }
                    }

                    ValidateSpans(spans, (span) => span.Name, expected);
                }
        }