public async Task CaptureAutoInstrumentedSpans(string targetFramework) { if (!TestEnvironment.IsWindows) { return; } var apmLogger = new InMemoryBlockingLogger(Elastic.Apm.Logging.LogLevel.Error); var apmServer = new MockApmServer(apmLogger, nameof(CaptureAutoInstrumentedSpans)); var port = apmServer.FindAvailablePortToListen(); apmServer.RunInBackground(port); using (var profiledApplication = new ProfiledApplication("OracleManagedDataAccessSample")) { IDictionary <string, string> environmentVariables = new Dictionary <string, string> { ["ELASTIC_APM_SERVER_URL"] = $"http://localhost:{port}", ["ORACLE_CONNECTION_STRING"] = _fixture.ConnectionString, ["ELASTIC_APM_DISABLE_METRICS"] = "*", // to fix ORA-01882 Timezone region not found on CI. ["TZ"] = "GMT", ["ELASTIC_APM_EXIT_SPAN_MIN_DURATION"] = "0", ["ELASTIC_APM_SPAN_COMPRESSION_ENABLED"] = "false" }; profiledApplication.Start( targetFramework, TimeSpan.FromMinutes(2), environmentVariables, null, line => _output.WriteLine(line.Line), exception => _output.WriteLine($"{exception}")); } apmServer.ReceivedData.Transactions.Should().HaveCount(2); apmServer.ReceivedData.Spans.Should().HaveCount(AdoNetTestData.DbRunnerExpectedTotalSpans + AdoNetTestData.OracleProviderExpectedSpans); var testSpans = apmServer.ReceivedData.Spans .Where(s => !s.Name.StartsWith(AdoNetTestData.OracleProviderSpanNameStart)) .ToList(); var genericTransaction = apmServer.ReceivedData.Transactions.FirstOrDefault(t => t.Name == "RunAllAsync<TDbCommand>"); genericTransaction.Should().NotBeNull(); var genericSpans = testSpans.Where(s => s.TransactionId == genericTransaction.Id).ToList(); genericSpans.Should().HaveCount(AdoNetTestData.DbRunnerExpectedRunAllAsyncSpans); var baseTransaction = apmServer.ReceivedData.Transactions.FirstOrDefault(t => t.Name == "RunBaseTypesAsync"); baseTransaction.Should().NotBeNull(); var baseSpans = testSpans.Where(s => s.TransactionId == baseTransaction.Id).ToList(); baseSpans.Should().HaveCount(AdoNetTestData.DbRunnerExpectedRunBaseTypesAsyncSpans); await apmServer.StopAsync(); }
public void ErrorContextSanitizerFilterDoesNotThrowWhenTransactionNotSampled() { var waitHandle = new ManualResetEventSlim(); using var localServer = LocalServer.Create(context => { context.Response.StatusCode = 200; waitHandle.Set(); }); var config = new MockConfiguration(transactionSampleRate: "0", serverUrl: localServer.Uri, flushInterval: "0"); var logger = new InMemoryBlockingLogger(LogLevel.Warning); var payloadSender = new PayloadSenderV2(logger, config, Service.GetDefaultService(config, logger), new Api.System(), MockApmServerInfo.Version710); using var agent = new ApmAgent(new AgentComponents(payloadSender: payloadSender, configurationReader: config)); agent.Tracer.CaptureTransaction("Test", "Test", t => { t.CaptureException(new Exception("boom!")); }); waitHandle.Wait(); logger.Lines.Should().NotContain(line => line.Contains("Exception during execution of the filter on transaction")); }
public async Task CaptureAutoInstrumentedSpans(string targetFramework) { var apmLogger = new InMemoryBlockingLogger(Elastic.Apm.Logging.LogLevel.Error); var apmServer = new MockApmServer(apmLogger, nameof(CaptureAutoInstrumentedSpans)); var port = apmServer.FindAvailablePortToListen(); apmServer.RunInBackground(port); using (var profiledApplication = new ProfiledApplication("MySqlDataSample")) { IDictionary <string, string> environmentVariables = new Dictionary <string, string> { ["ELASTIC_APM_SERVER_URL"] = $"http://localhost:{port}", ["MYSQL_CONNECTION_STRING"] = _fixture.ConnectionString, ["ELASTIC_APM_DISABLE_METRICS"] = "*", ["ELASTIC_APM_EXIT_SPAN_MIN_DURATION"] = "0", ["ELASTIC_APM_SPAN_COMPRESSION_ENABLED"] = "false" }; profiledApplication.Start( targetFramework, TimeSpan.FromMinutes(2), environmentVariables, null, line => _output.WriteLine(line.Line), exception => _output.WriteLine($"{exception}")); } // RunAllAsync<TDbCommand> transaction // RunBaseTypesAsync transaction apmServer.ReceivedData.Transactions.Should().HaveCount(2); // The first MySqlCommand on an opened MySqlConnection executes an additional // command that the profiler instrumentation will create a span for. Since there // are two connections opened, 1 for RunAllAsync<TDbCommand> and 1 for RunBaseTypesAsync, // expect 2 additional spans apmServer.ReceivedData.Spans.Should().HaveCount(AdoNetTestData.DbRunnerExpectedTotalSpans + 2); var genericTransaction = apmServer.ReceivedData.Transactions.FirstOrDefault(t => t.Name == "RunAllAsync<TDbCommand>"); genericTransaction.Should().NotBeNull(); var genericSpans = apmServer.ReceivedData.Spans.Where(s => s.TransactionId == genericTransaction.Id).ToList(); genericSpans.Should().HaveCount(AdoNetTestData.DbRunnerExpectedRunAllAsyncSpans + 1); var baseTransaction = apmServer.ReceivedData.Transactions.FirstOrDefault(t => t.Name == "RunBaseTypesAsync"); baseTransaction.Should().NotBeNull(); var baseSpans = apmServer.ReceivedData.Spans.Where(s => s.TransactionId == baseTransaction.Id).ToList(); baseSpans.Should().HaveCount(AdoNetTestData.DbRunnerExpectedRunBaseTypesAsyncSpans + 1); await apmServer.StopAsync(); }
public async Task Auto_Instrument_With_StartupHook_Should_Capture_Error(string targetFramework) { var apmLogger = new InMemoryBlockingLogger(LogLevel.Error); var apmServer = new MockApmServer(apmLogger, nameof(Auto_Instrument_With_StartupHook_Should_Capture_Error)); var port = apmServer.FindAvailablePortToListen(); apmServer.RunInBackground(port); var transactionWaitHandle = new ManualResetEvent(false); var errorWaitHandle = new ManualResetEvent(false); apmServer.OnReceive += o => { if (o is TransactionDto) { transactionWaitHandle.Set(); } if (o is ErrorDto) { errorWaitHandle.Set(); } }; using (var sampleApp = new SampleApplication()) { var environmentVariables = new Dictionary <string, string> { [EnvVarNames.ServerUrl] = $"http://localhost:{port}", [EnvVarNames.CloudProvider] = "none" }; var uri = sampleApp.Start(targetFramework, environmentVariables); var builder = new UriBuilder(uri) { Path = "Home/Exception" }; var client = new HttpClient(); var response = await client.GetAsync(builder.Uri); response.IsSuccessStatusCode.Should().BeFalse(); transactionWaitHandle.WaitOne(TimeSpan.FromMinutes(2)); apmServer.ReceivedData.Transactions.Should().HaveCount(1); var transaction = apmServer.ReceivedData.Transactions.First(); transaction.Name.Should().Be("GET Home/Exception"); errorWaitHandle.WaitOne(TimeSpan.FromMinutes(2)); apmServer.ReceivedData.Errors.Should().HaveCount(1); var error = apmServer.ReceivedData.Errors.First(); error.Culprit.Should().Be("Elastic.Apm.StartupHook.Sample.Controllers.HomeController"); } await apmServer.StopAsync(); }
public async Task CaptureAutoInstrumentedSpans(string targetFramework, string npgsqlVersion) { var apmLogger = new InMemoryBlockingLogger(Elastic.Apm.Logging.LogLevel.Error); var apmServer = new MockApmServer(apmLogger, nameof(CaptureAutoInstrumentedSpans)); var port = apmServer.FindAvailablePortToListen(); apmServer.RunInBackground(port); using (var profiledApplication = new ProfiledApplication("NpgsqlSample")) { var environmentVariables = new Dictionary <string, string> { ["ELASTIC_APM_SERVER_URL"] = $"http://localhost:{port}", ["POSTGRES_CONNECTION_STRING"] = _fixture.ConnectionString, ["ELASTIC_APM_DISABLE_METRICS"] = "*", ["ELASTIC_APM_EXIT_SPAN_MIN_DURATION"] = "0", ["ELASTIC_APM_SPAN_COMPRESSION_ENABLED"] = "false" }; var msBuildProperties = npgsqlVersion is null ? null : new Dictionary <string, string> { ["NpgsqlVersion"] = npgsqlVersion }; profiledApplication.Start( targetFramework, TimeSpan.FromMinutes(2), environmentVariables, msBuildProperties, line => _output.WriteLine(line.Line), exception => _output.WriteLine($"{exception}")); } apmServer.ReceivedData.Transactions.Should().HaveCount(2); apmServer.ReceivedData.Spans.Should().HaveCount(AdoNetTestData.DbRunnerExpectedTotalSpans); var genericTransaction = apmServer.ReceivedData.Transactions.FirstOrDefault(t => t.Name == "RunAllAsync<TDbCommand>"); genericTransaction.Should().NotBeNull(); var genericSpans = apmServer.ReceivedData.Spans.Where(s => s.TransactionId == genericTransaction.Id).ToList(); genericSpans.Should().HaveCount(AdoNetTestData.DbRunnerExpectedRunAllAsyncSpans); var baseTransaction = apmServer.ReceivedData.Transactions.FirstOrDefault(t => t.Name == "RunBaseTypesAsync"); baseTransaction.Should().NotBeNull(); var baseSpans = apmServer.ReceivedData.Spans.Where(s => s.TransactionId == baseTransaction.Id).ToList(); baseSpans.Should().HaveCount(AdoNetTestData.DbRunnerExpectedRunBaseTypesAsyncSpans); await apmServer.StopAsync(); }
public void SubscribeDuplicateDiagnosticListenerTypesShouldOnlySubscribeSingle() { using (var listener = new DiagnosticListener("Test")) { var logger = new InMemoryBlockingLogger(LogLevel.Debug); using var agent = new ApmAgent(new TestAgentComponents(logger: logger)); agent.Subscribe(new TestSubscriber()); agent.Subscribe(new TestSubscriber()); logger.Lines.Should().Contain(line => line.Contains($"Subscribed {typeof(TestListener).FullName} to `Test' events source")); logger.Lines.Should().Contain(line => line.Contains($"already subscribed to `Test' events source")); agent.SubscribedListeners.Should().HaveCount(1).And.Contain(typeof(TestListener)); } }
public void DisposeSubscriptionShouldRemoveFromSubscribedListeners() { using (var listener = new DiagnosticListener("Test")) { var logger = new InMemoryBlockingLogger(LogLevel.Debug); using var agent = new ApmAgent(new TestAgentComponents(logger: logger)); using (agent.Subscribe(new TestSubscriber())) { logger.Lines.Should().Contain(line => line.Contains($"Subscribed {typeof(TestListener).FullName} to `Test' events source")); agent.SubscribedListeners.Should().HaveCount(1).And.Contain(typeof(TestListener)); } agent.SubscribedListeners.Should().BeEmpty(); } }
public void RequestTimeoutTest() { var waitHandle = new ManualResetEvent(false); using var localServer = LocalServer.Create(context => { Thread.Sleep(500000); context.Response.StatusCode = 200; }); var iterCount = 0; var handler = new MockHttpMessageHandler((r, c) => { // 1. request times out if (iterCount == 0) { throw new OperationCanceledException(); } // 2. request returns OK iterCount++; waitHandle.Set(); return(Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK))); }); var config = new MockConfiguration(serverUrl: localServer.Uri, flushInterval: "1ms"); var logger = new InMemoryBlockingLogger(LogLevel.Trace); var payloadSender = new PayloadSenderV2(logger, config, Service.GetDefaultService(config, logger), new Api.System(), MockApmServerInfo.Version710, handler); using var agent = new ApmAgent(new AgentComponents(payloadSender: payloadSender, configurationReader: config)); // This won't be sent due to timeout agent.Tracer.CaptureTransaction("Test", "Test", t => { }); Thread.Sleep(500); // This will be sent agent.Tracer.CaptureTransaction("Test2", "Test", t => { }); Thread.Sleep(500); waitHandle.WaitOne(TimeSpan.FromMilliseconds(1000)); logger.Lines.Should().NotContain(l => l.Contains("WorkLoop is about to exit because it was cancelled")); }
public async Task Auto_Instrument_With_StartupHook_Should_Capture_Metadata( string targetFramework, string expectedRuntimeName, string expectedFrameworkVersion) { var apmLogger = new InMemoryBlockingLogger(LogLevel.Error); var apmServer = new MockApmServer(apmLogger, nameof(Auto_Instrument_With_StartupHook_Should_Capture_Metadata)); var port = apmServer.FindAvailablePortToListen(); apmServer.RunInBackground(port); var waitHandle = new ManualResetEvent(false); apmServer.OnReceive += o => { if (o is MetadataDto) { waitHandle.Set(); } }; using (var sampleApp = new SampleApplication()) { var environmentVariables = new Dictionary <string, string> { [EnvVarNames.ServerUrl] = $"http://localhost:{port}", [EnvVarNames.CloudProvider] = "none" }; var uri = sampleApp.Start(targetFramework, environmentVariables); var client = new HttpClient(); var response = await client.GetAsync(uri); response.IsSuccessStatusCode.Should().BeTrue(); waitHandle.WaitOne(TimeSpan.FromMinutes(2)); apmServer.ReceivedData.Metadata.Should().HaveCountGreaterOrEqualTo(1); var metadata = apmServer.ReceivedData.Metadata.First(); metadata.Service.Runtime.Name.Should().Be(expectedRuntimeName); metadata.Service.Framework.Name.Should().Be("ASP.NET Core"); metadata.Service.Framework.Version.Should().Be(expectedFrameworkVersion); } await apmServer.StopAsync(); }
public void OsTest() { var logger = new InMemoryBlockingLogger(LogLevel.Trace); var cgroupMetricsProvider = new CgroupMetricsProvider(logger, new List <WildcardMatcher>()); var samples = cgroupMetricsProvider.GetSamples(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { samples.Should().NotBeNullOrEmpty(); logger.Lines.Where(line => line.Contains("detected a non Linux OS, therefore Cgroup metrics will not be reported")).Should().BeNullOrEmpty(); } else { samples.Should().BeNullOrEmpty(); logger.Lines.Where(line => line.Contains("detected a non Linux OS, therefore Cgroup metrics will not be reported")).Should().NotBeNullOrEmpty(); } }
public void CentralConfigNoUserNamePwPrinted() { var userName = "******"; var pw = "def"; var inMemoryLogger = new InMemoryBlockingLogger(LogLevel.Error); var configReader = new MockConfiguration(serverUrls: $"http://{userName}:{pw}@localhost:8123", maxBatchEventCount: "0", flushInterval: "0"); var configStore = new ConfigurationStore(configReader, inMemoryLogger); using var centralConfigFetcher = new CentralConfigurationFetcher(inMemoryLogger, configStore, Service.GetDefaultService(configReader, inMemoryLogger)); inMemoryLogger.Lines.Should().HaveCount(1); inMemoryLogger.Lines.Should().NotContain(n => n.Contains($"{userName}:{pw}")); inMemoryLogger.Lines.Should().Contain(n => n.Contains("http://[REDACTED]:[REDACTED]@localhost:8123")); }
public void PayloadSenderNoUserNamePwPrintedForServerUrl() { var userName = "******"; var pw = "def"; var inMemoryLogger = new InMemoryBlockingLogger(LogLevel.Warning); var configReader = new MockConfiguration(serverUrls: $"http://{userName}:{pw}@localhost:8234", maxBatchEventCount: "0", flushInterval: "0"); using var payloadSender = new PayloadSenderV2(inMemoryLogger, configReader, Service.GetDefaultService(configReader, inMemoryLogger), new Api.System(), MockApmServerInfo.Version710); using var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender)); agent.Tracer.CaptureTransaction("Test", "TestTransaction", () => { }); inMemoryLogger.Lines.Should().HaveCount(1); inMemoryLogger.Lines.Should().NotContain(n => n.Contains($"{userName}:{pw}")); inMemoryLogger.Lines.Should().Contain(n => n.Contains("http://[REDACTED]:[REDACTED]@localhost:8234")); }
public void PayloadSenderNoUserNamePwPrintedForServerUrlWithServerReturn() { var userName = "******"; var pw = "def"; var inMemoryLogger = new InMemoryBlockingLogger(LogLevel.Error); var port = new Random(DateTime.UtcNow.Millisecond).Next(8100, 65535); var configReader = new MockConfigSnapshot(serverUrls: $"http://{userName}:{pw}@localhost:{port}", maxBatchEventCount: "0", flushInterval: "0"); using var payloadSender = new PayloadSenderV2(inMemoryLogger, configReader, Service.GetDefaultService(configReader, inMemoryLogger), new Api.System()); using var localServer = new LocalServer(httpListenerContext => { httpListenerContext.Response.StatusCode = 500; }, $"http://*****:*****@localhost:{port}")); }
public void PayloadSenderNoUserNamePwPrintedForServerUrlWithServerReturn() { var userName = "******"; var pw = "def"; var inMemoryLogger = new InMemoryBlockingLogger(LogLevel.Error); using var localServer = LocalServer.Create(httpListenerContext => { httpListenerContext.Response.StatusCode = 500; }); var uri = new Uri(localServer.Uri); var configReader = new MockConfiguration(serverUrls: $"http://{userName}:{pw}@{uri.Authority}", maxBatchEventCount: "0", flushInterval: "0"); using var payloadSender = new PayloadSenderV2(inMemoryLogger, configReader, Service.GetDefaultService(configReader, inMemoryLogger), new Api.System(), MockApmServerInfo.Version710); using var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender)); agent.Tracer.CaptureTransaction("Test", "TestTransaction", () => { }); inMemoryLogger.Lines.Should().HaveCount(1); inMemoryLogger.Lines.Should().NotContain(n => n.Contains($"{userName}:{pw}")); inMemoryLogger.Lines.Should().Contain(n => n.Contains($"http://[REDACTED]:[REDACTED]@{uri.Authority}")); }
public async Task Auto_Instrument_With_StartupHook(string template, string name, string targetFramework, string path) { var apmLogger = new InMemoryBlockingLogger(LogLevel.Trace); var apmServer = new MockApmServer(apmLogger, nameof(Auto_Instrument_With_StartupHook)); var port = apmServer.FindAvailablePortToListen(); apmServer.RunInBackground(port); var transactionWaitHandle = new ManualResetEvent(false); var metadataWaitHandle = new ManualResetEvent(false); apmServer.OnReceive += o => { if (o is TransactionDto) { transactionWaitHandle.Set(); } else if (o is MetadataDto) { metadataWaitHandle.Set(); } }; using var project = DotnetProject.Create(name, template, targetFramework, "--no-https"); var environmentVariables = new Dictionary <string, string> { [EnvVarNames.ServerUrl] = $"http://*****:*****@"\s*Now listening on:\s*(?<endpoint>http\:[^\s]*)"); process.SubscribeLines( line => { capturedLines.Add(line.Line); var match = endpointRegex.Match(line.Line); if (match.Success) { try { var endpoint = match.Groups["endpoint"].Value.Trim(); uri = new UriBuilder(endpoint) { Path = path }.Uri; } catch (Exception exception) { e = ExceptionDispatchInfo.Capture(exception); } startHandle.Set(); } }, exception => e = ExceptionDispatchInfo.Capture(exception)); var timeout = TimeSpan.FromMinutes(2); var signalled = startHandle.WaitOne(timeout); if (!signalled) { throw new Exception($"Could not start dotnet project within timeout {timeout}: " + string.Join(Environment.NewLine, capturedLines)); } e?.Throw(); var client = new HttpClient(); var response = await client.GetAsync(uri); response.IsSuccessStatusCode.Should().BeTrue(); signalled = transactionWaitHandle.WaitOne(timeout); if (!signalled) { throw new Exception($"Did not receive transaction within timeout {timeout}: " + string.Join(Environment.NewLine, capturedLines) + Environment.NewLine + string.Join(Environment.NewLine, apmLogger.Lines)); } apmServer.ReceivedData.Transactions.Should().HaveCount(1); var transaction = apmServer.ReceivedData.Transactions.First(); transaction.Name.Should().NotBeNullOrEmpty(); signalled = metadataWaitHandle.WaitOne(timeout); if (!signalled) { throw new Exception($"Did not receive metadata within timeout {timeout}: " + string.Join(Environment.NewLine, capturedLines) + Environment.NewLine + string.Join(Environment.NewLine, apmLogger.Lines)); } apmServer.ReceivedData.Metadata.Should().HaveCountGreaterOrEqualTo(1); var metadata = apmServer.ReceivedData.Metadata.First(); metadata.Service.Runtime.Name.Should().NotBeNullOrEmpty(); metadata.Service.Framework.Name.Should().Be("ASP.NET Core"); metadata.Service.Framework.Version.Should().NotBeNullOrEmpty(); } await apmServer.StopAsync(); }
public async Task CaptureAutoInstrumentedSpans(string targetFramework) { var apmLogger = new InMemoryBlockingLogger(Elastic.Apm.Logging.LogLevel.Error); var apmServer = new MockApmServer(apmLogger, nameof(CaptureAutoInstrumentedSpans)); var port = apmServer.FindAvailablePortToListen(); apmServer.RunInBackground(port); var ignoreTopic = "ignore-topic"; using (var profiledApplication = new ProfiledApplication("KafkaSample")) { IDictionary <string, string> environmentVariables = new Dictionary <string, string> { ["KAFKA_HOST"] = _fixture.BootstrapServers, ["ELASTIC_APM_SERVER_URL"] = $"http://localhost:{port}", ["ELASTIC_APM_DISABLE_METRICS"] = "*", ["ELASTIC_APM_IGNORE_MESSAGE_QUEUES"] = ignoreTopic }; profiledApplication.Start( targetFramework, TimeSpan.FromMinutes(2), environmentVariables, line => _output.WriteLine(line.Line), exception => _output.WriteLine($"{exception}")); } // 6 * 10 consume transactions, 14 produce transactions var transactions = apmServer.ReceivedData.Transactions; transactions.Should().HaveCount(74); var consumeTransactions = transactions.Where(t => t.Name.StartsWith("Kafka RECEIVE")).ToList(); consumeTransactions.Should().HaveCount(60); foreach (var consumeTransaction in consumeTransactions) { var spans = apmServer.ReceivedData.Spans.Where(s => s.TransactionId == consumeTransaction.Id); spans.Should().HaveCount(1); consumeTransaction.Context.Message.Queue.Should().NotBeNull(); consumeTransaction.Context.Message.Queue.Name.Should().NotBeNullOrEmpty(); consumeTransaction.ParentId.Should().NotBeNull(); } var produceTransactions = transactions.Where(t => !t.Name.StartsWith("Kafka RECEIVE")).ToList(); produceTransactions.Should().HaveCount(14); foreach (var produceTransaction in produceTransactions) { var spans = apmServer.ReceivedData.Spans.Where(s => s.TransactionId == produceTransaction.Id).ToList(); if (produceTransaction.Name.Contains("INVALID-TOPIC")) { spans.Should().HaveCount(1); } // the produce transaction shouldn't have an auto instrumented publish span as topic is ignored else if (produceTransaction.Name.Contains(ignoreTopic)) { spans.Should().BeEmpty(); } else { spans.Should().HaveCount(10); } foreach (var span in spans) { span.Context.Message.Should().NotBeNull(); span.Context.Message.Queue.Should().NotBeNull(); span.Context.Message.Queue.Name.Should().NotBeNullOrEmpty(); } } await apmServer.StopAsync(); }
public async Task CaptureAutoInstrumentedSpans(string targetFramework) { var apmLogger = new InMemoryBlockingLogger(Logging.LogLevel.Error); var apmServer = new MockApmServer(apmLogger, nameof(CaptureAutoInstrumentedSpans)); var port = apmServer.FindAvailablePortToListen(); apmServer.RunInBackground(port); using (var profiledApplication = new ProfiledApplication("RabbitMqSample")) { IDictionary <string, string> environmentVariables = new Dictionary <string, string> { ["RABBITMQ_HOST"] = _fixture.ConnectionString, ["ELASTIC_APM_SERVER_URL"] = $"http://localhost:{port}", ["ELASTIC_APM_DISABLE_METRICS"] = "*", ["ELASTIC_APM_IGNORE_MESSAGE_QUEUES"] = "test-ignore-exchange-name,test-ignore-queue-name" }; profiledApplication.Start( targetFramework, TimeSpan.FromMinutes(2), environmentVariables, null, line => _output.WriteLine(line.Line), exception => _output.WriteLine($"{exception}")); } var transactions = apmServer.ReceivedData.Transactions; var spans = apmServer.ReceivedData.Spans; transactions.Should().HaveCount(9); var ignoreTransaction = transactions.Single(t => t.Name == "PublishAndGetIgnore"); // don't capture any spans for ignored queues and messages spans.Where(s => s.TransactionId == ignoreTransaction.Id).Should().BeEmpty(); var publishAndGetTransaction = transactions.Single(t => t.Name == "PublishAndGet"); spans.Where(s => s.TransactionId == publishAndGetTransaction.Id).Should().HaveCount(3, "PublishAndGet"); var publishAndGetDefaultTransaction = transactions.Single(t => t.Name == "PublishAndGetDefault"); spans.Where(s => s.TransactionId == publishAndGetDefaultTransaction.Id).Should().HaveCount(3, "PublishAndGetDefault"); var senderTransactions = transactions.Where(t => t.Name == "PublishToConsumer").ToList(); senderTransactions.Should().HaveCount(3, "PublishToConsumer"); var consumeTransactions = transactions.Where(t => t.Name.StartsWith("RabbitMQ RECEIVE from")).ToList(); consumeTransactions.Should().HaveCount(3, "RabbitMQ RECEIVE from"); foreach (var senderTransaction in senderTransactions) { var senderSpan = spans.FirstOrDefault(s => s.TransactionId == senderTransaction.Id); senderSpan.Should().NotBeNull(); var tracingTransaction = consumeTransactions.FirstOrDefault(t => t.TraceId == senderTransaction.TraceId); tracingTransaction.Should().NotBeNull(); tracingTransaction.ParentId.Should().Be(senderSpan.Id); } foreach (var consumeTransaction in consumeTransactions) { spans.Where(s => s.TransactionId == consumeTransaction.Id).Should().HaveCount(1); } await apmServer.StopAsync(); }