Extracts QuickPulse data from the telemetry stream.
Inheritance: ITelemetryProcessor, ITelemetryModule, IQuickPulseTelemetryProcessor
        public void QuickPulseTelemetryProcessorCallsNext()
        {
            // ARRANGE
            var spy = new SimpleTelemetryProcessorSpy();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(spy);

            // ACT
            telemetryProcessor.Process(new RequestTelemetry() { Context = { InstrumentationKey = "some ikey" } });

            // ASSERT
            Assert.AreEqual(1, spy.ReceivedCalls);
        }
        private static QuickPulseTelemetryModule InitializeQuickPulseModule()
        {
            var quickPulseModule = new QuickPulseTelemetryModule();

            quickPulseModule.QuickPulseServiceEndpoint = "http://localhost:4555/QuickPulseService.svc/";

            QuickPulseTelemetryProcessor processor = null;
            TelemetryConfiguration.Active.TelemetryProcessorChainBuilder.Use(
                (next) =>
                {
                    processor = new QuickPulseTelemetryProcessor(next);
                    quickPulseModule.RegisterTelemetryProcessor(processor);
                    return processor;
                });

            TelemetryConfiguration.Active.TelemetryProcessorChainBuilder.Build();

            quickPulseModule.Initialize(TelemetryConfiguration.Active);

            return quickPulseModule;
        }
        public void QuickPulseTelemetryModuleCollectsData()
        {
            // ARRANGE
            var pause = TimeSpan.FromMilliseconds(100);
            var interval = TimeSpan.FromMilliseconds(1);
            var timings = new QuickPulseTimings(interval, interval);
            var collectionTimeSlotManager = new QuickPulseCollectionTimeSlotManagerMock(timings);
            var serviceClient = new QuickPulseServiceClientMock { ReturnValueFromPing = true, ReturnValueFromSubmitSample = true };
            var performanceCollector = new PerformanceCollectorMock();

            var module = new QuickPulseTelemetryModule(collectionTimeSlotManager, null, serviceClient, performanceCollector, timings);
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());
            telemetryProcessor.Initialize(new TelemetryConfiguration());
            module.RegisterTelemetryProcessor(telemetryProcessor);

            // ACT
            module.Initialize(new TelemetryConfiguration() { InstrumentationKey = "some ikey" });

            Thread.Sleep(pause);

            telemetryProcessor.Process(new RequestTelemetry() { Context = { InstrumentationKey = "some ikey" } });
            telemetryProcessor.Process(new DependencyTelemetry() { Context = { InstrumentationKey = "some ikey" } });

            Thread.Sleep(pause);

            Assert.AreEqual(1, serviceClient.PingCount);

            // ASSERT
            serviceClient.ReturnValueFromPing = false;
            serviceClient.ReturnValueFromSubmitSample = false;

            Thread.Sleep(pause);
            Assert.IsTrue(serviceClient.SnappedSamples.Count > 0);

            Assert.IsTrue(serviceClient.SnappedSamples.Any(s => s.AIRequestsPerSecond > 0));
            Assert.IsTrue(serviceClient.SnappedSamples.Any(s => s.AIDependencyCallsPerSecond > 0));
            Assert.IsTrue(
                serviceClient.SnappedSamples.Any(
                    s => Math.Abs(s.PerfCountersLookup[@"\Processor(_Total)\% Processor Time"]) > double.Epsilon));
        }
        public void QuickPulseTelemetryProcessorKeepsAccurateCountOfExceptions()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = "some ikey" });

            // ACT
            telemetryProcessor.Process(new ExceptionTelemetry() { Context = { InstrumentationKey = "some ikey" } });
            telemetryProcessor.Process(new ExceptionTelemetry() { Context = { InstrumentationKey = "some ikey" } });
            telemetryProcessor.Process(new ExceptionTelemetry() { Context = { InstrumentationKey = "some ikey" } });

            // ASSERT
            Assert.AreEqual(3, accumulatorManager.CurrentDataAccumulator.AIExceptionCount);
        }
        public void QuickPulseTelemetryProcessorKeepsAccurateCountOfRequests()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = "some ikey" });

            // ACT
            telemetryProcessor.Process(
                new RequestTelemetry()
                    {
                        Success = false,
                        ResponseCode = "200",
                        Duration = TimeSpan.FromSeconds(1),
                        Context = { InstrumentationKey = "some ikey" }
                    });
            telemetryProcessor.Process(
                new RequestTelemetry()
                    {
                        Success = true,
                        ResponseCode = "200",
                        Duration = TimeSpan.FromSeconds(2),
                        Context = { InstrumentationKey = "some ikey" }
                    });
            telemetryProcessor.Process(
                new RequestTelemetry()
                    {
                        Success = false,
                        ResponseCode = string.Empty,
                        Duration = TimeSpan.FromSeconds(3),
                        Context = { InstrumentationKey = "some ikey" }
                    });
            telemetryProcessor.Process(
                new RequestTelemetry()
                    {
                        Success = null,
                        ResponseCode = string.Empty,
                        Duration = TimeSpan.FromSeconds(4),
                        Context = { InstrumentationKey = "some ikey" }
                    });
            telemetryProcessor.Process(
                new RequestTelemetry()
                    {
                        Success = true,
                        ResponseCode = string.Empty,
                        Duration = TimeSpan.FromSeconds(5),
                        Context = { InstrumentationKey = "some ikey" }
                    });
            telemetryProcessor.Process(
                new RequestTelemetry()
                    {
                        Success = null,
                        ResponseCode = "404",
                        Duration = TimeSpan.FromSeconds(6),
                        Context = { InstrumentationKey = "some ikey" }
                    });

            // ASSERT
            Assert.AreEqual(6, accumulatorManager.CurrentDataAccumulator.AIRequestCount);
            Assert.AreEqual(
                1 + 2 + 3 + 4 + 5 + 6,
                TimeSpan.FromTicks(accumulatorManager.CurrentDataAccumulator.AIRequestDurationInTicks).TotalSeconds);
            Assert.AreEqual(4, accumulatorManager.CurrentDataAccumulator.AIRequestSuccessCount);
            Assert.AreEqual(2, accumulatorManager.CurrentDataAccumulator.AIRequestFailureCount);
        }
        public void QuickPulseTelemetryProcessorTruncatesLongFullExceptionTelemetryItemProperties()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());
            var instrumentationKey = "some ikey";
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = instrumentationKey });

            // ACT
            var exceptionShort = new ExceptionTelemetry(new ArgumentException())
            {
                Properties = { { new string('p', MaxFieldLength), new string('v', MaxFieldLength) } },
                Message = new string('m', MaxFieldLength),
                Context = { InstrumentationKey = instrumentationKey }
            };

            var exceptionLong = new ExceptionTelemetry(new ArgumentException())
            {
                Properties = { { new string('p', MaxFieldLength + 1), new string('v', MaxFieldLength + 1) } },
                Message = new string('m', MaxFieldLength),
                Context = { InstrumentationKey = instrumentationKey }
            };

            // process in the opposite order to allow for an easier validation order
            telemetryProcessor.Process(exceptionLong);
            telemetryProcessor.Process(exceptionShort);

            // ASSERT
            var telemetryDocuments = accumulatorManager.CurrentDataAccumulator.TelemetryDocuments.Cast<ExceptionTelemetryDocument>().ToList();

            var expected = exceptionShort.Properties.First();
            var actual = telemetryDocuments[0].Properties.First();
            Assert.AreEqual(expected.Key, actual.Key);
            Assert.AreEqual(expected.Value, actual.Value);

            actual = telemetryDocuments[1].Properties.First();
            Assert.AreEqual(expected.Key, actual.Key);
            Assert.AreEqual(expected.Value, actual.Value);
        }
        public void QuickPulseTelemetryProcessorKeepsAccurateCountOfDependencies()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = "some ikey" });

            // ACT
            telemetryProcessor.Process(
                new DependencyTelemetry() { Success = true, Duration = TimeSpan.FromSeconds(1), Context = { InstrumentationKey = "some ikey" } });
            telemetryProcessor.Process(
                new DependencyTelemetry() { Success = true, Duration = TimeSpan.FromSeconds(1), Context = { InstrumentationKey = "some ikey" } });
            telemetryProcessor.Process(
                new DependencyTelemetry() { Success = false, Duration = TimeSpan.FromSeconds(2), Context = { InstrumentationKey = "some ikey" } });
            telemetryProcessor.Process(
                new DependencyTelemetry() { Success = null, Duration = TimeSpan.FromSeconds(3), Context = { InstrumentationKey = "some ikey" } });

            // ASSERT
            Assert.AreEqual(4, accumulatorManager.CurrentDataAccumulator.AIDependencyCallCount);
            Assert.AreEqual(1 + 1 + 2 + 3, TimeSpan.FromTicks(accumulatorManager.CurrentDataAccumulator.AIDependencyCallDurationInTicks).TotalSeconds);
            Assert.AreEqual(2, accumulatorManager.CurrentDataAccumulator.AIDependencyCallSuccessCount);
            Assert.AreEqual(1, accumulatorManager.CurrentDataAccumulator.AIDependencyCallFailureCount);
        }
        public void QuickPulseTelemetryProcessorTruncatesLongFullDependencyTelemetryItemProperties()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());
            var instrumentationKey = "some ikey";
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = instrumentationKey });

            // ACT
            var dependencyShort = new DependencyTelemetry(
                "dependencyShort",
                "dependencyShort",
                "dependencyShort",
                "dependencyShort",
                DateTimeOffset.Now,
                TimeSpan.FromSeconds(1),
                "dependencyShort",
                false)
            {
                Properties = { { new string('p', MaxFieldLength), new string('v', MaxFieldLength) } },
                Context = { InstrumentationKey = instrumentationKey }
            };

            var dependencyLong = new DependencyTelemetry(
                "dependencyLong",
                "dependencyLong",
                "dependencyLong",
                "dependencyLong",
                DateTimeOffset.Now,
                TimeSpan.FromSeconds(1),
                "dependencyLong",
                false)
            {
                Properties = { { new string('p', MaxFieldLength + 1), new string('v', MaxFieldLength + 1) } },
                Context = { InstrumentationKey = instrumentationKey }
            };

            // process in the opposite order to allow for an easier validation order
            telemetryProcessor.Process(dependencyLong);
            telemetryProcessor.Process(dependencyShort);

            // ASSERT
            var telemetryDocuments = accumulatorManager.CurrentDataAccumulator.TelemetryDocuments.Cast<DependencyTelemetryDocument>().ToList();

            var expected = dependencyShort.Properties.First();
            var actual = telemetryDocuments[0].Properties.First();
            Assert.AreEqual(expected.Key, actual.Key);
            Assert.AreEqual(expected.Value, actual.Value);

            actual = telemetryDocuments[1].Properties.First();
            Assert.AreEqual(expected.Key, actual.Key);
            Assert.AreEqual(expected.Value, actual.Value);
        }
        public void QuickPulseTelemetryProcessorDoesNotCollectFullTelemetryItemsWhenSwitchIsOff()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());
            var instrumentationKey = "some ikey";
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = instrumentationKey },
                disableFullTelemetryItems: true);

            // ACT
            var request = new RequestTelemetry()
            {
                Success = false,
                ResponseCode = "500",
                Duration = TimeSpan.FromSeconds(1),
                Context = { InstrumentationKey = instrumentationKey }
            };

            var dependency = new DependencyTelemetry()
            {
                Success = false,
                Duration = TimeSpan.FromSeconds(1),
                Context = { InstrumentationKey = instrumentationKey }
            };

            var exception = new ExceptionTelemetry(new ArgumentException("bla")) { Context = { InstrumentationKey = instrumentationKey } };

            telemetryProcessor.Process(request);
            telemetryProcessor.Process(dependency);
            telemetryProcessor.Process(exception);

            // ASSERT
            Assert.AreEqual(0, accumulatorManager.CurrentDataAccumulator.TelemetryDocuments.Count);
        }
        public void QuickPulseTelemetryProcessorFiltersOutDependencyCallsToQuickPulseServiceDuringCollection()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var simpleTelemetryProcessorSpy = new SimpleTelemetryProcessorSpy();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(simpleTelemetryProcessorSpy);
            var config = new TelemetryConfiguration() { InstrumentationKey = "some ikey" };

            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("https://qps.cloudapp.net/endpoint.svc"),
                config);

            // ACT
            telemetryProcessor.Process(
                new DependencyTelemetry() { Name = "http://microsoft.ru", Context = { InstrumentationKey = config.InstrumentationKey } });
            telemetryProcessor.Process(
                new DependencyTelemetry() { Name = "http://qps.cloudapp.net/blabla", Context = { InstrumentationKey = config.InstrumentationKey } });
            telemetryProcessor.Process(
                new DependencyTelemetry() { Name = "https://bing.com", Context = { InstrumentationKey = config.InstrumentationKey } });

            // ASSERT
            Assert.AreEqual(2, simpleTelemetryProcessorSpy.ReceivedCalls);
            Assert.AreEqual("http://microsoft.ru", (simpleTelemetryProcessorSpy.ReceivedItems[0] as DependencyTelemetry).Name);
            Assert.AreEqual("https://bing.com", (simpleTelemetryProcessorSpy.ReceivedItems[1] as DependencyTelemetry).Name);
            Assert.AreEqual(2, accumulatorManager.CurrentDataAccumulator.AIDependencyCallCount);
        }
        public void QuickPulseTelemetryProcessorSwitchesBetweenMultipleAccumulatorManagers()
        {
            // ARRANGE
            var accumulatorManager1 = new QuickPulseDataAccumulatorManager();
            var accumulatorManager2 = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());

            // ACT
            var serviceEndpoint = new Uri("http://microsoft.com");
            var config = new TelemetryConfiguration() { InstrumentationKey = "some ikey" };
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager1,
                serviceEndpoint,
                config);
            telemetryProcessor.Process(new RequestTelemetry() { Context = { InstrumentationKey = "some ikey" } });
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StopCollection();

            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager2,
                serviceEndpoint,
                config);
            telemetryProcessor.Process(new DependencyTelemetry() { Context = { InstrumentationKey = "some ikey" } });
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StopCollection();

            // ASSERT
            Assert.AreEqual(1, accumulatorManager1.CurrentDataAccumulator.AIRequestCount);
            Assert.AreEqual(0, accumulatorManager1.CurrentDataAccumulator.AIDependencyCallCount);

            Assert.AreEqual(0, accumulatorManager2.CurrentDataAccumulator.AIRequestCount);
            Assert.AreEqual(1, accumulatorManager2.CurrentDataAccumulator.AIDependencyCallCount);
        }
        public void QuickPulseTelemetryProcessorExpandsExceptionMessageWhenSingleInnerException()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());
            var instrumentationKey = "some ikey";
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = instrumentationKey });

            var exception = new Exception("Exception 1", new Exception("Exception 2"));

            // ACT
            var exceptionTelemetry = new ExceptionTelemetry(exception) { Context = { InstrumentationKey = instrumentationKey } };

            telemetryProcessor.Process(exceptionTelemetry);

            // ASSERT
            var telemetryDocuments = accumulatorManager.CurrentDataAccumulator.TelemetryDocuments.Cast<ExceptionTelemetryDocument>().ToList();

            Assert.AreEqual("Exception 1 <--- Exception 2", telemetryDocuments[0].ExceptionMessage);
        }
        public void QuickPulseTelemetryProcessorFiltersOutDependencyCallsToDefaultQuickPulseServiceEndpointInIdleMode()
        {
            // ARRANGE
            var simpleTelemetryProcessorSpy = new SimpleTelemetryProcessorSpy();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(simpleTelemetryProcessorSpy);

            // ACT
            telemetryProcessor.Process(new DependencyTelemetry() { Name = "http://microsoft.ru" });
            telemetryProcessor.Process(new DependencyTelemetry() { Name = "http://rt.services.visualstudio.com/blabla" });
            telemetryProcessor.Process(new DependencyTelemetry() { Name = "https://bing.com" });

            // ASSERT
            Assert.AreEqual(2, simpleTelemetryProcessorSpy.ReceivedCalls);
            Assert.AreEqual("http://microsoft.ru", (simpleTelemetryProcessorSpy.ReceivedItems[0] as DependencyTelemetry).Name);
            Assert.AreEqual("https://bing.com", (simpleTelemetryProcessorSpy.ReceivedItems[1] as DependencyTelemetry).Name);
        }
        public void QuickPulseTelemetryProcessorCollectsFullTelemetryItems()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());
            var instrumentationKey = "some ikey";
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = instrumentationKey });

            // ACT
            var request = new RequestTelemetry()
                              {
                                  Name = Guid.NewGuid().ToString(),
                                  Success = false,
                                  ResponseCode = "500",
                                  Duration = TimeSpan.FromSeconds(1),
                                  Properties = { { "Prop1", "Val1" }, { "Prop2", "Val2" }, { "Prop3", "Val3" }, { "Prop4", "Val4" } },
                                  Context = { InstrumentationKey = instrumentationKey }
                              };

            var dependency = new DependencyTelemetry()
                                 {
                                     Name = Guid.NewGuid().ToString(),
                                     Success = false,
                                     Duration = TimeSpan.FromSeconds(1),
                                     Properties = { { "Prop1", "Val1" }, { "Prop2", "Val2" }, { "Prop3", "Val3" }, { "Prop4", "Val4" }, { "ErrorMessage", "EMValue" } },
                                     Context = { InstrumentationKey = instrumentationKey }
                                 };

            var exception = new ExceptionTelemetry(new ArgumentNullException())
                                {
                                    Properties = { { "Prop1", "Val1" }, { "Prop2", "Val2" }, { "Prop3", "Val3" }, { "Prop4", "Val4" } },
                                    Context = { InstrumentationKey = instrumentationKey }
                                };

            telemetryProcessor.Process(request);
            telemetryProcessor.Process(dependency);
            telemetryProcessor.Process(exception);

            // ASSERT
            var collectedTelemetry = accumulatorManager.CurrentDataAccumulator.TelemetryDocuments.ToArray().Reverse().ToArray();

            Assert.AreEqual(3, accumulatorManager.CurrentDataAccumulator.TelemetryDocuments.Count);

            Assert.AreEqual(TelemetryDocumentType.Request, Enum.Parse(typeof(TelemetryDocumentType), collectedTelemetry[0].DocumentType));
            Assert.AreEqual(request.Name, ((RequestTelemetryDocument)collectedTelemetry[0]).Name);
            Assert.AreEqual(3, collectedTelemetry[0].Properties.Length);

            Assert.IsTrue(collectedTelemetry[0].Properties.ToList().TrueForAll(pair => pair.Key.Contains("Prop") && pair.Value.Contains("Val")));

            Assert.AreEqual(TelemetryDocumentType.RemoteDependency, Enum.Parse(typeof(TelemetryDocumentType), collectedTelemetry[1].DocumentType));
            Assert.AreEqual(dependency.Name, ((DependencyTelemetryDocument)collectedTelemetry[1]).Name);
            Assert.AreEqual(3 + 1, collectedTelemetry[1].Properties.Length);

            Assert.IsTrue(collectedTelemetry[1].Properties.ToList().TrueForAll(pair => (pair.Key.Contains("Prop") && pair.Value.Contains("Val")) || (pair.Key == "ErrorMessage" && pair.Value == "EMValue")));

            Assert.AreEqual(TelemetryDocumentType.Exception, Enum.Parse(typeof(TelemetryDocumentType), collectedTelemetry[2].DocumentType));
            Assert.AreEqual(exception.Exception.ToString(), ((ExceptionTelemetryDocument)collectedTelemetry[2]).Exception);
            Assert.AreEqual(3, collectedTelemetry[2].Properties.Length);
            Assert.IsTrue(collectedTelemetry[2].Properties.ToList().TrueForAll(pair => (pair.Key.Contains("Prop") && pair.Value.Contains("Val"))));
        }
        public void QuickPulseTelemetryProcessorDoesNotCollectSucceededFullTelemetryItems()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());
            var instrumentationKey = "some ikey";
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = instrumentationKey });

            // ACT
            var request = new RequestTelemetry()
            {
                Success = true,
                ResponseCode = "200",
                Duration = TimeSpan.FromSeconds(1),
                Context = { InstrumentationKey = instrumentationKey }
            };

            var dependency = new DependencyTelemetry()
            {
                Success = true,
                Duration = TimeSpan.FromSeconds(1),
                Context = { InstrumentationKey = instrumentationKey }
            };

            var exception = new ExceptionTelemetry(new ArgumentException("bla")) { Context = { InstrumentationKey = instrumentationKey } };

            telemetryProcessor.Process(request);
            telemetryProcessor.Process(dependency);
            telemetryProcessor.Process(exception);

            // ASSERT
            var collectedTelemetry = accumulatorManager.CurrentDataAccumulator.TelemetryDocuments.ToArray().Reverse().ToArray();

            Assert.AreEqual(1, accumulatorManager.CurrentDataAccumulator.TelemetryDocuments.Count);
            Assert.AreEqual(exception.Exception.ToString(), ((ExceptionTelemetryDocument)collectedTelemetry[0]).Exception);
        }
        public void QuickPulseTelemetryProcessorDoesNotCollectFullExceptionTelemetryItemsOnceQuotaIsExhausted()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var timeProvider = new ClockMock();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy(), timeProvider, 60, 5);
            var instrumentationKey = "some ikey";
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = instrumentationKey });

            // ACT
            int counter = 0;
            for (int i = 0; i < 100; i++)
            {
                var exception = new ExceptionTelemetry(new Exception((counter++).ToString(CultureInfo.InvariantCulture)))
                                    {
                                        Context = { InstrumentationKey = instrumentationKey }
                                    };

                telemetryProcessor.Process(exception);
            }

            timeProvider.FastForward(TimeSpan.FromSeconds(30));

            for (int i = 0; i < 100; i++)
            {
                var exception = new ExceptionTelemetry(new Exception((counter++).ToString(CultureInfo.InvariantCulture)))
                                    {
                                        Context = { InstrumentationKey = instrumentationKey }
                                    };

                telemetryProcessor.Process(exception);
            }

            // ASSERT
            var collectedTelemetry = accumulatorManager.CurrentDataAccumulator.TelemetryDocuments.ToArray().Reverse().Cast<ExceptionTelemetryDocument>().ToArray();

            Assert.AreEqual(5 + 30, accumulatorManager.CurrentDataAccumulator.TelemetryDocuments.Count);

            // out of the first 100 items we expect to see items 0 through 4 (the initial quota)
            for (int i = 0; i < 5; i++)
            {
                Assert.AreEqual(i, int.Parse(collectedTelemetry[i].ExceptionMessage, CultureInfo.InvariantCulture));
            }

            // out of the second 100 items we expect to see items 100 through 129 (the new quota for 30 seconds)
            for (int i = 5; i < 35; i++)
            {
                Assert.AreEqual(95 + i, int.Parse(collectedTelemetry[i].ExceptionMessage, CultureInfo.InvariantCulture));
            }
        }
        public void QuickPulseTelemetryProcessorMustBeStoppedBeforeReceivingStartCommand()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());

            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://test.com"),
                new TelemetryConfiguration());

            // ACT
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://test.com"),
                new TelemetryConfiguration());

            // ASSERT
            // must throw
        }
        public void QuickPulseTelemetryProcessorHandlesDuplicatePropertyNamesDueToTruncation()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());
            var instrumentationKey = "some ikey";
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = instrumentationKey });

            // ACT
            var exception = new ExceptionTelemetry(new ArgumentException())
            {
                Properties =
                {
                    { new string('p', MaxFieldLength + 1), "Val1" },
                    { new string('p', MaxFieldLength + 2), "Val2" }
                },
                Message = "Message",
                Context = { InstrumentationKey = instrumentationKey }
            };

            telemetryProcessor.Process(exception);

            // ASSERT
            var telemetryDocuments = accumulatorManager.CurrentDataAccumulator.TelemetryDocuments.Cast<ExceptionTelemetryDocument>().ToList();

            Assert.AreEqual(1, telemetryDocuments[0].Properties.Length);
            Assert.AreEqual(new string('p', MaxFieldLength), telemetryDocuments[0].Properties.First().Key);
        }
        public void QuickPulseTelemetryProcessorRegistersWithModule()
        {
            // ARRANGE
            var module = new QuickPulseTelemetryModule(null, null, null, null, null);

            TelemetryModules.Instance.Modules.Add(module);

            // ACT
            var spy = new SimpleTelemetryProcessorSpy();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(spy);
            telemetryProcessor.Initialize(new TelemetryConfiguration());

            // ASSERT
            Assert.AreEqual(telemetryProcessor, QuickPulseTestHelper.GetTelemetryProcessors(module).Single());
        }
        public void QuickPulseTelemetryModuleDoesNotRegisterSameProcessorMoreThanOnce()
        {
            // ARRANGE
            var module = new QuickPulseTelemetryModule(null, null, null, null, null);
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());

            // ACT
            module.RegisterTelemetryProcessor(telemetryProcessor);
            module.RegisterTelemetryProcessor(telemetryProcessor);
            module.RegisterTelemetryProcessor(telemetryProcessor);

            // ASSERT
            Assert.AreEqual(telemetryProcessor, QuickPulseTestHelper.GetTelemetryProcessors(module).Single());
        }
        public void QuickPulseTelemetryProcessorTruncatesLongFullDependencyTelemetryItemName()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());
            var instrumentationKey = "some ikey";
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = instrumentationKey });

            // ACT
            var dependencyShort = new DependencyTelemetry(
                new string('c', MaxFieldLength),
                new string('c', MaxFieldLength),
                new string('c', MaxFieldLength),
                new string('c', MaxFieldLength),
                DateTimeOffset.Now,
                TimeSpan.FromSeconds(1),
                new string('c', MaxFieldLength),
                false)
            { Context = { InstrumentationKey = instrumentationKey } };

            var dependencyLong = new DependencyTelemetry(
                new string('c', MaxFieldLength + 1),
                new string('c', MaxFieldLength + 1),
                new string('c', MaxFieldLength + 1),
                new string('c', MaxFieldLength + 1),
                DateTimeOffset.Now,
                TimeSpan.FromSeconds(1),
                new string('c', MaxFieldLength + 1),
                false)
            { Context = { InstrumentationKey = instrumentationKey } };

            // process in the opposite order to allow for an easier validation order
            telemetryProcessor.Process(dependencyLong);
            telemetryProcessor.Process(dependencyShort);

            // ASSERT
            var telemetryDocuments = accumulatorManager.CurrentDataAccumulator.TelemetryDocuments.Cast<DependencyTelemetryDocument>().ToList();

            Assert.AreEqual(telemetryDocuments[0].Name, dependencyShort.Name);
            Assert.AreEqual(telemetryDocuments[1].Name, dependencyLong.Name.Substring(0, MaxFieldLength));
        }
        private void ProcessTelemetry(ITelemetry telemetry)
        {
            // only process items that are going to the instrumentation key that our module is initialized with
            if (string.IsNullOrWhiteSpace(this.config?.InstrumentationKey) ||
                !string.Equals(telemetry?.Context?.InstrumentationKey, this.config.InstrumentationKey, StringComparison.OrdinalIgnoreCase))
            {
                return;
            }

            var telemetryAsRequest    = telemetry as RequestTelemetry;
            var telemetryAsDependency = telemetry as DependencyTelemetry;
            var telemetryAsException  = telemetry as ExceptionTelemetry;
            var telemetryAsEvent      = telemetry as EventTelemetry;
            var telemetryAsTrace      = telemetry as TraceTelemetry;

            // update aggregates
            bool?originalRequestTelemetrySuccessValue = null;

            if (telemetryAsRequest != null)
            {
                // special treatment for RequestTelemetry.Success
                originalRequestTelemetrySuccessValue = telemetryAsRequest.Success;
                telemetryAsRequest.Success           = IsRequestSuccessful(telemetryAsRequest);

                this.UpdateRequestAggregates(telemetryAsRequest);
            }
            else if (telemetryAsDependency != null)
            {
                this.UpdateDependencyAggregates(telemetryAsDependency);
            }
            else if (telemetryAsException != null)
            {
                this.UpdateExceptionAggregates();
            }

            // get a local reference, the accumulator might get swapped out at any time
            // in case we continue to process this configuration once the accumulator is out, increase the reference count so that this accumulator is not sent out before we're done
            CollectionConfigurationAccumulator configurationAccumulatorLocal =
                this.dataAccumulatorManager.CurrentDataAccumulator.CollectionConfigurationAccumulator;

            // if the accumulator is swapped out and a sample is created and sent out - all while between these two lines, this telemetry item gets lost
            // however, that is not likely to happen
            configurationAccumulatorLocal.AddRef();

            try
            {
                // collect full telemetry items
                if (!this.disableFullTelemetryItems)
                {
                    ITelemetryDocument           telemetryDocument = null;
                    IEnumerable <DocumentStream> documentStreams   = configurationAccumulatorLocal.CollectionConfiguration.DocumentStreams;

                    //!!! report runtime errors for filter groups?
                    CollectionConfigurationError[] groupErrors;

                    if (telemetryAsRequest != null)
                    {
                        telemetryDocument = this.CreateTelemetryDocument(
                            telemetryAsRequest,
                            documentStreams,
                            documentStream => documentStream.RequestQuotaTracker,
                            documentStream => documentStream.CheckFilters(telemetryAsRequest, out groupErrors),
                            ConvertRequestToTelemetryDocument);
                    }
                    else if (telemetryAsDependency != null)
                    {
                        telemetryDocument = this.CreateTelemetryDocument(
                            telemetryAsDependency,
                            documentStreams,
                            documentStream => documentStream.DependencyQuotaTracker,
                            documentStream => documentStream.CheckFilters(telemetryAsDependency, out groupErrors),
                            ConvertDependencyToTelemetryDocument);
                    }
                    else if (telemetryAsException != null)
                    {
                        telemetryDocument = this.CreateTelemetryDocument(
                            telemetryAsException,
                            documentStreams,
                            documentStream => documentStream.ExceptionQuotaTracker,
                            documentStream => documentStream.CheckFilters(telemetryAsException, out groupErrors),
                            ConvertExceptionToTelemetryDocument);
                    }
                    else if (telemetryAsEvent != null)
                    {
                        telemetryDocument = this.CreateTelemetryDocument(
                            telemetryAsEvent,
                            documentStreams,
                            documentStream => documentStream.EventQuotaTracker,
                            documentStream => documentStream.CheckFilters(telemetryAsEvent, out groupErrors),
                            ConvertEventToTelemetryDocument);
                    }
                    else if (telemetryAsTrace != null)
                    {
                        telemetryDocument = this.CreateTelemetryDocument(
                            telemetryAsTrace,
                            documentStreams,
                            documentStream => documentStream.TraceQuotaTracker,
                            documentStream => documentStream.CheckFilters(telemetryAsTrace, out groupErrors),
                            ConvertTraceToTelemetryDocument);
                    }

                    if (telemetryDocument != null)
                    {
                        this.dataAccumulatorManager.CurrentDataAccumulator.TelemetryDocuments.Push(telemetryDocument);
                    }

                    this.dataAccumulatorManager.CurrentDataAccumulator.GlobalDocumentQuotaReached = this.globalQuotaTracker.QuotaExhausted;
                }

                // collect calculated metrics
                CollectionConfigurationError[] filteringErrors;
                string projectionError = null;

                if (telemetryAsRequest != null)
                {
                    QuickPulseTelemetryProcessor.ProcessMetrics(
                        configurationAccumulatorLocal,
                        configurationAccumulatorLocal.CollectionConfiguration.RequestMetrics,
                        telemetryAsRequest,
                        out filteringErrors,
                        ref projectionError);
                }
                else if (telemetryAsDependency != null)
                {
                    QuickPulseTelemetryProcessor.ProcessMetrics(
                        configurationAccumulatorLocal,
                        configurationAccumulatorLocal.CollectionConfiguration.DependencyMetrics,
                        telemetryAsDependency,
                        out filteringErrors,
                        ref projectionError);
                }
                else if (telemetryAsException != null)
                {
                    QuickPulseTelemetryProcessor.ProcessMetrics(
                        configurationAccumulatorLocal,
                        configurationAccumulatorLocal.CollectionConfiguration.ExceptionMetrics,
                        telemetryAsException,
                        out filteringErrors,
                        ref projectionError);
                }
                else if (telemetryAsEvent != null)
                {
                    QuickPulseTelemetryProcessor.ProcessMetrics(
                        configurationAccumulatorLocal,
                        configurationAccumulatorLocal.CollectionConfiguration.EventMetrics,
                        telemetryAsEvent,
                        out filteringErrors,
                        ref projectionError);
                }
                else if (telemetryAsTrace != null)
                {
                    QuickPulseTelemetryProcessor.ProcessMetrics(
                        configurationAccumulatorLocal,
                        configurationAccumulatorLocal.CollectionConfiguration.TraceMetrics,
                        telemetryAsTrace,
                        out filteringErrors,
                        ref projectionError);
                }

                //!!! report errors from string[] errors; and string projectionError;
            }
            finally
            {
                // special treatment for RequestTelemetry.Success - restore the value
                if (telemetryAsRequest != null)
                {
                    telemetryAsRequest.Success = originalRequestTelemetrySuccessValue;
                }

                configurationAccumulatorLocal.Release();
            }
        }
        public void QuickPulseTelemetryProcessorHandlesMultipleThreadsCorrectly()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());

            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = "some ikey" });

            // expected data loss if threading is misimplemented is around 10% (established through experiment)
            int taskCount = 10000;
            var tasks = new List<Task>(taskCount);

            for (int i = 0; i < taskCount; i++)
            {
                var requestTelemetry = new RequestTelemetry()
                                           {
                                               ResponseCode = (i % 2 == 0) ? "200" : "500",
                                               Duration = TimeSpan.FromMilliseconds(i),
                                               Context = { InstrumentationKey = "some ikey" }
                                           };

                var task = new Task(() => telemetryProcessor.Process(requestTelemetry));
                tasks.Add(task);
            }

            // ACT
            tasks.ForEach(task => task.Start());

            Task.WaitAll(tasks.ToArray());

            // ASSERT
            Assert.AreEqual(taskCount, accumulatorManager.CurrentDataAccumulator.AIRequestCount);
            Assert.AreEqual(taskCount / 2, accumulatorManager.CurrentDataAccumulator.AIRequestSuccessCount);
        }
        public void QuickPulseTelemetryProcessorDoesNotCollectFullRequestTelemetryItemsOnceQuotaIsExhausted()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var timeProvider = new ClockMock();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy(), timeProvider, 60, 5);
            var instrumentationKey = "some ikey";
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = instrumentationKey });

            // ACT
            int counter = 0;
            for (int i = 0; i < 100; i++)
            {
                var request = new RequestTelemetry()
                                  {
                                      Success = false,
                                      ResponseCode = "400",
                                      Duration = TimeSpan.FromSeconds(counter++),
                                      Context = { InstrumentationKey = instrumentationKey }
                                  };

                telemetryProcessor.Process(request);
            }

            timeProvider.FastForward(TimeSpan.FromSeconds(30));

            for (int i = 0; i < 100; i++)
            {
                var request = new RequestTelemetry()
                {
                    Success = false,
                    ResponseCode = "400",
                    Duration = TimeSpan.FromSeconds(counter++),
                    Context = { InstrumentationKey = instrumentationKey }
                };

                telemetryProcessor.Process(request);
            }

            // ASSERT
            var collectedTelemetry = accumulatorManager.CurrentDataAccumulator.TelemetryDocuments.ToArray().Reverse().Cast<RequestTelemetryDocument>().ToArray();

            Assert.AreEqual(5 + 30, accumulatorManager.CurrentDataAccumulator.TelemetryDocuments.Count);

            // out of the first 100 items we expect to see items 0 through 4 (the initial quota)
            for (int i = 0; i < 5; i++)
            {
                Assert.AreEqual(i, collectedTelemetry[i].Duration.TotalSeconds);
            }

            // out of the second 100 items we expect to see items 100 through 129 (the new quota for 30 seconds)
            for (int i = 5; i < 35; i++)
            {
                Assert.AreEqual(95 + i, collectedTelemetry[i].Duration.TotalSeconds);
            }
        }
        public void QuickPulseTelemetryModuleIsInitializedBySdk()
        {
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());
            var configuration = new TelemetryConfiguration();
            var builder = configuration.TelemetryProcessorChainBuilder;
            builder = builder.Use(current => telemetryProcessor);
            builder.Build();

            new QuickPulseTelemetryModule().Initialize(configuration);
        }
        public void QuickPulseTelemetryProcessorIgnoresTelemetryItemsToDifferentInstrumentationKeys()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());

            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = "some ikey" });

            // ACT
            telemetryProcessor.Process(new RequestTelemetry() { Context = { InstrumentationKey = "some other ikey" } });
            telemetryProcessor.Process(new RequestTelemetry() { Context = { InstrumentationKey = "some ikey" } });

            // ASSERT
            Assert.AreEqual(1, accumulatorManager.CurrentDataAccumulator.AIRequestCount);
        }
        public void QuickPulseTelemetryProcessorIgnoresUnrelatedTelemetryItems()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = "some ikey" });

            // ACT
            telemetryProcessor.Process(new EventTelemetry() { Context = { InstrumentationKey = "some ikey" } });
            telemetryProcessor.Process(new ExceptionTelemetry() { Context = { InstrumentationKey = "some ikey" } });
            telemetryProcessor.Process(new MetricTelemetry() { Context = { InstrumentationKey = "some ikey" } });
            telemetryProcessor.Process(new PageViewTelemetry() { Context = { InstrumentationKey = "some ikey" } });
            telemetryProcessor.Process(new TraceTelemetry() { Context = { InstrumentationKey = "some ikey" } });

            // ASSERT
            Assert.AreEqual(0, accumulatorManager.CurrentDataAccumulator.AIRequestCount);
            Assert.AreEqual(0, accumulatorManager.CurrentDataAccumulator.AIDependencyCallCount);
        }
        public void QuickPulseTelemetryProcessorTruncatesLongFullExceptionTelemetryItemMessage()
        {
            // ARRANGE
            var accumulatorManager = new QuickPulseDataAccumulatorManager();
            var telemetryProcessor = new QuickPulseTelemetryProcessor(new SimpleTelemetryProcessorSpy());
            var instrumentationKey = "some ikey";
            ((IQuickPulseTelemetryProcessor)telemetryProcessor).StartCollection(
                accumulatorManager,
                new Uri("http://microsoft.com"),
                new TelemetryConfiguration() { InstrumentationKey = instrumentationKey });

            // ACT
            var exceptionShort = new ExceptionTelemetry(new ArgumentException(new string('m', MaxFieldLength)))
            {
                Context = { InstrumentationKey = instrumentationKey }
            };

            var exceptionLong = new ExceptionTelemetry(new ArgumentException(new string('m', MaxFieldLength + 1)))
            {
                Context = { InstrumentationKey = instrumentationKey }
            };

            // process in the opposite order to allow for an easier validation order
            telemetryProcessor.Process(exceptionLong);
            telemetryProcessor.Process(exceptionShort);

            // ASSERT
            var telemetryDocuments = accumulatorManager.CurrentDataAccumulator.TelemetryDocuments.Cast<ExceptionTelemetryDocument>().ToList();

            Assert.AreEqual(telemetryDocuments[0].ExceptionMessage, exceptionShort.Exception.Message);
            Assert.AreEqual(telemetryDocuments[1].ExceptionMessage, exceptionLong.Exception.Message.Substring(0, MaxFieldLength));
        }