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]); } } }
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"]); } }
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"); } } }
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); } }
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}"); } }
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); } }
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); }
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); } }
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); } }
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); } }
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'")); } } } }
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); } }
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); } }
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); } }
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); } }
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); } }
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); } }
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); } } } }
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); } }