public void StartChildSpan_SampledLinkedParent() { var rootSpanUnsampled = SpanBuilder.Create(SPAN_NAME, SpanKind.Internal, (ISpan)null, spanBuilderOptions) .SetSampler(Samplers.NeverSample) .StartSpan(); Assert.False(rootSpanUnsampled.Context.TraceOptions.IsSampled); var rootSpanSampled = SpanBuilder.Create(SPAN_NAME, SpanKind.Internal, (ISpan)null, spanBuilderOptions) .SetSampler(Samplers.AlwaysSample) .StartSpan(); Assert.True(rootSpanSampled.Context.TraceOptions.IsSampled); // Sampled because the linked parent is sampled. ISpan childSpan = SpanBuilder.Create(SPAN_NAME, SpanKind.Internal, rootSpanUnsampled, spanBuilderOptions) .SetParentLinks(new List <ISpan>() { rootSpanSampled }) .StartSpan(); Assert.True(childSpan.Context.IsValid); Assert.Equal(rootSpanUnsampled.Context.TraceId, childSpan.Context.TraceId); Assert.True(childSpan.Context.TraceOptions.IsSampled); }
async public Task SendDataAsyncThrowsHttpException() { const int expectedNumSendBatchAsyncCall = 1; var dataSender = new SpanDataSender(new TelemetryConfiguration().WithApiKey("123456")); dataSender.WithDelayFunction(async(uint milliSecondsDelay) => { await Task.Delay(0); return; }); var actualCountCallsSendData = 0; dataSender.WithCaptureSendDataAsyncDelegate((spanBatch, retryNum) => { actualCountCallsSendData++; }); dataSender.WithHttpHandlerImpl((json) => { throw new Exception("Server Error", new Exception("Inner exception message")); }); var spanBatch = SpanBatchBuilder.Create() .WithSpan(SpanBuilder.Create("Test Span").Build()) .Build(); var result = await dataSender.SendDataAsync(spanBatch); Assert.AreEqual(NewRelicResponseStatus.Failure, result.ResponseStatus); Assert.AreEqual("Inner exception message", result.Message); Assert.IsNull(result.HttpStatusCode); Assert.AreEqual(expectedNumSendBatchAsyncCall, actualCountCallsSendData, "Unexpected Number of SendDataAsync calls"); }
public void BuildSpan() { var attributes = new Dictionary <string, object> { { "attrKey", "attrValue" } }; var spanBuilder = SpanBuilder.Create("spanId") .WithTraceId("traceId") .WithTimestamp(1L) .WithServiceName("serviceName") .WithDurationMs(67) .WithName("name") .WithParentId("parentId") .HasError(true) .WithAttribute("adsfasdf", 12) .WithAttributes(attributes); var span = spanBuilder.Build(); Assert.AreEqual("spanId", span.Id); Assert.AreEqual("traceId", span.TraceId); Assert.AreEqual(1L, span.Timestamp); Assert.AreEqual("serviceName", span.Attributes["service.name"]); Assert.AreEqual(true, span.Attributes["error"]); Assert.AreEqual(67, span.Attributes["duration.ms"]); Assert.AreEqual("name", span.Attributes["name"]); Assert.AreEqual("parentId", span.Attributes["parent.id"]); Assert.AreEqual("attrValue", span.Attributes["attrKey"]); }
async public 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(50).TotalMilliseconds; var actualResponseFromTestRun = new List <Response>(); uint actualDelayFromTestRun = 0; var dataSender = new SpanDataSender(new TelemetryConfiguration().WithApiKey("123456").WithMaxRetryAttempts(1)); 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 spanBatch = SpanBatchBuilder.Create() .WithSpan(SpanBuilder.Create("Test Span").Build()) .Build(); var response = await dataSender.SendDataAsync(spanBatch); Assert.IsTrue(actualDelayFromTestRun >= delayMs - errorMargin && actualDelayFromTestRun <= delayMs + errorMargin, $"Expected delay: {delayMs}, margin: +/-{errorMargin}, actual delay: {actualDelayFromTestRun}"); }
/// <summary> /// In this example, all of the spans are for the same trace. Accordingly, the /// TraceId is set in the SpanBatchBuilder. /// </summary> private static async Task Example_SpanBatchForSingleTrace() { var traceId = System.Guid.NewGuid().ToString(); // The SpanBatchBuilder manages a collection of Spans var spanBatchBuilder = SpanBatchBuilder.Create(); // Since all of the spans in this batch represent a single // execution trace, set the TraceId on the SpanBatch instead // of on the individual spans. spanBatchBuilder.WithTraceId(traceId); // Perform 10 units of work as part of this trace/spanBatch for (var spanIdx = 0; spanIdx < 5; spanIdx++) { // The SpanBuilder is used to crate a new span. // Create a new SpanBuilder assigning it a random guid as the spanId var spanBuilder = SpanBuilder.Create(Guid.NewGuid().ToString()); //Add a name to the span to better understand it in the New Relic UI. spanBuilder.WithName($"{traceId} - {spanIdx}"); // Capture the start time for later use in calculating duration. var startTime = DateTime.UtcNow; 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. spanBuilder.HasError(true); spanBuilder.WithAttribute("Exception", ex); } finally { // Calculate the duration of execution and record it var endTime = DateTime.UtcNow; spanBuilder.WithExecutionTimeInfo(startTime, endTime); //Obtain the completed Span from the SpanBuilder var span = spanBuilder.Build(); //Attach the span to the Span Batch. spanBatchBuilder.WithSpan(span); } } // Obtain the SpanBatch from the SpanBatchBuilder var spanBatch = spanBatchBuilder.Build(); // Send the SpanBatch to the New Relic endpoint. await SendDataToNewRelic(spanBatch); }
public void StartRootSpan_WithoutSpecifiedSampler() { // Apply default sampler (always true in the tests) for root spans. var rootSpan = SpanBuilder.Create(SPAN_NAME, SpanKind.Internal, (ISpan)null, spanBuilderOptions).StartSpan(); Assert.True(rootSpan.Context.IsValid); Assert.True(rootSpan.Context.TraceOptions.IsSampled); }
async public Task RequestTooLarge_SplitFail() { const int expectedCountCallsSendData = 7; const int expectedCountSuccessfulSpanBatches = 1; const string traceID_Success = "OK"; const string traceID_SplitBatch_Prefix = "TooLarge"; var actualCountCallsSendData = 0; var successfulSpans = new List <Span>(); var dataSender = new SpanDataSender(new TelemetryConfiguration().WithApiKey("123456")); var shouldSplitJsons = new List <string>(); // Mock the behavior to return EntityTooLarge for any span batch that has a span with an // id that starts with TooLarge. dataSender.WithCaptureSendDataAsyncDelegate((spanBatch, retryNum) => { actualCountCallsSendData++; if (spanBatch.Spans.Any(x => x.Id.StartsWith(traceID_SplitBatch_Prefix))) { shouldSplitJsons.Add(spanBatch.ToJson()); } else { successfulSpans.AddRange(spanBatch.Spans); } }); dataSender.WithHttpHandlerImpl((json) => { var response = shouldSplitJsons.Contains(json) ? new HttpResponseMessage(System.Net.HttpStatusCode.RequestEntityTooLarge) : new HttpResponseMessage(System.Net.HttpStatusCode.OK); return(Task.FromResult(response)); }); var spanBatchBuilder = SpanBatchBuilder.Create(); spanBatchBuilder.WithSpan(SpanBuilder.Create($"{traceID_SplitBatch_Prefix}1").Build()); spanBatchBuilder.WithSpan(SpanBuilder.Create($"{traceID_SplitBatch_Prefix}2").Build()); spanBatchBuilder.WithSpan(SpanBuilder.Create($"{traceID_SplitBatch_Prefix}3").Build()); spanBatchBuilder.WithSpan(SpanBuilder.Create(traceID_Success).Build()); var spanBatch = spanBatchBuilder.Build(); // Act var result = await dataSender.SendDataAsync(spanBatch); // Assert Assert.AreEqual(NewRelicResponseStatus.Failure, result.ResponseStatus); Assert.AreEqual(expectedCountCallsSendData, actualCountCallsSendData, "Unexpected number of calls"); Assert.AreEqual(expectedCountSuccessfulSpanBatches, successfulSpans.Count, $"Only {expectedCountSuccessfulSpanBatches} span should have successfully sent"); Assert.AreEqual(traceID_Success, successfulSpans[0].Id, "Incorrect span was sent"); }
/// <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() { var spanBatchBuilder = SpanBatchBuilder.Create(); for (var traceIdx = 0; traceIdx < 3; traceIdx++) { var traceId = Guid.NewGuid().ToString(); for (var spanIdx = 0; spanIdx < 5; spanIdx++) { var spanBuilder = SpanBuilder.Create(Guid.NewGuid().ToString()); // Since multiple traces will be reported in the same SpanBatch, // the TraceID needs to be attached to the individual spans. spanBuilder.WithTraceId(traceId) .WithName($"{traceId} - {spanIdx}"); // Capture the start time for later use in calculating duration. var startTime = DateTime.UtcNow; try { // Attempt to perform a unit of work DoWork($"Hello Outer Space Trace={traceId}, Span={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. spanBuilder.HasError(true); spanBuilder.WithAttribute("Exception", ex); } finally { // Calculate the duration of execution and record it var endTime = DateTime.UtcNow; spanBuilder.WithExecutionTimeInfo(startTime, endTime); //Obtain the completed Span from the SpanBuilder var span = spanBuilder.Build(); //Attach the span to the Span Batch. spanBatchBuilder.WithSpan(span); } } Console.WriteLine(); } // Obtain the SpanBatch from the SpanBatchBuilder var spanBatch = spanBatchBuilder.Build(); // Send the SpanBatch to the New Relic endpoint. await SendDataToNewRelic(spanBatch); }
public void StartSpanNullParentNoRecordOptions() { var span = SpanBuilder.Create(SPAN_NAME, SpanKind.Internal, (ISpan)null, spanBuilderOptions) .SetSampler(Samplers.NeverSample) .StartSpan(); Assert.True(span.Context.IsValid); Assert.False(span.IsRecordingEvents); Assert.False(span.Context.TraceOptions.IsSampled); }
public void StartRootSpan_WithSpecifiedSampler() { // Apply given sampler before default sampler for root spans. var rootSpan = SpanBuilder.Create(SPAN_NAME, SpanKind.Internal, (ISpan)null, spanBuilderOptions) .SetSampler(Samplers.NeverSample) .StartSpan(); Assert.True(rootSpan.Context.IsValid); Assert.False(rootSpan.Context.TraceOptions.IsSampled); }
public void StartSpan_NullParent() { var span = SpanBuilder.Create(SPAN_NAME, SpanKind.Internal, (SpanContext)null, spanBuilderOptions).StartSpan(); Assert.True(span.Context.IsValid); Assert.True(span.IsRecordingEvents); Assert.True(span.Context.TraceOptions.IsSampled); ISpanData spanData = ((Span)span).ToSpanData(); Assert.Null(spanData.ParentSpanId); }
async public Task RetryOn429_RetriesExceeded() { const int delayMS = 10000; const int expectedNumSendBatchAsyncCall = 9; // 1 first call + 3 calls from retries var expectedBackoffSequenceFromTestRun = new List <int>() { delayMS, delayMS, delayMS, delayMS, delayMS, delayMS, delayMS, delayMS }; var actualBackoffSequenceFromTestRun = new List <uint>(); var dataSender = new SpanDataSender(new TelemetryConfiguration().WithApiKey("123456")); dataSender.WithDelayFunction(async(uint milliSecondsDelay) => { actualBackoffSequenceFromTestRun.Add(milliSecondsDelay); await Task.Delay(0); return; }); var actualCountCallsSendData = 0; dataSender.WithCaptureSendDataAsyncDelegate((spanBatch, retryNum) => { actualCountCallsSendData++; }); dataSender.WithHttpHandlerImpl((json) => { var httpResponse = new HttpResponseMessage((HttpStatusCode)429); httpResponse.Headers.RetryAfter = new System.Net.Http.Headers.RetryConditionHeaderValue(TimeSpan.FromMilliseconds(delayMS)); return(Task.FromResult(httpResponse)); }); var spanBatch = SpanBatchBuilder.Create() .WithSpan(SpanBuilder.Create("Test Span").Build()) .Build(); var result = await dataSender.SendDataAsync(spanBatch); Assert.AreEqual(NewRelicResponseStatus.Failure, result.ResponseStatus); Assert.AreEqual(expectedNumSendBatchAsyncCall, actualCountCallsSendData, "Unexpected Number of SendDataAsync calls"); CollectionAssert.AreEqual(expectedBackoffSequenceFromTestRun, actualBackoffSequenceFromTestRun); }
public void SimpleStrongAndItalicSpan() { Span builded = SpanBuilder .Create() .Write("Lorem ") .Strong("ipsum") .Write(" ") .Italic("dolor") .Write(" sit amet.") .Span; Assert.Equal("Lorem **ipsum** *dolor* sit amet.", builded.ToString()); }
/// <example> /// SpanBuilder /// .Create() /// .Write("Lorem ipsum ") /// .StartStrongContext() /// .Write("dolor sit amet, ") /// .Strikethrough("consectetur") /// .Write(" adipiscing ") /// .Italic("elit") /// .EndContext() /// .Write("."); /// </example> public static ISpanBuilder FluentWay() { return(SpanBuilder .Create() .Write("Lorem ipsum ") .StartStrongContext() .Write("dolor sit amet, ") .Strikethrough("consectetur") .Write(" adipiscing ") .Italic("elit") .EndContext() .Write(".")); }
async public Task RetryBackoffSequence_IntermittentTimeoutEventuallySucceeds() { var expectedNumSendBatchAsyncCall = 4; // 1 first call + 3 calls from retries var expectedBackoffSequenceFromTestRun = new List <int>() { 5000, 10000, 20000, }; var actualBackoffSequenceFromTestRun = new List <uint>(); var dataSender = new SpanDataSender(new TelemetryConfiguration().WithApiKey("123456")); dataSender.WithDelayFunction(async(uint milliSecondsDelay) => { actualBackoffSequenceFromTestRun.Add(milliSecondsDelay); await Task.Delay(0); return; }); var actualCountCallsSendData = 0; dataSender.WithCaptureSendDataAsyncDelegate((spanBatch, retryNum) => { actualCountCallsSendData++; }); var callCount = 0; dataSender.WithHttpHandlerImpl((json) => { callCount++; if (callCount < 4) { return(Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.RequestTimeout))); } return(Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK))); }); var spanBatch = SpanBatchBuilder.Create() .WithSpan(SpanBuilder.Create("Test Span").Build()) .Build(); var result = await dataSender.SendDataAsync(spanBatch); Assert.AreEqual(NewRelicResponseStatus.Success, result.ResponseStatus); Assert.AreEqual(expectedNumSendBatchAsyncCall, actualCountCallsSendData, "Unexpected Number of SendDataAsync calls"); CollectionAssert.AreEqual(expectedBackoffSequenceFromTestRun, actualBackoffSequenceFromTestRun); }
public void StartSpanNullParent() { var span = SpanBuilder.Create(SPAN_NAME, SpanKind.Internal, (ISpan)null, spanBuilderOptions).StartSpan(); Assert.True(span.Context.IsValid); Assert.True(span.IsRecordingEvents); Assert.True(span.Context.TraceOptions.IsSampled); ISpanData spanData = ((Span)span).ToSpanData(); Assert.Null(spanData.ParentSpanId); Assert.InRange(spanData.StartTimestamp, Timestamp.FromDateTimeOffset(DateTimeOffset.Now).AddDuration(Duration.Create(-1, 0)), Timestamp.FromDateTimeOffset(DateTimeOffset.Now).AddDuration(Duration.Create(1, 0))); Assert.Equal(SPAN_NAME, spanData.Name); }
public void ToJson_NonEmptySpanBatch() { // Arrange var spanBatch = SpanBatchBuilder.Create() .WithTraceId("traceId") .WithSpan(SpanBuilder.Create("span1") .WithTraceId("traceId") .WithTimestamp(1L) .WithServiceName("serviceName") .WithDurationMs(67) .WithName("name") .WithParentId("parentId") .HasError(true).Build()) .Build(); // 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.FirstOrDefault(); 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.AssertForAttribValue(resultSpanAttribs, "error", true); TestHelpers.AssertForAttribCount(resultSpanAttribs, 5); }
public void StartSpanNullParentWithRecordEvents() { var span = SpanBuilder.Create(SPAN_NAME, SpanKind.Internal, (ISpan)null, spanBuilderOptions) .SetSampler(Samplers.NeverSample) .SetRecordEvents(true) .StartSpan(); Assert.True(span.Context.IsValid); Assert.True(span.IsRecordingEvents); Assert.False(span.Context.TraceOptions.IsSampled); ISpanData spanData = ((Span)span).ToSpanData(); Assert.Null(spanData.ParentSpanId); }
public void StartChildSpan() { var rootSpan = SpanBuilder.Create(SPAN_NAME, SpanKind.Internal, (ISpan)null, spanBuilderOptions).StartSpan(); Assert.True(rootSpan.Context.IsValid); Assert.True(rootSpan.IsRecordingEvents); Assert.True(rootSpan.Context.TraceOptions.IsSampled); ISpan childSpan = SpanBuilder.Create(SPAN_NAME, SpanKind.Internal, rootSpan, spanBuilderOptions).StartSpan(); Assert.True(childSpan.Context.IsValid); Assert.Equal(rootSpan.Context.TraceId, childSpan.Context.TraceId); Assert.Equal(rootSpan.Context.SpanId, ((Span)childSpan).ToSpanData().ParentSpanId); Assert.Equal(((Span)rootSpan).TimestampConverter, ((Span)childSpan).TimestampConverter); }
public void StartChildSpan_WithoutSpecifiedSampler() { var rootSpan = SpanBuilder.Create(SPAN_NAME, SpanKind.Internal, (ISpan)null, spanBuilderOptions) .SetSampler(Samplers.NeverSample) .StartSpan(); Assert.True(rootSpan.Context.IsValid); Assert.False(rootSpan.Context.TraceOptions.IsSampled); // Don't apply the default sampler (always true) for child spans. ISpan childSpan = SpanBuilder.Create(SPAN_NAME, SpanKind.Internal, rootSpan, spanBuilderOptions).StartSpan(); Assert.True(childSpan.Context.IsValid); Assert.Equal(rootSpan.Context.TraceId, childSpan.Context.TraceId); Assert.False(childSpan.Context.TraceOptions.IsSampled); }
async public Task SendDataAsyncThrowsNonHttpException() { const int expectedNumSendBatchAsyncCall = 1; const int expectedNumHttpHandlerCall = 0; var dataSender = new SpanDataSender(new TelemetryConfiguration().WithAPIKey("123456")); dataSender.WithDelayFunction(async(int 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 spanBatch = SpanBatchBuilder.Create() .WithSpan(SpanBuilder.Create("Test Span").Build()) .Build(); var result = await dataSender.SendDataAsync(spanBatch); Assert.AreEqual(NewRelicResponseStatus.Failure, result.ResponseStatus); Assert.AreEqual("Test Exception", result.Message); Assert.IsNull(result.HttpStatusCode); Assert.AreEqual(expectedNumSendBatchAsyncCall, actualCountCallsSendData, "Unexpected Number of SendDataAsync calls"); Assert.AreEqual(expectedNumHttpHandlerCall, actualCallsHttpHandler, "Unexpected Number of Http Handler calls"); }
public void StartRemoteChildSpan_WithoutSpecifiedSampler() { var rootSpan = SpanBuilder.Create(SPAN_NAME, SpanKind.Internal, (ISpan)null, spanBuilderOptions) .SetSampler(Samplers.NeverSample) .StartSpan(); Assert.True(rootSpan.Context.IsValid); Assert.False(rootSpan.Context.TraceOptions.IsSampled); // Apply default sampler (always true in the tests) for spans with remote parent. ISpan childSpan = SpanBuilder.Create(SPAN_NAME, SpanKind.Internal, rootSpan.Context, spanBuilderOptions) .StartSpan(); Assert.True(childSpan.Context.IsValid); Assert.Equal(rootSpan.Context.TraceId, childSpan.Context.TraceId); Assert.False(childSpan.Context.TraceOptions.IsSampled); }
public void StartRemoteSpan() { SpanContext spanContext = SpanContext.Create( TraceId.GenerateRandomId(randomHandler), SpanId.GenerateRandomId(randomHandler), TraceOptions.Default, Tracestate.Empty); ISpan span = SpanBuilder.Create(SPAN_NAME, SpanKind.Internal, spanContext, spanBuilderOptions) .SetRecordEvents(true) .StartSpan(); Assert.True(span.Context.IsValid); Assert.Equal(spanContext.TraceId, span.Context.TraceId); Assert.False(span.Context.TraceOptions.IsSampled); ISpanData spanData = ((Span)span).ToSpanData(); Assert.Equal(spanContext.SpanId, spanData.ParentSpanId); }
private static IBlock BuildParameterInfo(Parameter parameter) { Section result = new Section( new Header(parameter.Documentation.Title ?? parameter.Key)); if (!string.IsNullOrEmpty(parameter.ArgumentTemplate)) { result = result.AddChild(new Paragraph( SpanBuilder.Create() .Write("Regular expression: ") .Italic($"/{parameter.ArgumentTemplate}/"))); } if (!string.IsNullOrEmpty(parameter.Documentation?.Description)) { result = result.AddChild(new Paragraph(parameter.Documentation.Description)); } return(result); }
public void DateSpecificRetry_CorrectDelayDuration() { var traceId = "123"; var retryDuration = TimeSpan.FromSeconds(10); var retryDurationMs = retryDuration.TotalMilliseconds; var errorMargin = TimeSpan.FromMilliseconds(50).TotalMilliseconds; var spanBatch = SpanBatchBuilder.Create() .WithTraceId(traceId) .WithSpan(SpanBuilder.Create("TestSpan").Build()) .Build(); var config = new TelemetryConfiguration().WithAPIKey("12345"); var dataSender = new SpanDataSender(config); //Mock out the communication layer dataSender.WithHttpHandlerImpl((serializedJson) => { var response = new HttpResponseMessage((System.Net.HttpStatusCode) 429); var retryOnSpecificTime = DateTimeOffset.Now + retryDuration; response.Headers.RetryAfter = new System.Net.Http.Headers.RetryConditionHeaderValue(retryOnSpecificTime); return(Task.FromResult(response)); }); var capturedDelays = new List <int>(); dataSender.WithDelayFunction((delay) => { capturedDelays.Add(delay); return(Task.Delay(0)); }); var response = dataSender.SendDataAsync(spanBatch).Result; Assert.AreEqual(NewRelicResponseStatus.Failure, response.ResponseStatus); Assert.AreEqual(config.MaxRetryAttempts, capturedDelays.Count); Assert.AreEqual(System.Net.HttpStatusCode.RequestTimeout, response.HttpStatusCode); Assert.IsTrue(capturedDelays.All(x => x >= retryDurationMs - errorMargin && x <= retryDurationMs + errorMargin), "Expected duration out of range"); }
public void SendANonEmptySpanBatch() { var traceId = "123"; var spanBatch = SpanBatchBuilder.Create() .WithTraceId(traceId) .WithSpan(SpanBuilder.Create("TestSpan").Build()) .Build(); var dataSender = new SpanDataSender(new TelemetryConfiguration().WithAPIKey("123456")); dataSender.WithHttpHandlerImpl((serializedJson) => { var response = new HttpResponseMessage(System.Net.HttpStatusCode.OK); return(Task.FromResult(response)); }); var response = dataSender.SendDataAsync(spanBatch).Result; Assert.AreEqual(NewRelicResponseStatus.Success, response.ResponseStatus); }
public void ThrowExceptionIfNullId() { Assert.Throws <NullReferenceException>(new TestDelegate(() => SpanBuilder.Create(null))); }
/// <summary> /// Wraps a unit of work and records it as a span. It tracking calculates duration and handles exceptions. /// Upon completion, if this the topmost unit of work, the spans will be sent to the New Relic endpoint as /// a SpanBatch/Trace. /// </summary> /// <param name="action">The work to be executed and tracked as a span</param> public static void TrackWork(string name, Action action) { //If Tracing is not enabled, just invoke action and return if (!_isTracingEnabled) { action.Invoke(); return; } var parentSpan = _currentSpan.Value; var newSpanId = Guid.NewGuid().ToString(); var thisSpan = SpanBuilder.Create(newSpanId); if (!string.IsNullOrWhiteSpace(name)) { thisSpan.WithAttribute("Name", name); } _currentSpan.Value = thisSpan; Console.WriteLine($"{"SIMPLE TRACER: Span Started",-30}: Span={newSpanId}, Parent={(parentSpan == null ? "<TOPMOST ITEM>": parentSpan.SpanId)} - {name??"No Name"}"); // If this unit of work is part of a larger unit of work, // this will associate it as a child span of the larger span. if (parentSpan != null) { thisSpan.WithParentId(parentSpan.SpanId); } // collect the start timestamp var startTime = DateTime.UtcNow; try { action?.Invoke(); } catch (Exception ex) { Console.WriteLine($"{"SIMPLE TRACER: Error Detected",-30}: Span={newSpanId}, Parent={(parentSpan == null ? "<TOPMOST ITEM>" : parentSpan.SpanId)} - {name ?? "No Name"} - {ex.Message}"); // In the event an unhandled exception occurs, mark the span as "HasError" // and record the exception. thisSpan.HasError(true); thisSpan.WithAttribute("Exception", ex); // Rethrow the exception for the caller so as to not change the execution path of the application. throw; } finally { // In all cases, record the execution duration thisSpan.WithExecutionTimeInfo(startTime, DateTime.UtcNow); // Attach the span to the SpanBatch (Trace). var spanBatch = _currentSpanBatch.Value.WithSpan(thisSpan.Build()); Console.WriteLine($"{"SIMPLE TRACER: Span Completed",-30}: Span={newSpanId}, Parent={(parentSpan == null ? "<TOPMOST ITEM>" : parentSpan.SpanId)} - {name ?? "No Name"}"); // If this is a topmost unit of work, send the trace to the New Relic endpoint. if (parentSpan == null) { _currentSpan.Value = null; _currentSpanBatch.Value = null; var sb = spanBatch.Build(); Console.WriteLine($"{"SIMPLE TRACER: Trace Completed",-30}: {sb.CommonProperties.TraceId}"); SendDataToNewRelic(sb).Wait(); } else { _currentSpan.Value = parentSpan; } } }
public void StartRemoteChildSpan_WithProbabilitySamplerDefaultSampler() { var configMock = Mock.Get <ITraceConfig>(traceConfig); configMock.Setup((c) => c.ActiveTraceParams).Returns(TraceParams.Default); // This traceId will not be sampled by the ProbabilitySampler because the first 8 bytes as long // is not less than probability * Long.MAX_VALUE; ITraceId traceId = TraceId.FromBytes( new byte[] { 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, }); // If parent is sampled then the remote child must be sampled. ISpan childSpan = SpanBuilder.Create( SPAN_NAME, SpanKind.Internal, SpanContext.Create( traceId, SpanId.GenerateRandomId(randomHandler), TraceOptions.Builder().SetIsSampled(true).Build(), Tracestate.Empty), spanBuilderOptions) .StartSpan(); Assert.True(childSpan.Context.IsValid); Assert.Equal(traceId, childSpan.Context.TraceId); Assert.True(childSpan.Context.TraceOptions.IsSampled); childSpan.End(); Assert.Equal(TraceParams.Default, traceConfig.ActiveTraceParams); // If parent is not sampled then the remote child must be not sampled. childSpan = SpanBuilder.Create( SPAN_NAME, SpanKind.Internal, SpanContext.Create( traceId, SpanId.GenerateRandomId(randomHandler), TraceOptions.Default, Tracestate.Empty), spanBuilderOptions) .StartSpan(); Assert.True(childSpan.Context.IsValid); Assert.Equal(traceId, childSpan.Context.TraceId); Assert.False(childSpan.Context.TraceOptions.IsSampled); childSpan.End(); }
public async Task <IEnumerable <WeatherForecast> > Get() { // The SpanBuilder is a tool to help Build spans. Each span must have // a unique identifier. In this example, we are using a Guid. var spanId = Guid.NewGuid().ToString(); var spanBuilder = SpanBuilder.Create(spanId); // We can add additional attribution to a span using helper functions. // In this case a timestamp and the controller action name are recorded spanBuilder.WithTimestamp(DateTimeOffset.UtcNow) .WithName("WeatherForecast/Get"); // Wrapping the unit of work inside a try/catch is helpful to ensure that // spans are always reported to the endpoint, even if they have exceptions. try { // This is the unit of work being tracked by the span. var rng = new Random(); var result = Enumerable.Range(1, 5) .Select(index => new WeatherForecast() { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }) .ToArray(); return(result); } // If an unhandled exception occurs, it can be denoted on the span. catch (Exception ex) { // In the event of an exception spanBuilder.HasError(true); spanBuilder.WithAttribute("Exception", ex); //This ensures that tracking of spans doesn't interfere with the normal execution flow throw; } // In all cases, the span is sent up to the New Relic endpoint. finally { // Obtain the span from the SpanBuilder. var span = spanBuilder.Build(); // The SpanBatchBuilder is a tool to help create SpanBatches // Create a new SpanBatchBuilder and associate the span to it. var spanBatchBuilder = SpanBatchBuilder.Create() .WithSpan(span); // Since this SpanBatch represents a single trace, identify // the TraceId for the entire batch. spanBatchBuilder.WithTraceId(Guid.NewGuid().ToString()); // Obtain the spanBatch from the builder var spanBatch = spanBatchBuilder.Build(); // Send it to the New Relic endpoint. var newRelicResult = await _spanDataSender.SendDataAsync(spanBatch); if (newRelicResult.ResponseStatus == NewRelicResponseStatus.Failure) { _logger.LogWarning("There was a problem sending the SpanBatch to New Relic endpoint"); } } }