private static void ProcessMetrics <TTelemetry>(
            CollectionConfigurationAccumulator configurationAccumulatorLocal,
            IEnumerable <CalculatedMetric <TTelemetry> > metrics,
            TTelemetry telemetry,
            out CollectionConfigurationError[] filteringErrors,
            ref string projectionError)
        {
            filteringErrors = new CollectionConfigurationError[] { };

            foreach (CalculatedMetric <TTelemetry> metric in metrics)
            {
                if (metric.CheckFilters(telemetry, out filteringErrors))
                {
                    // the telemetry document has passed the filters, count it in and project
                    try
                    {
                        double projection = metric.Project(telemetry);

                        configurationAccumulatorLocal.MetricAccumulators[metric.Id].AddValue(projection);
                    }
                    catch (Exception e)
                    {
                        // most likely the projection did not result in a value parsable by double.Parse()
                        projectionError = e.ToString();
                    }
                }
            }
        }
        public void CollectionConfigurationAccumulatorPreparesMetricAccumulatorsTest()
        {
            // ARRANGE
            CollectionConfigurationError[] error;
            var metricInfo = new CalculatedMetricInfo()
            {
                Id            = "Metric1",
                TelemetryType = TelemetryType.Request,
                Projection    = "Name",
                Aggregation   = AggregationType.Min,
                FilterGroups  = new FilterConjunctionGroupInfo[0]
            };

            var collectionConfigurationInfo = new CollectionConfigurationInfo()
            {
                Metrics = new[] { metricInfo }
            };
            var collectionConfiguration = new CollectionConfiguration(collectionConfigurationInfo, out error, new ClockMock());

            // ACT
            var accumulator = new CollectionConfigurationAccumulator(collectionConfiguration);

            // ASSERT
            Assert.AreSame(collectionConfiguration, accumulator.CollectionConfiguration);
            Assert.AreEqual("Metric1", accumulator.MetricAccumulators.Single().Key);
        }
        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();
            }
        }