Exemple #1
0
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();


            var appInsightsConfig = app.ApplicationServices.GetService <TelemetryConfiguration>();

            TelemetryProcessorChainBuilder builder = appInsightsConfig.TelemetryProcessorChainBuilder;

            // some custom telemetry processor for Application Insights that filters out synthetic traffic (e.g traffic that comes from Azure to keep the server awake).
            builder.UseAdaptiveSampling(excludedTypes: "Exception");
            builder.UseAdaptiveSampling(maxTelemetryItemsPerSecond: 10000);
            builder.Build();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
Exemple #2
0
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            //ApplicationInsight

            //Initializers:
            TelemetryConfiguration.Active.TelemetryInitializers.Add(new HttpTelemetryInitializer());
            TelemetryConfiguration.Active.TelemetryInitializers.Add(new HttpRequestTelemetryInitializer());
            TelemetryConfiguration.Active.TelemetryInitializers.Add(new WebMethodInitializer());

            //Procesors:
            TelemetryProcessorChainBuilder builder = TelemetryConfiguration.Active.DefaultTelemetrySink.TelemetryProcessorChainBuilder;

            builder.Use((next) => new TelemetryUnAuthorizedFilter(next));
            builder.Use((next) => new TelemetryPathFilter(next));
            builder.Use((next) => new TelemetryBotFilter(next));
            builder.Use((next) => new TelemetryFastRemoteDependencyCallsFilter(next));
            builder.Use((next) => new SuccessfulDependencyFilter(next));

            builder.Build();
        }
Exemple #3
0
        private static TelemetryProcessorChain CreateTelemetryProcessorChainWithSampling(IList <ITelemetry> sentTelemetry, double samplingPercentage, string excludedTypes = null, string includedTypes = null)
        {
            var tc = new TelemetryConfiguration {
                TelemetryChannel = new StubTelemetryChannel()
            };

            tc.InstrumentationKey = Guid.NewGuid().ToString("D");

            var channelBuilder = new TelemetryProcessorChainBuilder(tc);

            channelBuilder.UseSampling(samplingPercentage, excludedTypes, includedTypes);
            channelBuilder.Use(next => new StubTelemetryProcessor(next)
            {
                OnProcess = t => sentTelemetry.Add(t)
            });

            channelBuilder.Build();

            TelemetryProcessorChain processors = tc.TelemetryProcessorChain;

            foreach (ITelemetryProcessor processor in processors.TelemetryProcessors)
            {
                ITelemetryModule m = processor as ITelemetryModule;
                if (m != null)
                {
                    m.Initialize(tc);
                }
            }

            return(processors);
        }
        internal static TelemetryConfiguration CreateTelemetryConfigWithExtractor(IList <ITelemetry> telemetrySentToChannel,
                                                                                  Func <ITelemetryProcessor, AutocollectedMetricsExtractor> extractorFactory)
        {
            ITelemetryChannel channel = new StubTelemetryChannel {
                OnSend = (t) => telemetrySentToChannel.Add(t)
            };
            string iKey = Guid.NewGuid().ToString("D");
            TelemetryConfiguration telemetryConfig = new TelemetryConfiguration(iKey, channel);

            var channelBuilder = new TelemetryProcessorChainBuilder(telemetryConfig);

            channelBuilder.Use(extractorFactory);
            channelBuilder.Build();

            TelemetryProcessorChain processors = telemetryConfig.TelemetryProcessorChain;

            foreach (ITelemetryProcessor processor in processors.TelemetryProcessors)
            {
                ITelemetryModule m = processor as ITelemetryModule;
                if (m != null)
                {
                    m.Initialize(telemetryConfig);
                }
            }


            return(telemetryConfig);
        }
Exemple #5
0
        public static TelemetryConfiguration CreateAITelemetryConfig(out IList <ITelemetry> telemetrySentToChannel)
        {
            StubApplicationInsightsTelemetryChannel channel = new StubApplicationInsightsTelemetryChannel();
            string iKey = Guid.NewGuid().ToString("D");
            TelemetryConfiguration telemetryConfig = new TelemetryConfiguration(iKey, channel);

            var channelBuilder = new TelemetryProcessorChainBuilder(telemetryConfig);

            channelBuilder.Build();

            foreach (ITelemetryProcessor initializer in telemetryConfig.TelemetryInitializers)
            {
                ITelemetryModule m = initializer as ITelemetryModule;
                if (m != null)
                {
                    m.Initialize(telemetryConfig);
                }
            }

            foreach (ITelemetryProcessor processor in telemetryConfig.TelemetryProcessors)
            {
                ITelemetryModule m = processor as ITelemetryModule;
                if (m != null)
                {
                    m.Initialize(telemetryConfig);
                }
            }

            telemetrySentToChannel = channel.TelemetryItems;
            return(telemetryConfig);
        }
        public ApplicationInsightsWriter(string instrumentationKey, WriterOptions options)
        {
            TelemetryConfiguration.Active.InstrumentationKey = instrumentationKey;

            TelemetryProcessorChainBuilder builder             = TelemetryConfiguration.Active.TelemetryProcessorChainBuilder;
            QuickPulseTelemetryProcessor   quickPulseProcessor = null;

            // add our own telemetry processor that can override session based variables
            //builder.Use(next => new LogMagicTelemetryProcessor(next));

            // optionally enable QuickPulse

            /*
             * - Free and is not counted towards the bill.
             * - The latency is 1 second compared to a few minutes.
             * - Retention is while the data is on the chart, not 90 days.
             * - Data is only streamed while you are in Live Metrics view.
             */
            if (options.EnableQuickPulse)
            {
                builder.Use((next) =>
                {
                    quickPulseProcessor = new QuickPulseTelemetryProcessor(next);
                    return(quickPulseProcessor);
                });
            }

            builder.Build();

            _telemetryClient = new TelemetryClient(TelemetryConfiguration.Active);
            _telemetryClient.InstrumentationKey = instrumentationKey;
            _context = new InsightsContext(_telemetryClient, options);

            if (options.EnableQuickPulse)
            {
                var quickPulse = new QuickPulseTelemetryModule();
                quickPulse.Initialize(TelemetryConfiguration.Active);
                quickPulse.RegisterTelemetryProcessor(quickPulseProcessor);
            }

#if NETFULL
            // see https://github.com/Microsoft/ApplicationInsights-dotnet-server/blob/develop/Src/PerformanceCollector/Perf.Shared/PerformanceCollectorModule.cs
            if (options.CollectPerformanceCounters)
            {
                //optionally enable performance counters collection
                var pcm = new PerformanceCollectorModule();
                //custom counters can be easily added here if required

                pcm.Counters.Add(new PerformanceCounterCollectionRequest(@"\.NET CLR Memory(LogMagic.Console)\# GC Handles", "GC Handles"));

                pcm.Initialize(TelemetryConfiguration.Active);
            }
#endif

            TelemetryConfiguration.Active.TelemetryInitializers.Add(new OperationTelemetryInitialiser());

            _options = options;
        }
Exemple #7
0
        public void UseAdaptiveSamplingAddsAdaptiveSamplingProcessorToTheChainWithCorrectExcludedTypes()
        {
            var tc = new TelemetryConfiguration {
                TelemetryChannel = new StubTelemetryChannel()
            };
            var channelBuilder = new TelemetryProcessorChainBuilder(tc);

            channelBuilder.UseAdaptiveSampling("request");
            channelBuilder.Build();

            Assert.AreEqual("request", ((AdaptiveSamplingTelemetryProcessor)tc.TelemetryProcessorChain.FirstTelemetryProcessor).ExcludedTypes);
        }
Exemple #8
0
        public void UseSamplingSetsAddsSamplingProcessorToTheChainWithCorrectPercentage()
        {
            var tc = new TelemetryConfiguration {
                TelemetryChannel = new StubTelemetryChannel()
            };
            var channelBuilder = new TelemetryProcessorChainBuilder(tc);

            channelBuilder.UseSampling(5);
            channelBuilder.Build();

            Assert.AreEqual(5, ((SamplingTelemetryProcessor)tc.TelemetryProcessorChain.FirstTelemetryProcessor).SamplingPercentage);
        }
Exemple #9
0
        public void UseAdaptiveSamplingAddsAdaptiveSamplingProcessorToTheChain()
        {
            var tc = new TelemetryConfiguration {
                TelemetryChannel = new StubTelemetryChannel()
            };
            var channelBuilder = new TelemetryProcessorChainBuilder(tc);

            channelBuilder.UseAdaptiveSampling();
            channelBuilder.Build();

            AssertEx.IsType <AdaptiveSamplingTelemetryProcessor>(tc.TelemetryProcessorChain.FirstTelemetryProcessor);
        }
Exemple #10
0
        public void UseAdaptiveSamplingAddsAdaptiveSamplingProcessorToTheChainWithCorrectMaxTelemetryItemsPerSecond()
        {
            var tc = new TelemetryConfiguration {
                TelemetryChannel = new StubTelemetryChannel()
            };
            var channelBuilder = new TelemetryProcessorChainBuilder(tc);

            channelBuilder.UseAdaptiveSampling(5);
            channelBuilder.Build();

            Assert.AreEqual(5, ((AdaptiveSamplingTelemetryProcessor)tc.TelemetryProcessorChain.FirstTelemetryProcessor).MaxTelemetryItemsPerSecond);
        }
        internal TelemetryConfiguration InitializeConfiguration()
        {
            TelemetryConfiguration config = new TelemetryConfiguration()
            {
                InstrumentationKey = _instrumentationKey
            };

            AddInitializers(config);

            // Plug in Live stream and adaptive sampling
            QuickPulseTelemetryProcessor   processor = null;
            TelemetryProcessorChainBuilder builder   = config.TelemetryProcessorChainBuilder
                                                       .Use((next) =>
            {
                processor = new QuickPulseTelemetryProcessor(next);
                return(processor);
            });

            if (_samplingSettings != null)
            {
                builder.Use((next) =>
                {
                    return(new AdaptiveSamplingTelemetryProcessor(_samplingSettings, null, next));
                });
            }

            builder.Build();

            _quickPulseModule = new QuickPulseTelemetryModule();
            _quickPulseModule.Initialize(config);
            _quickPulseModule.RegisterTelemetryProcessor(processor);

            // Plug in perf counters
            _perfModule = new PerformanceCollectorModule();
            _perfModule.Initialize(config);

            // Configure the TelemetryChannel
            ITelemetryChannel channel = CreateTelemetryChannel();

            // call Initialize if available
            ITelemetryModule module = channel as ITelemetryModule;

            if (module != null)
            {
                module.Initialize(config);
            }

            config.TelemetryChannel = channel;

            return(config);
        }
        public void AllTelemetryCapturedWhenProductionRateIsLow()
        {
            var sentTelemetry = new List <ITelemetry>();
            int itemsProduced = 0;

            using (var tc = new TelemetryConfiguration()
            {
                TelemetryChannel = new StubTelemetryChannel()
            })
            {
                var chainBuilder = new TelemetryProcessorChainBuilder(tc);

                // set up adaptive sampling that evaluates and changes sampling % frequently
                chainBuilder
                .UseAdaptiveSampling(
                    new Channel.Implementation.SamplingPercentageEstimatorSettings()
                {
                    EvaluationInterval = TimeSpan.FromSeconds(1),
                    SamplingPercentageDecreaseTimeout = TimeSpan.FromSeconds(2),
                    SamplingPercentageIncreaseTimeout = TimeSpan.FromSeconds(2),
                },
                    this.TraceSamplingPercentageEvaluation)
                .Use((next) => new StubTelemetryProcessor(next)
                {
                    OnProcess = (t) => sentTelemetry.Add(t)
                });

                chainBuilder.Build();

                const int productionFrequencyMs = 1000;

                var productionTimer = new Timer(
                    (state) =>
                {
                    tc.TelemetryProcessorChain.Process(new RequestTelemetry());
                    itemsProduced++;
                },
                    null,
                    productionFrequencyMs,
                    productionFrequencyMs);

                Thread.Sleep(25000);

                // dispose timer and wait for callbacks to complete
                DisposeTimer(productionTimer);
            }

            Assert.AreEqual(itemsProduced, sentTelemetry.Count);
        }
Exemple #13
0
        public ApplicationInsightsWriter(string instrumentationKey, WriterOptions options)
        {
            TelemetryConfiguration.Active.InstrumentationKey = instrumentationKey;

            TelemetryProcessorChainBuilder builder = TelemetryConfiguration.Active.TelemetryProcessorChainBuilder;

            builder.Use(next => new LogMagicTelemetryProcessor(next));
            builder.Build();

            _telemetryClient = new TelemetryClient(TelemetryConfiguration.Active);
            _telemetryClient.InstrumentationKey = instrumentationKey;
            _context = new InsightsContext(_telemetryClient, options);

            _options = options;
        }
        public void AdaptiveSamplingSetsExcludedTypesOnInternalSamplingProcessor()
        {
            var tc = new TelemetryConfiguration {
                TelemetryChannel = new StubTelemetryChannel()
            };
            var channelBuilder = new TelemetryProcessorChainBuilder(tc);

            channelBuilder.UseAdaptiveSampling(5, "request;");
            channelBuilder.Build();

            var fieldInfo = typeof(AdaptiveSamplingTelemetryProcessor).GetField("samplingProcessor", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic);
            SamplingTelemetryProcessor internalProcessor = (SamplingTelemetryProcessor)fieldInfo.GetValue(tc.TelemetryProcessorChain.FirstTelemetryProcessor);

            Assert.AreEqual("request;", internalProcessor.ExcludedTypes);
        }
        private static TelemetryProcessorChain CreateTelemetryProcessorChainWithSampling(IList <ITelemetry> sentTelemetry, double samplingPercentage, string excludedTypes = null, string includedTypes = null)
        {
            var tc = new TelemetryConfiguration {
                TelemetryChannel = new StubTelemetryChannel()
            };
            var channelBuilder = new TelemetryProcessorChainBuilder(tc);

            channelBuilder.UseSampling(samplingPercentage, excludedTypes, includedTypes);
            channelBuilder.Use(next => new StubTelemetryProcessor(next)
            {
                OnProcess = t => sentTelemetry.Add(t)
            });

            channelBuilder.Build();

            return(tc.TelemetryProcessorChain);
        }
Exemple #16
0
        private static TelemetryProcessorChain CreateTelemetryProcessorChainWithSampling(IList <ITelemetry> sentTelemetry, double samplingPercentage)
        {
            var tc = new TelemetryConfiguration()
            {
                TelemetryChannel = new StubTelemetryChannel()
            };
            var channelBuilder = new TelemetryProcessorChainBuilder(tc);

            channelBuilder.UseSampling(samplingPercentage);
            channelBuilder.Use((next) => new StubTelemetryProcessor(next)
            {
                OnProcess = (t) => sentTelemetry.Add(t)
            });

            channelBuilder.Build();

            return(tc.TelemetryProcessors);
        }
Exemple #17
0
        internal static TelemetryConfiguration CreateTelemetryConfigWithExtractor(IList <ITelemetry> telemetrySentToChannel,
                                                                                  Func <ITelemetryProcessor, AutocollectedMetricsExtractor> extractorFactory)
        {
            ITelemetryChannel channel = new StubTelemetryChannel
            {
                OnSend = (t) => telemetrySentToChannel.Add(t)
            };
            string iKey = Guid.NewGuid().ToString("D");
            TelemetryConfiguration telemetryConfig = new TelemetryConfiguration(iKey, channel);

            var channelBuilder = new TelemetryProcessorChainBuilder(telemetryConfig);

            channelBuilder.Use(extractorFactory);
            channelBuilder.Build();


            return(telemetryConfig);
        }
Exemple #18
0
        public void UseAdaptiveSamplingAddsAdaptiveSamplingProcessorToTheChainWithCorrectSettings()
        {
            SamplingPercentageEstimatorSettings settings = new SamplingPercentageEstimatorSettings
            {
                MaxSamplingPercentage = 13
            };
            AdaptiveSamplingPercentageEvaluatedCallback callback = (second, percentage, samplingPercentage, changed, estimatorSettings) => { };

            var tc = new TelemetryConfiguration {
                TelemetryChannel = new StubTelemetryChannel()
            };
            var channelBuilder = new TelemetryProcessorChainBuilder(tc);

            channelBuilder.UseAdaptiveSampling(settings, callback);
            channelBuilder.Build();

            Assert.AreEqual(13, ((AdaptiveSamplingTelemetryProcessor)tc.TelemetryProcessorChain.FirstTelemetryProcessor).MaxSamplingPercentage);
        }
Exemple #19
0
        public void ProactivelySampledInItemsPassIfCurrentRateIsLowerThanExpected()
        {
            var sentTelemetry = new List <ITelemetry>();

            var tc = new TelemetryConfiguration
            {
                TelemetryChannel   = new StubTelemetryChannel(),
                InstrumentationKey = Guid.NewGuid().ToString("D")
            };

            var channelBuilder = new TelemetryProcessorChainBuilder(tc);

            channelBuilder.Use(next => new SamplingTelemetryProcessor(next)
            {
                SamplingPercentage          = 50,
                ProactiveSamplingPercentage = 100
            });
            channelBuilder.Use(next => new StubTelemetryProcessor(next)
            {
                OnProcess = t => sentTelemetry.Add(t)
            });
            channelBuilder.Build();

            int sampledInCount = 0;

            for (int i = 0; i < 1000; i++)
            {
                var item = new RequestTelemetry();
                item.Context.Operation.Id = ActivityTraceId.CreateRandom().ToHexString();

                // sample in random items - they all should  pass through regardless of the score
                if (i % 2 == 0)
                {
                    item.ProactiveSamplingDecision = SamplingDecision.SampledIn;
                    sampledInCount++;
                }

                tc.TelemetryProcessorChain.Process(item);
            }

            // all proactively sampled in items passed through regardless of their score.
            Assert.AreEqual(sampledInCount, sentTelemetry.Count(i => ((ISupportAdvancedSampling)i).ProactiveSamplingDecision == SamplingDecision.SampledIn));
        }
Exemple #20
0
        public void ProactivelySampledInItemsPassAccordingToScoreIfCurrentRateIsHigherThanExpected()
        {
            var sentTelemetry = new List <ITelemetry>();

            var tc = new TelemetryConfiguration
            {
                TelemetryChannel   = new StubTelemetryChannel(),
                InstrumentationKey = Guid.NewGuid().ToString("D")
            };

            var channelBuilder = new TelemetryProcessorChainBuilder(tc);

            channelBuilder.Use(next => new SamplingTelemetryProcessor(next)
            {
                SamplingPercentage          = 50,
                ProactiveSamplingPercentage = 50
            });
            channelBuilder.Use(next => new StubTelemetryProcessor(next)
            {
                OnProcess = t => sentTelemetry.Add(t)
            });
            channelBuilder.Build();

            int count = 5000;

            for (int i = 0; i < count; i++)
            {
                var item = new RequestTelemetry();
                item.Context.Operation.Id = ActivityTraceId.CreateRandom().ToHexString();

                // generate a lot sampled-in items, only 1/CurrentProactiveSampledInRatioToTarget of them should pass through
                // and SamplingPercentage of sampled-out items
                if (SamplingScoreGenerator.GetSamplingScore(item.Context.Operation.Id) < 80)
                {
                    item.ProactiveSamplingDecision = SamplingDecision.SampledIn;
                }

                tc.TelemetryProcessorChain.Process(item);
            }

            Assert.AreEqual(0, sentTelemetry.Count(i => ((ISupportAdvancedSampling)i).ProactiveSamplingDecision == SamplingDecision.None));
            Assert.AreEqual(count / 2, sentTelemetry.Count(i => ((ISupportAdvancedSampling)i).ProactiveSamplingDecision == SamplingDecision.SampledIn), count / 2 / 10);
        }
Exemple #21
0
        private TelemetryConfiguration GetAppInsightsTestTelemetryConfiguration()
        {
            var config = new TelemetryConfiguration();

            config.InstrumentationKey = "58ce4b98-2e30-4ac1-982b-e47c85b8d31d"; // Fake, but needs to be there in order for TelemetryClient to call the processor chain.

            var channel = new TestTelemetryChannel();

            config.DefaultTelemetrySink.TelemetryChannel = channel;

            var chainbuilder = new TelemetryProcessorChainBuilder(config, config.DefaultTelemetrySink);

            chainbuilder.Use((next) =>
            {
                var p1 = new EventFlowTelemetryProcessor(next);
                return(p1);
            });
            config.DefaultTelemetrySink.TelemetryProcessorChainBuilder = chainbuilder;
            chainbuilder.Build();

            return(config);
        }
        public void SamplingPercentageAdjustsAccordingToConstantHighProductionRate()
        {
            var sentTelemetry = new List <ITelemetry>();
            int itemsProduced = 0;

            using (var tc = new TelemetryConfiguration()
            {
                TelemetryChannel = new StubTelemetryChannel()
            })
            {
                var chainBuilder = new TelemetryProcessorChainBuilder(tc);

                // set up adaptive sampling that evaluates and changes sampling % frequently
                chainBuilder
                .UseAdaptiveSampling(
                    new Channel.Implementation.SamplingPercentageEstimatorSettings()
                {
                    EvaluationInterval = TimeSpan.FromSeconds(1),
                    SamplingPercentageDecreaseTimeout = TimeSpan.FromSeconds(2),
                    SamplingPercentageIncreaseTimeout = TimeSpan.FromSeconds(2),
                },
                    this.TraceSamplingPercentageEvaluation)
                .Use((next) => new StubTelemetryProcessor(next)
                {
                    OnProcess = (t) => sentTelemetry.Add(t)
                });

                chainBuilder.Build();

                const int productionFrequencyMs = 100;

                var productionTimer = new Timer(
                    (state) =>
                {
                    for (int i = 0; i < 2; i++)
                    {
                        tc.TelemetryProcessorChain.Process(new RequestTelemetry());
                        itemsProduced++;
                    }
                },
                    null,
                    0,
                    productionFrequencyMs);

                Thread.Sleep(25000);

                // dispose timer and wait for callbacks to complete
                DisposeTimer(productionTimer);
            }

            // number of items produced should be close to target of 5/second
            int targetItemCount = 25 * 5;

            // tolrance +-
            int tolerance = targetItemCount / 2;

            Trace.WriteLine(string.Format("'Ideal' telemetry item count: {0}", targetItemCount));
            Trace.WriteLine(string.Format(
                                "Expected range: from {0} to {1}",
                                targetItemCount - tolerance,
                                targetItemCount + tolerance));
            Trace.WriteLine(string.Format(
                                "Actual telemetry item count: {0} ({1:##.##}% of ideal)",
                                sentTelemetry.Count,
                                100.0 * sentTelemetry.Count / targetItemCount));

            Assert.IsTrue(sentTelemetry.Count > targetItemCount - tolerance);
            Assert.IsTrue(sentTelemetry.Count < targetItemCount + tolerance);
        }
        public (int proactivelySampledInAndSentCount, double sentCount) ProactiveSamplingTest(
            int proactivelySampledInRatePerSec,
            int beforeSamplingRatePerSec,
            double targetAfterSamplingRatePerSec,
            double precision,
            int testDurationInSec)
        {
            // we'll ignore telemetry reported during first few percentage evaluations
            int warmUpInSec = 12;

            // we'll produce proactively  sampled in items and also 'normal' items with the same rate
            // but allow only proactively sampled in + a bit more

            // number of items produced should be close to target
            int targetItemCount = (int)(testDurationInSec * targetAfterSamplingRatePerSec);

            var sentTelemetry = new List <ITelemetry>();

            using (var tc = new TelemetryConfiguration()
            {
                TelemetryChannel = new StubTelemetryChannel()
            })
            {
                var chainBuilder = new TelemetryProcessorChainBuilder(tc);

                // set up adaptive sampling that evaluates and changes sampling % frequently
                chainBuilder
                .UseAdaptiveSampling(
                    new Channel.Implementation.SamplingPercentageEstimatorSettings()
                {
                    // help algo get to stabilize earlier
                    InitialSamplingPercentage         = targetAfterSamplingRatePerSec / (double)beforeSamplingRatePerSec * 100,
                    MaxTelemetryItemsPerSecond        = targetAfterSamplingRatePerSec,
                    EvaluationInterval                = TimeSpan.FromSeconds(2),
                    SamplingPercentageDecreaseTimeout = TimeSpan.FromSeconds(4),
                    SamplingPercentageIncreaseTimeout = TimeSpan.FromSeconds(4),
                },
                    this.TraceSamplingPercentageEvaluation)
                .Use((next) => new StubTelemetryProcessor(next)
                {
                    OnProcess = (t) => sentTelemetry.Add(t)
                });

                chainBuilder.Build();

                var sw = Stopwatch.StartNew();
                var productionTimer = new Timer(
                    (state) =>
                {
                    var requests = new RequestTelemetry[beforeSamplingRatePerSec];

                    for (int i = 0; i < beforeSamplingRatePerSec; i++)
                    {
                        requests[i] = new RequestTelemetry()
                        {
                            ProactiveSamplingDecision = i < proactivelySampledInRatePerSec ? SamplingDecision.SampledIn : SamplingDecision.None
                        };

                        requests[i].Context.Operation.Id = ActivityTraceId.CreateRandom().ToHexString();
                    }

                    foreach (var request in requests)
                    {
                        if (((Stopwatch)state).Elapsed.TotalSeconds < warmUpInSec)
                        {
                            // let's ignore telemetry from first few rate evaluations - it does not make sense
                            request.Properties["ignore"] = "true";
                        }

                        tc.TelemetryProcessorChain.Process(request);
                    }
                },
                    sw,
                    0,
                    1000);

                Thread.Sleep(TimeSpan.FromSeconds(testDurationInSec + warmUpInSec));

                // dispose timer and wait for callbacks to complete
                DisposeTimer(productionTimer);
            }

            var notIgnoredSent = sentTelemetry.Where(i => i is ISupportProperties propItem && !propItem.Properties.ContainsKey("ignore")).ToArray();

            var proactivelySampledInAndSentCount = notIgnoredSent.Count(i =>
                                                                        i is ISupportAdvancedSampling advSamplingItem &&
                                                                        advSamplingItem.ProactiveSamplingDecision == SamplingDecision.SampledIn);

            // check that normal sampling requirements still apply (we generated as much items as expected)
            Trace.WriteLine($"'Ideal' telemetry item count: {targetItemCount}");
            Trace.WriteLine($"Expected range: from {targetItemCount - precision * targetItemCount} to {targetItemCount + precision * targetItemCount}");
            Trace.WriteLine(
                $"Actual telemetry item count: {notIgnoredSent.Length} ({100.0 * notIgnoredSent.Length / targetItemCount:##.##}% of ideal)");
            Trace.WriteLine(
                $"Actual proactive sampled in and sent: {proactivelySampledInAndSentCount}");

            Assert.IsTrue(notIgnoredSent.Length / (double)targetItemCount > 1 - precision);
            Assert.IsTrue(notIgnoredSent.Length / (double)targetItemCount < 1 + precision);

            return(proactivelySampledInAndSentCount, notIgnoredSent.Length);
        }
        public void SamplingPercentageAdjustsForSpikyProductionRate()
        {
            var sentTelemetry = new List <ITelemetry>();
            int itemsProduced = 0;

            using (var tc = new TelemetryConfiguration()
            {
                TelemetryChannel = new StubTelemetryChannel()
            })
            {
                var chainBuilder = new TelemetryProcessorChainBuilder(tc);

                // set up adaptive sampling that evaluates and changes sampling % frequently
                chainBuilder
                .UseAdaptiveSampling(
                    new SamplingPercentageEstimatorSettings()
                {
                    InitialSamplingPercentage         = 5.0,
                    EvaluationInterval                = TimeSpan.FromSeconds(1),
                    SamplingPercentageDecreaseTimeout = TimeSpan.FromSeconds(2),
                    SamplingPercentageIncreaseTimeout = TimeSpan.FromSeconds(10),
                },
                    this.TraceSamplingPercentageEvaluation)
                .Use((next) => new StubTelemetryProcessor(next)
                {
                    OnProcess = (t) => sentTelemetry.Add(t)
                });

                chainBuilder.Build();

                const int regularProductionFrequencyMs = 100;
                const int spikeProductionFrequencyMs   = 3000;

                using (var regularProductionTimer = new Timer(
                           (state) =>
                {
                    for (int i = 0; i < 2; i++)
                    {
                        tc.TelemetryProcessorChain.Process(new RequestTelemetry());
                        Interlocked.Increment(ref itemsProduced);
                    }
                },
                           null,
                           0,
                           regularProductionFrequencyMs))
                    using (var spikeProductionTimer = new Timer(
                               (state) =>
                    {
                        for (int i = 0; i < 200; i++)
                        {
                            tc.TelemetryProcessorChain.Process(new RequestTelemetry());
                            Interlocked.Increment(ref itemsProduced);
                        }
                    },
                               null,
                               0,
                               spikeProductionFrequencyMs))
                    {
                        Thread.Sleep(30000);
                    }
            }

            // number of items produced should be close to target of 5/second
            int targetItemCount = 30 * 5;
            int tolerance       = targetItemCount / 2;

            Trace.WriteLine(string.Format("'Ideal' telemetry item count: {0}", targetItemCount));
            Trace.WriteLine(string.Format(
                                "Expected range: from {0} to {1}",
                                targetItemCount - tolerance,
                                targetItemCount + tolerance));
            Trace.WriteLine(string.Format(
                                "Actual telemetry item count: {0} ({1:##.##}% of ideal)",
                                sentTelemetry.Count,
                                100.0 * sentTelemetry.Count / targetItemCount));

            Assert.IsTrue(sentTelemetry.Count > targetItemCount - tolerance);
            Assert.IsTrue(sentTelemetry.Count < targetItemCount + tolerance);
        }