public async Task AddsTraceKeepRateMetricToRootSpan()
        {
            // Traces should be dropped when both buffers are full
            var calculator = new MovingAverageKeepRateCalculator(windowSize: 10, Timeout.InfiniteTimeSpan);

            var tracer = new Mock <IDatadogTracer>();

            tracer.Setup(x => x.DefaultServiceName).Returns("Default");
            var traceContext    = new TraceContext(tracer.Object);
            var rootSpanContext = new SpanContext(null, traceContext, null);
            var rootSpan        = new Span(rootSpanContext, DateTimeOffset.UtcNow);
            var childSpan       = new Span(new SpanContext(rootSpanContext, traceContext, null), DateTimeOffset.UtcNow);

            traceContext.AddSpan(rootSpan);
            traceContext.AddSpan(childSpan);
            var trace       = new[] { rootSpan, childSpan };
            var sizeOfTrace = ComputeSizeOfTrace(trace);

            // Make the buffer size big enough for a single trace
            var api = new Mock <IApi>();

            api.Setup(x => x.SendTracesAsync(It.IsAny <ArraySegment <byte> >(), It.IsAny <int>()))
            .ReturnsAsync(() => true);
            var agent = new AgentWriter(api.Object, statsd: null, calculator, automaticFlush: false, maxBufferSize: (sizeOfTrace * 2) + SpanBuffer.HeaderSize - 1, batchInterval: 100);

            // Fill both buffers
            agent.WriteTrace(trace);
            agent.WriteTrace(trace);

            // Drop one
            agent.WriteTrace(trace);
            await agent.FlushTracesAsync(); // Force a flush to make sure the trace is written to the API

            // Write another one
            agent.WriteTrace(trace);
            await agent.FlushTracesAsync(); // Force a flush to make sure the trace is written to the API

            api.Verify();
            api.Invocations.Clear();

            // Write trace and update keep rate
            calculator.UpdateBucket();
            agent.WriteTrace(trace);
            await agent.FlushTracesAsync(); // Force a flush to make sure the trace is written to the API

            const double expectedTraceKeepRate = 0.75;

            rootSpan.SetMetric(Metrics.TracesKeepRate, expectedTraceKeepRate);
            var expectedData = Vendors.MessagePack.MessagePackSerializer.Serialize(trace, new FormatterResolverWrapper(SpanFormatterResolver.Instance));
            await agent.FlushAndCloseAsync();

            api.Verify(x => x.SendTracesAsync(It.Is <ArraySegment <byte> >(y => Equals(y, expectedData)), It.Is <int>(i => i == 1)), Times.Once);
        }
Exemplo n.º 2
0
        public void FullFlushShouldNotPropagateSamplingPriority()
        {
            const int partialFlushThreshold = 3;

            Span CreateSpan() => new Span(new SpanContext(42, SpanIdGenerator.ThreadInstance.CreateNew()), DateTimeOffset.UtcNow);

            var tracer = new Mock <IDatadogTracer>();

            tracer.Setup(t => t.Settings).Returns(new Trace.Configuration.TracerSettings
            {
                Exporter = new Trace.Configuration.ExporterSettings()
                {
                    PartialFlushEnabled  = true,
                    PartialFlushMinSpans = partialFlushThreshold
                }
            }.Build());

            ArraySegment <Span>?spans = null;

            tracer.Setup(t => t.Write(It.IsAny <ArraySegment <Span> >()))
            .Callback <ArraySegment <Span> >(s => spans = s);

            var traceContext = new TraceContext(tracer.Object);

            traceContext.SetSamplingPriority(SamplingPriorityValues.UserKeep);

            var rootSpan = CreateSpan();

            traceContext.AddSpan(rootSpan);

            for (int i = 0; i < partialFlushThreshold - 1; i++)
            {
                var span = CreateSpan();
                traceContext.AddSpan(span);
                traceContext.CloseSpan(span);
            }

            // At this point, only one span is missing to reach the threshold for partial flush
            spans.Should().BeNull("partial flush should not have been triggered");

            // Closing the root span brings the number of closed spans to the threshold
            // but a full flush should be triggered rather than a partial, because every span in the trace has been closed
            traceContext.CloseSpan(rootSpan);

            spans.Value.Should().NotBeNullOrEmpty("a full flush should have been triggered");

            rootSpan.GetMetric(Metrics.SamplingPriority).Should().Be(SamplingPriorityValues.UserKeep, "priority should be assigned to the root span");

            spans.Value.Should().OnlyContain(s => s == rootSpan || s.GetMetric(Metrics.SamplingPriority) == null, "only the root span should have a priority");
        }
Exemplo n.º 3
0
        public void PartialFlushShouldPropagateSamplingPriority()
        {
            const int partialFlushThreshold = 2;

            Span CreateSpan() => new Span(new SpanContext(42, SpanIdGenerator.ThreadInstance.CreateNew()), DateTimeOffset.UtcNow);

            var tracer = new Mock <IDatadogTracer>();

            tracer.Setup(t => t.Settings).Returns(new Trace.Configuration.TracerSettings
            {
                Exporter = new Trace.Configuration.ExporterSettings()
                {
                    PartialFlushEnabled  = true,
                    PartialFlushMinSpans = partialFlushThreshold
                }
            }.Build());

            ArraySegment <Span>?spans = null;

            tracer.Setup(t => t.Write(It.IsAny <ArraySegment <Span> >()))
            .Callback <ArraySegment <Span> >(s => spans = s);

            var traceContext = new TraceContext(tracer.Object);

            traceContext.SetSamplingPriority(SamplingPriorityValues.UserKeep);

            var rootSpan = CreateSpan();

            // Root span will stay open for the duration of the test
            traceContext.AddSpan(rootSpan);

            // Add enough child spans to trigger partial flush
            for (int i = 0; i < partialFlushThreshold; i++)
            {
                var span = CreateSpan();
                traceContext.AddSpan(span);
                traceContext.CloseSpan(span);
            }

            spans.Value.Should().NotBeNullOrEmpty("partial flush should have been triggered");

            spans.Value.Should().OnlyContain(s => (int)s.GetMetric(Metrics.SamplingPriority) == SamplingPriorityValues.UserKeep);
        }
Exemplo n.º 4
0
        public void FlushPartialTraces(bool partialFlush)
        {
            var tracer = new Mock <IDatadogTracer>();

            tracer.Setup(t => t.Settings).Returns(new Trace.Configuration.TracerSettings
            {
                Exporter = new Trace.Configuration.ExporterSettings()
                {
                    PartialFlushEnabled  = partialFlush,
                    PartialFlushMinSpans = 5
                }
            }.Build());

            var traceContext = new TraceContext(tracer.Object);

            void AddAndCloseSpan()
            {
                var span = new Span(new SpanContext(42, SpanIdGenerator.ThreadInstance.CreateNew()), DateTimeOffset.UtcNow);

                traceContext.AddSpan(span);
                traceContext.CloseSpan(span);
            }

            var rootSpan = new Span(new SpanContext(42, SpanIdGenerator.ThreadInstance.CreateNew()), DateTimeOffset.UtcNow);

            traceContext.AddSpan(rootSpan);

            for (int i = 0; i < 4; i++)
            {
                AddAndCloseSpan();
            }

            // At this point in time, we have 4 closed spans in the trace
            tracer.Verify(t => t.Write(It.IsAny <ArraySegment <Span> >()), Times.Never);

            AddAndCloseSpan();

            // Now we have 5 closed spans, partial flush should kick-in if activated
            if (partialFlush)
            {
                tracer.Verify(t => t.Write(It.Is <ArraySegment <Span> >(s => s.Count == 5)), Times.Once);
            }
            else
            {
                tracer.Verify(t => t.Write(It.IsAny <ArraySegment <Span> >()), Times.Never);
            }

            for (int i = 0; i < 5; i++)
            {
                AddAndCloseSpan();
            }

            // We have 5 more closed spans, partial flush should kick-in a second time if activated
            if (partialFlush)
            {
                tracer.Verify(t => t.Write(It.Is <ArraySegment <Span> >(s => s.Count == 5)), Times.Exactly(2));
            }
            else
            {
                tracer.Verify(t => t.Write(It.IsAny <ArraySegment <Span> >()), Times.Never);
            }

            traceContext.CloseSpan(rootSpan);

            // Now the remaining spans are flushed
            if (partialFlush)
            {
                tracer.Verify(t => t.Write(It.Is <ArraySegment <Span> >(s => s.Count == 1)), Times.Once);
            }
            else
            {
                tracer.Verify(t => t.Write(It.Is <ArraySegment <Span> >(s => s.Count == 11)), Times.Once);
            }
        }