/// <summary> /// Asserts on 1 async transaction and 1 error /// </summary> private async Task <MockPayloadSender> AssertWith1TransactionAnd1ErrorAsync(Func <ApmAgent, Task> func) { var payloadSender = new MockPayloadSender(); var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender)); await func(agent); payloadSender.Transactions.Should().NotBeEmpty(); payloadSender.Transactions.Should().NotBeEmpty(); payloadSender.FirstTransaction.Name.Should().Be(TransactionName); payloadSender.FirstTransaction.Type.Should().Be(TransactionType); var duration = payloadSender.FirstTransaction.Duration; duration.Should().BeGreaterOrEqualToMinimumSleepLength(); payloadSender.Errors.Should().NotBeEmpty(); payloadSender.Errors.Should().NotBeEmpty(); payloadSender.FirstError.Exception.Type.Should().Be(typeof(InvalidOperationException).FullName); payloadSender.FirstError.Exception.Message.Should().Be(ExceptionMessage); return(payloadSender); }
public void TransactionWithSpan() { var transactionName = "TestTransaction"; var transactionType = "UnitTest"; var spanName = "TestSpan"; var payloadSender = new MockPayloadSender(); var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender)); var transaction = agent.Tracer.StartTransaction(transactionName, transactionType); var span = transaction.StartSpan(spanName, ApiConstants.TypeExternal); Thread.Sleep(5); //Make sure we have duration > 0 span.End(); transaction.End(); Assert.NotEmpty(payloadSender.Payloads); Assert.NotEmpty(payloadSender.Payloads[0].Transactions[0].Spans); Assert.Equal(spanName, payloadSender.Payloads[0].Transactions[0].Spans[0].Name); Assert.True(payloadSender.Payloads[0].Transactions[0].Spans[0].Duration >= 5); Assert.True(payloadSender.Payloads[0].Transactions[0].Spans[0].Id >= 5); Assert.NotNull(payloadSender.Payloads[0].Service); }
public void DefaultStackTraceLimitAndSpanFramesMinDuration_ShortSpan() { var payloadSender = new MockPayloadSender(); using (var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender))) { agent.Tracer.CaptureTransaction("TestTransaction", "Test", t => { t.CaptureSpan("span", "span", () => { }); }); } payloadSender.FirstSpan.Should().NotBeNull(); if (payloadSender.FirstSpan.Duration < ConfigConsts.DefaultValues.SpanFramesMinDurationInMilliseconds) { payloadSender.FirstSpan.StackTrace.Should().BeNullOrEmpty(); } else { payloadSender.FirstSpan.StackTrace.Should().NotBeNullOrEmpty(); } }
public async Task CancelledAsyncTask() { var payloadSender = new MockPayloadSender(); var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender)); var cancellationTokenSource = new CancellationTokenSource(); var token = cancellationTokenSource.Token; cancellationTokenSource.Cancel(); Func <Task> act = async() => { await agent.Tracer.CaptureTransaction(TransactionName, TransactionType, async() => { // ReSharper disable once MethodSupportsCancellation, we want to delay before we throw the exception await WaitHelpers.DelayMinimum(); token.ThrowIfCancellationRequested(); }); }; await act.Should().ThrowAsync <OperationCanceledException>(); payloadSender.Transactions.Should().NotBeEmpty(); payloadSender.FirstTransaction.Name.Should().Be(TransactionName); payloadSender.FirstTransaction.Type.Should().Be(TransactionType); var duration = payloadSender.FirstTransaction.Duration; duration.Should().BeGreaterOrEqualToMinimumSleepLength(); payloadSender.Errors.Should().NotBeEmpty(); payloadSender.FirstError.Culprit.Should().Be("A task was canceled"); payloadSender.FirstError.Exception.Message.Should().Be("Task canceled"); }
/// <summary> /// Asserts on 1 transaction with 1 async span and 1 error /// </summary> private async Task <MockPayloadSender> AssertWith1TransactionAnd1ErrorAnd1SpanAsync(Func <ITransaction, Task> func) { var payloadSender = new MockPayloadSender(); using var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender)); await agent.Tracer.CaptureTransaction(TransactionName, TransactionType, async t => { await WaitHelpers.DelayMinimum(); await func(t); }); payloadSender.WaitForTransactions(); payloadSender.Transactions.Should().NotBeEmpty(); payloadSender.FirstTransaction.Name.Should().Be(TransactionName); payloadSender.FirstTransaction.Type.Should().Be(TransactionType); var duration = payloadSender.FirstTransaction.Duration; duration.Should().BeGreaterOrEqualToMinimumSleepLength(3); payloadSender.WaitForSpans(); payloadSender.SpansOnFirstTransaction.Should().NotBeEmpty(); payloadSender.SpansOnFirstTransaction[0].Name.Should().Be(SpanName); payloadSender.SpansOnFirstTransaction[0].Type.Should().Be(SpanType); payloadSender.WaitForErrors(); payloadSender.Errors.Should().NotBeEmpty(); payloadSender.FirstError.Exception.Type.Should().Be(typeof(InvalidOperationException).FullName); payloadSender.FirstError.Exception.Message.Should().Be(ExceptionMessage); return(payloadSender); }
public void TransactionWithSpanWithSubTypeAndAction() { const string transactionName = TestTransaction; const string transactionType = UnitTest; const string spanName = "TestSpan"; var payloadSender = new MockPayloadSender(); using (var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender))) { var transaction = agent.Tracer.StartTransaction(transactionName, transactionType); var span = transaction.StartSpan(spanName, ApiConstants.TypeDb, ApiConstants.SubtypeMssql, ApiConstants.ActionQuery); span.End(); transaction.End(); payloadSender.Transactions.Should().NotBeEmpty(); payloadSender.SpansOnFirstTransaction.Should().NotBeEmpty(); payloadSender.SpansOnFirstTransaction[0].Type.Should().Be(ApiConstants.TypeDb); payloadSender.SpansOnFirstTransaction[0].Subtype.Should().Be(ApiConstants.SubtypeMssql); payloadSender.SpansOnFirstTransaction[0].Action.Should().Be(ApiConstants.ActionQuery); agent.Service.Should().NotBeNull(); } }
public void DisableMetrics_DisableCpuMetrics() { var mockPayloadSender = new MockPayloadSender(); var noopLogger = new NoopLogger(); using var metricsProvider = new MetricsCollector(noopLogger, mockPayloadSender, new ConfigStore(new MockConfigSnapshot(disableMetrics: "*cpu*"), noopLogger)); metricsProvider.CollectAllMetrics(); mockPayloadSender.Metrics.Should().NotBeEmpty(); var firstMetrics = mockPayloadSender.Metrics.First(); firstMetrics.Should().NotBeNull(); firstMetrics.Samples.Should().NotContain(n => n.KeyValue.Key.Contains("cpu")); //These are collected on all platforms, with the given config they always should be there firstMetrics.Samples.Should() .Contain(n => n.KeyValue.Key.Equals("system.process.memory.size", StringComparison.InvariantCultureIgnoreCase)); firstMetrics.Samples.Should() .Contain(n => n.KeyValue.Key.Equals("system.process.memory.rss.bytes", StringComparison.InvariantCultureIgnoreCase)); }
public void FillSpanContext() { var payloadSender = new MockPayloadSender(); using var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender, configuration: new MockConfiguration(exitSpanMinDuration: "0"))); agent.Tracer.CaptureTransaction(TransactionName, TransactionType, t => { WaitHelpers.SleepMinimum(); t.CaptureSpan("SampleSpan1", "SampleSpanType", span => { span.Context.Http = new Http { Url = "http://mysite.com", Method = "GET", StatusCode = 200 }; }, isExitSpan: true); t.CaptureSpan("SampleSpan2", "SampleSpanType", span => { span.Context.Db = new Database { Statement = "Select * from MyTable", Type = Database.TypeSql, Instance = "MyInstance" }; }, isExitSpan: true); }); payloadSender.Spans[0].Name.Should().Be("SampleSpan1"); payloadSender.Spans[0].Context.Http.Url.Should().Be("http://mysite.com"); payloadSender.Spans[0].Context.Http.Method.Should().Be("GET"); payloadSender.Spans[0].Context.Http.StatusCode.Should().Be(200); payloadSender.Spans[0].Context.Destination.Address.Should().Be("mysite.com"); payloadSender.Spans[0].Context.Destination.Port.Should().Be(UrlUtilsTests.DefaultHttpPort); payloadSender.Spans[1].Name.Should().Be("SampleSpan2"); payloadSender.Spans[1].Context.Db.Statement.Should().Be("Select * from MyTable"); payloadSender.Spans[1].Context.Db.Type.Should().Be(Database.TypeSql); payloadSender.Spans[1].Context.Db.Instance.Should().Be("MyInstance"); }
public async Task CancelledAsyncTask() { var payloadSender = new MockPayloadSender(); var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender)); var cancellationTokenSource = new CancellationTokenSource(); var token = cancellationTokenSource.Token; cancellationTokenSource.Cancel(); await Assert.ThrowsAsync <OperationCanceledException>(async() => { await agent.Tracer.CaptureTransaction(TransactionName, TransactionType, async() => { // ReSharper disable once MethodSupportsCancellation, we want to delay before we throw the exception await Task.Delay(SleepLength); token.ThrowIfCancellationRequested(); }); }); Assert.NotEmpty(payloadSender.Payloads); Assert.NotEmpty(payloadSender.Payloads[0].Transactions); Assert.Equal(TransactionName, payloadSender.Payloads[0].Transactions[0].Name); Assert.Equal(TransactionType, payloadSender.Payloads[0].Transactions[0].Type); Assert.True(payloadSender.Payloads[0].Transactions[0].Duration >= SleepLength); Assert.NotEmpty(payloadSender.Errors); Assert.NotEmpty(payloadSender.Errors[0].Errors); Assert.Equal("A task was canceled", payloadSender.Errors[0].Errors[0].Culprit); Assert.Equal("Task canceled", payloadSender.Errors[0].Errors[0].Exception.Message); }
public void AgentDisabledTransactionWithLambda() { var payloadSender = new MockPayloadSender(); var configReader = new MockConfigSnapshot(enabled: "false"); using var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender, config: configReader)); var codeExecuted = false; agent.Tracer.CaptureTransaction("TestTransaction", "Test", transaction => { codeExecuted = true; transaction.Should().NotBeOfType <Transaction>(); transaction.Should().BeOfType <NoopTransaction>(); // ReSharper disable AccessToDisposedClosure agent.Tracer.CurrentTransaction.Should().NotBeNull(); agent.Tracer.CurrentTransaction.Should().Be(transaction); }); codeExecuted.Should().BeTrue(); payloadSender.SignalEndTransactions(); payloadSender.WaitForTransactions(); payloadSender.Transactions.Should().BeNullOrEmpty(); }
/// <summary> /// Shared between ErrorOnTransaction and ErrorOnTransactionWithCulprit /// </summary> /// <param name="culprit">Culprit.</param> private void ErrorOnTransactionCommon(string culprit = null) { var transactionName = "TestTransaction"; var transacitonType = "UnitTest"; var exceptionMessage = "Foo!"; var payloadSender = new MockPayloadSender(); var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender)); var transaction = agent.Tracer.StartTransaction(transactionName, transacitonType); Thread.Sleep(5); //Make sure we have duration > 0 try { throw new InvalidOperationException(exceptionMessage); } catch (Exception e) { if (string.IsNullOrEmpty(culprit)) { transaction.CaptureException(e); } else { transaction.CaptureException(e, culprit); } } transaction.End(); Assert.Single(payloadSender.Payloads); Assert.Single(payloadSender.Errors); Assert.Equal(exceptionMessage, payloadSender.Errors[0].Errors[0].Exception.Message); Assert.Equal(exceptionMessage, payloadSender.Errors[0].Errors[0].Exception.Message); Assert.Equal(!string.IsNullOrEmpty(culprit) ? culprit : "PublicAPI-CaptureException", payloadSender.Errors[0].Errors[0].Culprit); }
public void OnNextWithStartAndStop() { var logger = new TestLogger(); var payloadSender = new MockPayloadSender(); var agent = new ApmAgent(new TestAgentComponents(logger, payloadSender: payloadSender)); StartTransaction(agent); var listener = HttpDiagnosticListener.New(agent); var request = new HttpRequestMessage(HttpMethod.Get, "https://elastic.co"); var response = new HttpResponseMessage(HttpStatusCode.OK); //Simulate Start listener.OnNext(new KeyValuePair <string, object>(StartEventKey(listener), new { Request = request })); //Simulate Stop listener.OnNext(new KeyValuePair <string, object>(StopEventKey(listener), new { Request = request, Response = response })); ProcessingRequestsCount(listener).Should().Be(0); var firstSpan = payloadSender.FirstSpan; firstSpan.Should().NotBeNull(); firstSpan.Context.Http.Url.Should().BeEquivalentTo(request.RequestUri.AbsoluteUri); firstSpan.Context.Http.Method.Should().Be(HttpMethod.Get.Method); }
public async Task HttpCallWithW3CActivityFormar() { Activity.DefaultIdFormat = ActivityIdFormat.W3C; var mockPayloadSender = new MockPayloadSender(); using var localServer = new LocalServer(); using var agent = new ApmAgent(new TestAgentComponents(payloadSender: mockPayloadSender)); agent.Subscribe(new HttpDiagnosticsSubscriber()); await agent.Tracer.CaptureTransaction("Test", "Test", async() => { var httpClient = new HttpClient(); try { await httpClient.GetAsync(localServer.Uri); } catch { //ignore - we don't care about the result } }); mockPayloadSender.Spans.Should().HaveCount(1); }
/// <summary> /// Asserts on 1 async transaction and 1 error /// </summary> private async Task <MockPayloadSender> AssertWith1TransactionAnd1ErrorAsync(Func <ApmAgent, Task> func) { var payloadSender = new MockPayloadSender(); var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender)); await func(agent); Assert.NotEmpty(payloadSender.Payloads); Assert.NotEmpty(payloadSender.Payloads[0].Transactions); Assert.Equal(TransactionName, payloadSender.Payloads[0].Transactions[0].Name); Assert.Equal(TransactionType, payloadSender.Payloads[0].Transactions[0].Type); var duration = payloadSender.Payloads[0].Transactions[0].Duration; WaitHelpers.AssertMinimumSleepLength(duration); Assert.NotEmpty(payloadSender.Errors); Assert.NotEmpty(payloadSender.Errors[0].Errors); Assert.Equal(typeof(InvalidOperationException).FullName, payloadSender.Errors[0].Errors[0].Exception.Type); Assert.Equal(ExceptionMessage, payloadSender.Errors[0].Errors[0].Exception.Message); return(payloadSender); }
public void CreateSubSpan() { var payloadSender = new MockPayloadSender(); StartTransactionAndSpanWithSubSpan(payloadSender, s => { }); }
public void ErrorShouldContainTransactionData(bool isSampled, bool captureOnSpan, bool captureAsError) { var payloadSender = new MockPayloadSender(); var expectedErrorContext = new Context(); expectedErrorContext.InternalLabels.Value.InnerDictionary["one"] = 1; expectedErrorContext.InternalLabels.Value.InnerDictionary["twenty two"] = "22"; expectedErrorContext.InternalLabels.Value.InnerDictionary["true"] = true; ITransaction capturedTransaction = null; IExecutionSegment errorCapturingExecutionSegment = null; var mockConfig = new MockConfigSnapshot(transactionSampleRate: isSampled ? "1" : "0"); using (var agent = new ApmAgent(new TestAgentComponents(config: mockConfig, payloadSender: payloadSender))) { agent.Tracer.CaptureTransaction(TestTransaction, CustomTransactionTypeForTests, transaction => { capturedTransaction = transaction; foreach (var item in expectedErrorContext.InternalLabels.Value.MergedDictionary) { transaction.Context.InternalLabels.Value.MergedDictionary[item.Key] = item.Value; } ISpan span = null; if (captureOnSpan) { span = transaction.StartSpan(TestSpan1, ApiConstants.TypeExternal); errorCapturingExecutionSegment = span; } else { errorCapturingExecutionSegment = transaction; } if (captureAsError) { errorCapturingExecutionSegment.CaptureError("Test error message", "Test error culprit", new StackTrace(true).GetFrames()); } else { errorCapturingExecutionSegment.CaptureException(new TestException("test exception")); } // Immutable snapshot of the context should be captured instead of reference to a mutable object // transaction.Context.Labels["three hundred thirty three"] = "333"; span?.End(); }); } payloadSender.WaitForErrors(); payloadSender.Errors.Count.Should().Be(1); payloadSender.FirstError.Transaction.IsSampled.Should().Be(isSampled); payloadSender.FirstError.Transaction.Type.Should().Be(CustomTransactionTypeForTests); payloadSender.FirstError.TransactionId.Should().Be(capturedTransaction.Id); payloadSender.FirstError.TraceId.Should().Be(capturedTransaction.TraceId); payloadSender.FirstError.ParentId.Should() .Be(errorCapturingExecutionSegment.IsSampled ? errorCapturingExecutionSegment.Id : capturedTransaction.Id); payloadSender.FirstError.Transaction.IsSampled.Should().Be(isSampled); if (isSampled) { payloadSender.FirstError.Context.Should().NotBeNull().And.BeEquivalentTo(expectedErrorContext); } else { payloadSender.FirstError.Context.Should().BeNull(); } }
public void destination_properties_set_manually_have_precedence_over_automatically_deduced() { var url = new Uri("http://elastic.co"); const string manualAddress = "manual.address"; const int manualPort = 1234; var payloadSender = new MockPayloadSender(); using (var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender))) { agent.Tracer.CaptureTransaction("test TX name", "test TX type", tx => { tx.CaptureSpan("manually set destination address", "test_span_type", span => { span.Context.Destination = new Destination { Address = manualAddress }; span.Context.Http = new Http { Method = "PUT", Url = url.ToString() }; }); tx.CaptureSpan("manually set destination port", "test_span_type", span => { span.Context.Destination = new Destination { Port = manualPort }; span.Context.Http = new Http { Method = "PUT", Url = url.ToString() }; }); tx.CaptureSpan("manually set destination address to null", "test_span_type", span => { span.Context.Destination = new Destination { Address = null }; span.Context.Http = new Http { Method = "PUT", Url = url.ToString() }; }); tx.CaptureSpan("manually set destination port to null", "test_span_type", span => { span.Context.Destination = new Destination { Port = null }; span.Context.Http = new Http { Method = "PUT", Url = url.ToString() }; }); }); } payloadSender.WaitForSpans(count: 4); var manualAddressSpan = payloadSender.Spans.Single(s => s.Name == "manually set destination address"); manualAddressSpan.Context.Destination.Address.Should().Be(manualAddress); manualAddressSpan.Context.Destination.Port.Should().Be(url.Port); var manualPortSpan = payloadSender.Spans.Single(s => s.Name == "manually set destination port"); manualPortSpan.Context.Destination.Address.Should().Be(url.Host); manualPortSpan.Context.Destination.Port.Should().Be(manualPort); var nullAddressSpan = payloadSender.Spans.Single(s => s.Name == "manually set destination address to null"); nullAddressSpan.Context.Destination.Address.Should().BeNull(); nullAddressSpan.Context.Destination.Port.Should().Be(url.Port); var nullPortSpan = payloadSender.Spans.Single(s => s.Name == "manually set destination port to null"); nullPortSpan.Context.Destination.Address.Should().Be(url.Host); nullPortSpan.Context.Destination.Port.Should().BeNull(); }
public async Task Capture_Redis_Commands_On_Transaction() { var containerBuilder = new TestcontainersBuilder <RedisTestcontainer>() .WithDatabase(new RedisTestcontainerConfiguration()); await using var container = containerBuilder.Build(); await container.StartAsync(); var connection = await ConnectionMultiplexer.ConnectAsync(container.ConnectionString); var count = 0; while (!connection.IsConnected) { if (count < 5) { count++; await Task.Delay(500); } else { throw new Exception("Could not connect to redis for integration test"); } } var payloadSender = new MockPayloadSender(); var transactionCount = 2; using var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender)); connection.UseElasticApm(agent); for (var i = 0; i < transactionCount; i++) { await agent.Tracer.CaptureTransaction("Set and Get String", ApiConstants.TypeDb, async() => { var database = connection.GetDatabase(); await database.StringSetAsync($"string{i}", i); await database.StringGetAsync($"string{i}"); await database.StringSetAsync($"string{i}", i); await database.StringGetAsync($"string{i}"); // fire and forget commands may not end up being captured before transaction end is // called and profiling session is finished await database.StringSetAsync($"string{i}", i, flags: CommandFlags.FireAndForget); await database.StringGetAsync($"string{i}", CommandFlags.FireAndForget); }); } var transactions = payloadSender.Transactions; transactions.Should().HaveCount(transactionCount); var minSpansPerTransaction = 4; payloadSender.Spans.Should().HaveCountGreaterOrEqualTo(transactionCount * minSpansPerTransaction); foreach (var transaction in transactions) { payloadSender.Spans.Count(s => s.TransactionId == transaction.Id).Should().BeGreaterOrEqualTo(minSpansPerTransaction); } await container.StopAsync(); }
public async Task Capture_Redis_Commands_On_Span() { var containerBuilder = new TestcontainersBuilder <RedisTestcontainer>() .WithDatabase(new RedisTestcontainerConfiguration()); await using var container = containerBuilder.Build(); await container.StartAsync(); var connection = await ConnectionMultiplexer.ConnectAsync(container.ConnectionString); var count = 0; while (!connection.IsConnected) { if (count < 5) { count++; await Task.Delay(500); } else { throw new Exception("Could not connect to redis for integration test"); } } var payloadSender = new MockPayloadSender(); var transactionCount = 2; using var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender)); connection.UseElasticApm(agent); var database = connection.GetDatabase(); for (var i = 0; i < transactionCount; i++) { await agent.Tracer.CaptureTransaction($"transaction {i}", ApiConstants.TypeDb, async t => { // span 1 await database.StringSetAsync($"string{i}", i); // span 2 await database.StringGetAsync($"string{i}"); // span 3 await t.CaptureSpan($"parent span {i}", ApiConstants.TypeDb, async() => { // spans 4,5,6,7 await database.StringSetAsync($"string{i}", i); await database.StringGetAsync($"string{i}"); await database.StringSetAsync($"string{i}", i); await database.StringGetAsync($"string{i}"); }); }); } var transactions = payloadSender.Transactions; transactions.Should().HaveCount(transactionCount); var spansPerParentSpan = 4; var topLevelSpans = 3; var spansPerTransaction = spansPerParentSpan + topLevelSpans; payloadSender.Spans.Should().HaveCount(transactionCount * spansPerTransaction); foreach (var transaction in transactions) { var transactionSpans = payloadSender.Spans .Where(s => s.TransactionId == transaction.Id) .ToList(); transactionSpans.Should().HaveCount(spansPerTransaction); var parentSpans = transactionSpans.Where(s => s.ParentId == s.TransactionId).ToList(); parentSpans.Should().HaveCount(topLevelSpans); var parentSpanId = parentSpans.OrderByDescending(s => s.Timestamp).First().Id; transactionSpans.Count(s => s.ParentId == parentSpanId).Should().Be(spansPerParentSpan); } await container.StopAsync(); }
public void CompressionOnParentSpan() { var payloadSender = new MockPayloadSender(); using (var agent = new ApmAgent(new TestAgentComponents(apmServerInfo: MockApmServerInfo.Version80, payloadSender: payloadSender, configuration: new MockConfiguration(spanCompressionEnabled: "true", spanCompressionExactMatchMaxDuration: "5s", exitSpanMinDuration: "0")))) { agent.Tracer.CaptureTransaction("Foo", "Bar", t => { t.CaptureSpan("foo1", "bar", span1 => { for (var i = 0; i < 10; i++) { span1.CaptureSpan("Select * From Table1", ApiConstants.TypeDb, (s) => { s.Context.Db = new Database() { Type = "mssql", Instance = "01" }; }, ApiConstants.SubtypeMssql, isExitSpan: true); } span1.CaptureSpan("foo2", "randomSpan", () => { }); for (var i = 0; i < 10; i++) { span1.CaptureSpan("Select * From Table2", ApiConstants.TypeDb, (s2) => { s2.Context.Db = new Database() { Type = "mssql", Instance = "01" }; }, ApiConstants.SubtypeMssql, isExitSpan: true); } }); }); } payloadSender.Transactions.Should().HaveCount(1); payloadSender.Spans.Should().HaveCount(4); (payloadSender.Spans[3] as Span) !.Composite.Should().BeNull(); payloadSender.Spans[3].Name.Should().Be("foo1"); (payloadSender.Spans[0] as Span) !.Composite.Should().NotBeNull(); (payloadSender.Spans[0] as Span) !.Composite.Count.Should().Be(10); (payloadSender.Spans[0] as Span) !.Composite.CompressionStrategy = "exact_match"; payloadSender.Spans[0].Name.Should().Be("Select * From Table1"); payloadSender.Spans[1].Name.Should().Be("foo2"); (payloadSender.Spans[1] as Span) !.Composite.Should().BeNull(); (payloadSender.Spans[2] as Span) !.Composite.Should().NotBeNull(); (payloadSender.Spans[2] as Span) !.Composite.Count.Should().Be(10); (payloadSender.Spans[2] as Span) !.Composite.CompressionStrategy = "exact_match"; payloadSender.Spans[2].Name.Should().Be("Select * From Table2"); payloadSender.Spans[0].ParentId.Should().Be(payloadSender.Spans[3].Id); payloadSender.Spans[1].ParentId.Should().Be(payloadSender.Spans[3].Id); payloadSender.Spans[1].ParentId.Should().Be(payloadSender.Spans[3].Id); }
public async Task BasicGrpcTest(bool withDiagnosticSource) { var payloadSender = new MockPayloadSender(); using var apmAgent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender)); IHost sampleAppHost; if (withDiagnosticSource) { sampleAppHost = new SampleAppHostBuilder().BuildHost(); apmAgent.Subscribe(new AspNetCoreDiagnosticSubscriber()); } else { sampleAppHost = new SampleAppHostBuilder().BuildHostWithMiddleware(apmAgent); } apmAgent.Subscribe(new GrpcClientDiagnosticSubscriber(), new HttpDiagnosticsSubscriber()); await sampleAppHost.StartAsync(); AppContext.SetSwitch( "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); var channel = GrpcChannel.ForAddress(SampleAppHostBuilder.SampleAppUrl); var client = new Greeter.GreeterClient(channel); await apmAgent.Tracer.CaptureTransaction("SampleCall", "test", async() => { var response = await client.SayHelloAsync( new HelloRequest { Name = "World" }); Debug.WriteLine(response.Message); }); payloadSender.Transactions.Should().HaveCount(2); payloadSender.Transactions.Should() .Contain(transaction => transaction.Result == "OK" && transaction.Name == $"/{Greeter.Descriptor.FullName}/{nameof(client.SayHello)}" && transaction.Outcome == Outcome.Success); //Make sure all spans are collected Thread.Sleep(500); payloadSender.Spans.Should().HaveCountGreaterThan(1); payloadSender.Spans.Should() .Contain(span => span.Subtype == ApiConstants.SubTypeGrpc && span.Outcome == Outcome.Success && span.Name == $"/{Greeter.Descriptor.FullName}/{nameof(client.SayHello)}" && span.Context.Destination.Address == "localhost" && span.Context.Destination.Port == SampleAppHostBuilder.SampleAppPort && span.Context.Destination.Service.Type == "external" && span.Context.Destination.Service.Name == SampleAppHostBuilder.SampleAppUrl && span.Context.Destination.Service.Resource == $"localhost:{SampleAppHostBuilder.SampleAppPort}" ); await sampleAppHost.StopAsync(); }
public void ChangeTransactionContextAfterError() { var mockPayloadSender = new MockPayloadSender(); using var agent = new ApmAgent(new TestAgentComponents(payloadSender: mockPayloadSender)); agent.Tracer.CaptureTransaction("Test", "Test", t => { t.Context.Request = new Request("GET", new Url { Full = "http://localhost", Protocol = "http", Search = "abc" }) { Body = "abc", Headers = new Dictionary <string, string> { { "header1", "headerValue" } } }; t.Context.Response = new Response { StatusCode = 404, Finished = false }; t.SetLabel("foo", "bar"); // Let's capture an error t.CaptureError("Test Error", "Test", new StackTrace().GetFrames()); // Let's change CurrentTransaction.Context after the error is captured t.Context.Request.Method = "PUT"; t.Context.Request.Body = "cde"; t.Context.Request.Headers["header2"] = "headerValue"; t.Context.Request.Url.Full = "http://elastic.co"; t.Context.Request.Url.Protocol = "tcp"; t.Context.Request.Url.Search = "cde"; t.Context.Response.StatusCode = 500; t.Context.Response.Finished = true; t.Context.InternalLabels.Value.InnerDictionary["foo"].Value.Should().Be("bar"); // Asserts on the captured error mockPayloadSender.WaitForErrors(); mockPayloadSender.FirstError.Should().NotBeNull("first error should not be null"); mockPayloadSender.FirstError.Context.Should().NotBeNull("context should not be null"); mockPayloadSender.FirstError.Context.Request.Method.Should().Be("GET"); mockPayloadSender.FirstError.Context.Request.Body.Should().Be("abc"); mockPayloadSender.FirstError.Context.Request.Headers.Count.Should().Be(1); mockPayloadSender.FirstError.Context.Request.Headers["header1"].Should().Be("headerValue"); mockPayloadSender.FirstError.Context.Request.Url.Full.Should().Be("http://localhost"); mockPayloadSender.FirstError.Context.Request.Url.Protocol.Should().Be("http"); mockPayloadSender.FirstError.Context.Request.Url.Search.Should().Be("abc"); mockPayloadSender.FirstError.Context.Response.StatusCode.Should().Be(404); mockPayloadSender.FirstError.Context.Response.Finished.Should().BeFalse(); mockPayloadSender.FirstError.Context.InternalLabels.Value.InnerDictionary["foo"].Value.Should().Be("bar"); mockPayloadSender.FirstError.Context.Response.Headers.Should().BeNull(); }); // Asserts on the captured transaction mockPayloadSender.WaitForTransactions(); mockPayloadSender.FirstTransaction.Context.Request.Method.Should().Be("PUT"); mockPayloadSender.FirstTransaction.Context.Request.Body.Should().Be("cde"); mockPayloadSender.FirstTransaction.Context.Request.Headers.Count.Should().Be(2); mockPayloadSender.FirstTransaction.Context.Request.Headers["header1"].Should().Be("headerValue"); mockPayloadSender.FirstTransaction.Context.Request.Headers["header2"].Should().Be("headerValue"); mockPayloadSender.FirstTransaction.Context.Request.Url.Full.Should().Be("http://elastic.co"); mockPayloadSender.FirstTransaction.Context.Request.Url.Protocol.Should().Be("tcp"); mockPayloadSender.FirstTransaction.Context.Request.Url.Search.Should().Be("cde"); mockPayloadSender.FirstTransaction.Context.Response.StatusCode.Should().Be(500); mockPayloadSender.FirstTransaction.Context.Response.Finished.Should().BeTrue(); mockPayloadSender.FirstTransaction.Context.InternalLabels.Value.InnerDictionary["foo"].Value.Should().Be("bar"); mockPayloadSender.FirstTransaction.Context.Response.Headers.Should().BeNull(); }
public void MultipleDroppedSpanTest() { var payloadSender = new MockPayloadSender(); using (var agent = new ApmAgent(new TestAgentComponents(payloadSender: payloadSender, configuration: new MockConfiguration(transactionMaxSpans: "1")))) { var transaction = agent.Tracer.StartTransaction("foo", "test"); //This is the span which won't be dropped transaction.CaptureSpan("fooSpan", "test", () => { }); //Next spans will be dropped var span1 = transaction.StartSpan("foo", "bar", isExitSpan: true); span1.Context.Http = new Http { Method = "GET", StatusCode = 200, Url = "https://foo.bar" }; span1.Duration = 100; span1.End(); var span2 = transaction.StartSpan("foo", "bar", isExitSpan: true); span2.Context.Http = new Http { Method = "GET", StatusCode = 200, Url = "https://foo.bar" }; span2.Duration = 150; span2.End(); var span3 = transaction.StartSpan("foo", "bar", isExitSpan: true); span3.Context.Http = new Http { Method = "GET", StatusCode = 400, Url = "https://foo.bar" }; span3.Outcome = Outcome.Failure; span3.Duration = 50; span3.End(); var span4 = transaction.StartSpan("foo", "bar", isExitSpan: true); span4.Context.Http = new Http { Method = "GET", StatusCode = 400, Url = "https://foo2.bar" }; span4.Duration = 15; span4.End(); for (var i = 0; i < 50; i++) { var span5 = transaction.StartSpan("foo", "bar", isExitSpan: true); span5.Context.Destination = new Destination { Service = new Destination.DestinationService { Resource = "mysql" } }; span5.Context.Db = new Database { Instance = "instance1", Type = "mysql", Statement = "Select Foo From Bar" }; span5.Duration = 50; span5.End(); } transaction.End(); } payloadSender.Spans.Should().HaveCount(1); payloadSender.FirstTransaction.DroppedSpanStats.Should().NotBeNullOrEmpty(); payloadSender.FirstTransaction.DroppedSpanStats.Should().HaveCount(4); payloadSender.FirstTransaction.DroppedSpanStats.Should() .Contain(n => n.Outcome == Outcome.Success && n.DurationCount == 2 && Math.Abs(n.DurationSumUs - 250) < 1 && n.DestinationServiceResource == "foo.bar:443"); payloadSender.FirstTransaction.DroppedSpanStats.Should() .Contain(n => n.Outcome == Outcome.Failure && n.DurationCount == 1 && Math.Abs(n.DurationSumUs - 50) < 1 && n.DestinationServiceResource == "foo.bar:443"); payloadSender.FirstTransaction.DroppedSpanStats.Should() .Contain(n => n.Outcome == Outcome.Success && n.DurationCount == 1 && Math.Abs(n.DurationSumUs - 15) < 1 && n.DestinationServiceResource == "foo2.bar:443"); payloadSender.FirstTransaction.DroppedSpanStats.Should() .Contain(n => n.Outcome == Outcome.Success && n.DurationCount == 50 && Math.Abs(n.DurationSumUs - 50 * 50) < 1 && n.DestinationServiceResource == "mysql"); }
public DiagnosticListenerTest(WebApplicationFactory <Startup> factory) { _agent = new ApmAgent(new TestAgentComponents()); _capturedPayload = _agent.PayloadSender as MockPayloadSender; _client = Helper.GetClientWithoutDiagnosticListeners(_agent, factory); }