public void TraceIdIsNotSet() { var spanBatch = new NewRelicSpanBatch( spans: new NewRelicSpan[0]); Assert.Null(spanBatch.CommonProperties.TraceId); }
public void SendANonEmptySpanBatch() { var traceId = "123"; var span = new NewRelicSpan( traceId: null, spanId: "Span1", timestamp: DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), parentSpanId: null, attributes: new Dictionary <string, object>() { { NewRelicConsts.Tracing.AttribNameName, "TestSpan" }, }); var spanBatch = new NewRelicSpanBatch( spans: new[] { span }, commonProperties: new NewRelicSpanBatchCommonProperties(traceId)); var dataSender = new TraceDataSender(new TelemetryConfiguration() { ApiKey = "123456" }, null); dataSender.WithHttpHandlerImpl((serializedJson) => { var response = new HttpResponseMessage(System.Net.HttpStatusCode.OK); return(Task.FromResult(response)); }); var response = dataSender.SendDataAsync(spanBatch).Result; Assert.Equal(NewRelicResponseStatus.Success, response.ResponseStatus); }
public async Task RetryBackoffSequence_RetriesExceeded() { var expectedNumSendBatchAsyncCall = 9; // 1 first call + 8 calls from retries var expectedBackoffSequenceFromTestRun = new List <uint>() { 5000, 10000, 20000, 40000, 80000, 80000, 80000, 80000, }; var actualBackoffSequenceFromTestRun = new List <uint>(); var actualCountCallsSendData = 0; var dataSender = new TraceDataSender(new TelemetryConfiguration() { ApiKey = "123456" }, null); dataSender.WithDelayFunction(async(uint milliSecondsDelay) => { actualBackoffSequenceFromTestRun.Add(milliSecondsDelay); await Task.Delay(0); return; }); dataSender.WithCaptureSendDataAsyncDelegate((newRelicSpanBatch, retryNum) => { actualCountCallsSendData++; }); dataSender.WithHttpHandlerImpl((json) => { return(Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.RequestTimeout))); }); var spans = new List <NewRelicSpan>() { new NewRelicSpan( traceId: null, spanId: "Test Span", timestamp: 12345, parentSpanId: null, attributes: null), }; var spanBatch = new NewRelicSpanBatch(spans); var result = await dataSender.SendDataAsync(spanBatch); Assert.Equal(NewRelicResponseStatus.Failure, result.ResponseStatus); Assert.Equal(HttpStatusCode.RequestTimeout, result.HttpStatusCode); Assert.Equal(expectedNumSendBatchAsyncCall, actualCountCallsSendData); Assert.Equal(expectedBackoffSequenceFromTestRun, actualBackoffSequenceFromTestRun); }
/// <summary> /// Sends the data to New Relic endpoint. /// </summary> /// <param name="spanBatch"></param> /// <returns></returns> private static async Task SendDataToNewRelic(NewRelicSpanBatch spanBatch) { var result = await _dataSvc.SendDataAsync(spanBatch); Console.WriteLine("Send Data to New Relic"); Console.WriteLine($"{"Result",-20}: {result.ResponseStatus}"); Console.WriteLine($"{"Http Status",-20}: {result.HttpStatusCode}"); Console.WriteLine($"{"Message",-20}: {result.Message}"); }
public async Task SendDataAsyncThrowsNonHttpException() { const int expectedNumSendBatchAsyncCall = 1; const int expectedNumHttpHandlerCall = 0; var dataSender = new TraceDataSender(new TelemetryConfiguration() { ApiKey = "123456" }, null); dataSender.WithDelayFunction(async(uint milliSecondsDelay) => { await Task.Delay(0); return; }); var actualCountCallsSendData = 0; dataSender.WithCaptureSendDataAsyncDelegate((sb, retry) => { actualCountCallsSendData++; throw new Exception("Test Exception"); }); var actualCallsHttpHandler = 0; dataSender.WithHttpHandlerImpl((json) => { actualCallsHttpHandler++; return(Task.FromResult(new HttpResponseMessage() { StatusCode = HttpStatusCode.OK })); }); var spans = new List <NewRelicSpan>() { new NewRelicSpan( traceId: null, spanId: "Test Span", timestamp: 12345, parentSpanId: null, attributes: null), }; var spanBatch = new NewRelicSpanBatch(spans); var result = await dataSender.SendDataAsync(spanBatch); Assert.Equal(NewRelicResponseStatus.Failure, result.ResponseStatus); Assert.Equal("Test Exception", result.Message); Assert.Null(result.HttpStatusCode); Assert.Equal(expectedNumSendBatchAsyncCall, actualCountCallsSendData); Assert.Equal(expectedNumHttpHandlerCall, actualCallsHttpHandler); }
public void TraceIdIsSet() { var traceId = "myId"; var spanBatch = new NewRelicSpanBatch( commonProperties: new NewRelicSpanBatchCommonProperties(traceId: traceId), spans: new NewRelicSpan[0]); Assert.Equal(traceId, spanBatch.CommonProperties.TraceId); }
public void ToJson_NonEmptySpanBatch() { // Arrange var spanBatch = new NewRelicSpanBatch( commonProperties: new NewRelicSpanBatchCommonProperties( traceId: "traceId"), spans: new NewRelicSpan[] { new NewRelicSpan( spanId: "span1", traceId: "traceId", timestamp: 1L, parentSpanId: "parentId", attributes: new Dictionary <string, object>() { { NewRelicConsts.Tracing.AttribNameDurationMs, 67 }, { NewRelicConsts.Tracing.AttribNameServiceName, "serviceName" }, { NewRelicConsts.Tracing.AttribNameName, "name" }, }), }); // Act var jsonString = spanBatch.ToJson(); // Assert var resultSpanBatches = TestHelpers.DeserializeArray(jsonString); TestHelpers.AssertForCollectionLength(resultSpanBatches, 1); var resultSpanBatch = resultSpanBatches.First(); var resultCommonProps = TestHelpers.DeserializeObject(resultSpanBatch["common"]); TestHelpers.AssertForAttribValue(resultCommonProps, "trace.id", "traceId"); var resultSpans = TestHelpers.DeserializeArray(resultSpanBatch["spans"]); TestHelpers.AssertForCollectionLength(resultSpans, 1); var resultSpan = resultSpans.First(); TestHelpers.AssertForAttribValue(resultSpan, "id", "span1"); TestHelpers.AssertForAttribValue(resultSpan, "trace.id", "traceId"); TestHelpers.AssertForAttribValue(resultSpan, "timestamp", 1); TestHelpers.AssertForAttribCount(resultSpan, 4); var resultSpanAttribs = TestHelpers.DeserializeObject(resultSpan["attributes"]); TestHelpers.AssertForAttribValue(resultSpanAttribs, "duration.ms", 67); TestHelpers.AssertForAttribValue(resultSpanAttribs, "name", "name"); TestHelpers.AssertForAttribValue(resultSpanAttribs, "service.name", "serviceName"); TestHelpers.AssertForAttribValue(resultSpanAttribs, "parent.id", "parentId"); TestHelpers.AssertForAttribCount(resultSpanAttribs, 4); }
/// <summary> /// In this example, multiple traces will be reported in the same batch. /// Accordingly, the TraceId is applied to the individual spans, and NOT on /// the SpanBatch. /// </summary> private static async Task Example_SpanBatchForMultipleTraces() { // The collection of Spans var spans = new List <NewRelicSpan>(); for (var traceIdx = 0; traceIdx < 3; traceIdx++) { var traceId = Guid.NewGuid().ToString(); // Perform 10 units of work as part of this trace/spanBatch for (var spanIdx = 0; spanIdx < 5; spanIdx++) { var spanStartTime = DateTime.UtcNow; var spanAttribs = new Dictionary <string, object>(); try { // Attempt to perform a unit of work DoWork($"Hello World #{spanIdx}"); } catch (Exception ex) { // In the event of an exception, mark the span // as having an error and record a custom attribute // with the details about the exception. spanAttribs[NewRelicConsts.Tracing.AttribNameErrorMsg] = ex.Message; } finally { spanAttribs[NewRelicConsts.Tracing.AttribNameName] = $"{traceId} - {spanIdx}"; spanAttribs[NewRelicConsts.Tracing.AttribNameDurationMs] = DateTime.UtcNow.Subtract(spanStartTime).TotalMilliseconds; // Create a new Span assigning it a random guid as the spanId var span = new NewRelicSpan( traceId: traceId, //Since we're mixing traces in the same batch, the trace id is supplied on each span spanId: Guid.NewGuid().ToString(), parentSpanId: null, timestamp: DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), attributes: spanAttribs); spans.Add(span); } } } var spanBatch = new NewRelicSpanBatch(spans); // Send the SpanBatch to the New Relic endpoint. await SendDataToNewRelic(spanBatch); }
public async Task RetryOn429WithSpecificDate_429HappensOnce() { const int delayMs = 10000; // The actual retry delay will be slightly less than delayMs since UtcNow is recalculated in RetryWithServerDelay() var errorMargin = TimeSpan.FromMilliseconds(1000).TotalMilliseconds; var actualResponseFromTestRun = new List <Response>(); uint actualDelayFromTestRun = 0; var dataSender = new TraceDataSender(new TelemetryConfiguration() { ApiKey = "123456", MaxRetryAttempts = 1 }, null); dataSender.WithDelayFunction(async(uint milliSecondsDelay) => { actualDelayFromTestRun = milliSecondsDelay; await Task.Delay(0); return; }); dataSender.WithHttpHandlerImpl((json) => { var httpResponse = new HttpResponseMessage((HttpStatusCode)429); var retryOnSpecificTime = DateTimeOffset.UtcNow + TimeSpan.FromMilliseconds(delayMs); httpResponse.Headers.RetryAfter = new System.Net.Http.Headers.RetryConditionHeaderValue(retryOnSpecificTime); return(Task.FromResult(httpResponse)); }); var spans = new List <NewRelicSpan>() { new NewRelicSpan( traceId: null, spanId: "Test Span", timestamp: 12345, parentSpanId: null, attributes: null), }; var spanBatch = new NewRelicSpanBatch(spans); var result = await dataSender.SendDataAsync(spanBatch); Assert.True(actualDelayFromTestRun >= delayMs - errorMargin && actualDelayFromTestRun <= delayMs + errorMargin, $"Expected delay: {delayMs}, margin: +/-{errorMargin}, actual delay: {actualDelayFromTestRun}"); }
public void ToJson_EmptySpanBatch() { // Arrange var spanBatch = new NewRelicSpanBatch( spans: new NewRelicSpan[0], commonProperties: new NewRelicSpanBatchCommonProperties( traceId: "traceId")); // Act var jsonString = spanBatch.ToJson(); // Assert var resultSpanBatch = TestHelpers.DeserializeArrayFirst(jsonString); var resultCommonProps = TestHelpers.DeserializeObject(resultSpanBatch["common"]); TestHelpers.AssertForAttribValue(resultCommonProps, "trace.id", "traceId"); }
public void SendAnEmptySpanBatch() { var traceId = "123"; var spanBatch = new NewRelicSpanBatch( spans: new NewRelicSpan[0], commonProperties: new NewRelicSpanBatchCommonProperties(traceId)); var dataSender = new TraceDataSender(new TelemetryConfiguration() { ApiKey = "123456" }, null); dataSender.WithHttpHandlerImpl((serializedJson) => { var response = new HttpResponseMessage(System.Net.HttpStatusCode.OK); return(Task.FromResult(response)); }); var response = dataSender.SendDataAsync(spanBatch).Result; Assert.Equal(NewRelicResponseStatus.DidNotSend_NoData, response.ResponseStatus); }
public void ToJson_SpanBatchWithMultipleSpans() { var spanBatch = new NewRelicSpanBatch( commonProperties: new NewRelicSpanBatchCommonProperties( traceId: "traceId", attributes: new Dictionary <string, object>() { { "customAtt1", "hello" }, { "customAtt2", 1 }, { "customAtt3", 1.2 }, { "customAtt4", true }, }), spans: new NewRelicSpan[] { new NewRelicSpan( spanId: "span1", traceId: "traceId1", timestamp: 1L, parentSpanId: "parentId1", attributes: new Dictionary <string, object>() { { NewRelicConsts.Tracing.AttribNameDurationMs, 100 }, { NewRelicConsts.Tracing.AttribNameServiceName, "serviceName1" }, { NewRelicConsts.Tracing.AttribNameName, "name1" }, }), new NewRelicSpan( spanId: "span2", traceId: "traceId2", timestamp: 2L, parentSpanId: "parentId2", attributes: new Dictionary <string, object>() { { NewRelicConsts.Tracing.AttribNameDurationMs, 200 }, { NewRelicConsts.Tracing.AttribNameServiceName, "serviceName2" }, { NewRelicConsts.Tracing.AttribNameName, "name2" }, }), }); // Act var jsonString = spanBatch.ToJson(); // Assert var resultSpanBatches = TestHelpers.DeserializeArray(jsonString); TestHelpers.AssertForCollectionLength(resultSpanBatches, 1); var resultSpanBatch = resultSpanBatches.First(); var resultCommonProps = TestHelpers.DeserializeObject(resultSpanBatch["common"]); TestHelpers.AssertForAttribValue(resultCommonProps, "trace.id", "traceId"); var resultCommonPropAttribs = TestHelpers.DeserializeObject(resultCommonProps["attributes"]); TestHelpers.AssertForAttribCount(resultCommonPropAttribs, 4); TestHelpers.AssertForAttribValue(resultCommonPropAttribs, "customAtt1", "hello"); TestHelpers.AssertForAttribValue(resultCommonPropAttribs, "customAtt2", 1); TestHelpers.AssertForAttribValue(resultCommonPropAttribs, "customAtt3", 1.2); TestHelpers.AssertForAttribValue(resultCommonPropAttribs, "customAtt4", true); var resultSpans = TestHelpers.DeserializeArray(resultSpanBatch["spans"]); TestHelpers.AssertForCollectionLength(resultSpans, 2); var firstSpan = resultSpans[0]; TestHelpers.AssertForAttribValue(firstSpan, "id", "span1"); TestHelpers.AssertForAttribValue(firstSpan, "trace.id", "traceId1"); TestHelpers.AssertForAttribValue(firstSpan, "timestamp", 1); TestHelpers.AssertForAttribCount(firstSpan, 4); var firstSpanAttribs = TestHelpers.DeserializeObject(firstSpan["attributes"]); TestHelpers.AssertForAttribValue(firstSpanAttribs, "duration.ms", 100); TestHelpers.AssertForAttribValue(firstSpanAttribs, "name", "name1"); TestHelpers.AssertForAttribValue(firstSpanAttribs, "service.name", "serviceName1"); TestHelpers.AssertForAttribValue(firstSpanAttribs, "parent.id", "parentId1"); TestHelpers.AssertForAttribCount(firstSpanAttribs, 4); var secondSpan = resultSpans[1]; TestHelpers.AssertForAttribValue(secondSpan, "id", "span2"); TestHelpers.AssertForAttribValue(secondSpan, "trace.id", "traceId2"); TestHelpers.AssertForAttribValue(secondSpan, "timestamp", 2); TestHelpers.AssertForAttribCount(secondSpan, 4); var secondSpanAttribs = TestHelpers.DeserializeObject(secondSpan["attributes"]); TestHelpers.AssertForAttribValue(secondSpanAttribs, "duration.ms", 200); TestHelpers.AssertForAttribValue(secondSpanAttribs, "name", "name2"); TestHelpers.AssertForAttribValue(secondSpanAttribs, "service.name", "serviceName2"); TestHelpers.AssertForAttribValue(secondSpanAttribs, "parent.id", "parentId2"); TestHelpers.AssertForAttribCount(secondSpanAttribs, 4); }
public async Task RetryOn429WithDuration_429HappensOnce() { const int delayMS = 10000; const int expectedNumSendBatchAsyncCall = 2; var expectedBackoffSequenceFromTestRun = new List <uint>() { delayMS, }; var actualBackoffSequenceFromTestRun = new List <uint>(); var dataSender = new TraceDataSender(new TelemetryConfiguration() { ApiKey = "123456" }, null); dataSender.WithDelayFunction(async(uint milliSecondsDelay) => { actualBackoffSequenceFromTestRun.Add(milliSecondsDelay); await Task.Delay(0); return; }); var actualCountCallsSendData = 0; dataSender.WithCaptureSendDataAsyncDelegate((newRelicSpanBatch, retryNum) => { actualCountCallsSendData++; }); dataSender.WithHttpHandlerImpl((json) => { if (actualCountCallsSendData < 2) { var httpResponse = new HttpResponseMessage((HttpStatusCode)429); httpResponse.Headers.RetryAfter = new System.Net.Http.Headers.RetryConditionHeaderValue(TimeSpan.FromMilliseconds(delayMS)); return(Task.FromResult(httpResponse)); } return(Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK))); }); var spans = new List <NewRelicSpan>() { new NewRelicSpan( traceId: null, spanId: "Test Span", timestamp: 12345, parentSpanId: null, attributes: null), }; var spanBatch = new NewRelicSpanBatch(spans); var result = await dataSender.SendDataAsync(spanBatch); Assert.Equal(NewRelicResponseStatus.Success, result.ResponseStatus); Assert.Equal(expectedNumSendBatchAsyncCall, actualCountCallsSendData); Assert.Equal(expectedBackoffSequenceFromTestRun, actualBackoffSequenceFromTestRun); }
public async Task RequestTooLarge_SplitSuccess() { const int expectedCountSpans = 9; const int expectedCountCallsSendData = 7; const int expectedCountSuccessfulSpanBatches = 4; const string expectedTraceID = "TestTrace"; var actualCountCallsSendData = 0; // Arrange var successfulSpanBatches = new List <NewRelicSpanBatch>(); var dataSender = new TraceDataSender(new TelemetryConfiguration() { ApiKey = "123456" }, null); var okJsons = new List <string>(); // Mock the behavior to return EntityTooLarge for any span batch with 4 or more spans. // Anything with less than 4 will return success. dataSender.WithCaptureSendDataAsyncDelegate((spanBatch, retryNum) => { actualCountCallsSendData++; if (spanBatch.Spans.Count() < 4) { okJsons.Add(spanBatch.ToJson()); successfulSpanBatches.Add(spanBatch); } }); dataSender.WithHttpHandlerImpl((json) => { var response = okJsons.Contains(json) ? new HttpResponseMessage(System.Net.HttpStatusCode.OK) : new HttpResponseMessage(System.Net.HttpStatusCode.RequestEntityTooLarge); return(Task.FromResult(response)); }); var attribs = new Dictionary <string, object>() { { "testAttrib1", "testAttribValue1" }, }; var spans = new List <NewRelicSpan>(); for (var i = 0; i < expectedCountSpans; i++) { spans.Add(new NewRelicSpan(null, i.ToString(), i, null, null)); } var spanBatch = new NewRelicSpanBatch(spans, new NewRelicSpanBatchCommonProperties(expectedTraceID, attribs)); // Act await dataSender.SendDataAsync(spanBatch); // Assert Assert.Equal(expectedCountCallsSendData, actualCountCallsSendData); // Test the Spans Assert.Equal(expectedCountSuccessfulSpanBatches, successfulSpanBatches.Count); Assert.Equal(expectedCountSpans, successfulSpanBatches.SelectMany(x => x.Spans).Count()); Assert.Equal(expectedCountSpans, successfulSpanBatches.SelectMany(x => x.Spans).Select(x => x.Id).Distinct().Count()); // Test the attributes on the NewRelicSpanBatch Assert.Single(successfulSpanBatches.Select(x => x.CommonProperties.TraceId).Distinct()); Assert.Equal(expectedTraceID, successfulSpanBatches.FirstOrDefault().CommonProperties.TraceId); Assert.Single(successfulSpanBatches.Select(x => x.CommonProperties.Attributes).Distinct()); Assert.Equal(attribs, successfulSpanBatches.Select(x => x.CommonProperties.Attributes).FirstOrDefault()); }