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();
                    }
                }
            }
        }
        private void UpdateConfiguration(CollectionConfigurationInfo configurationInfo)
        {
            // we only get here if Etag in the header is different from the current one, but we still want to check if Etag in the body is also different
            if (configurationInfo != null && !string.Equals(configurationInfo.ETag, this.currentConfigurationETag, StringComparison.Ordinal))
            {
                QuickPulseEventSource.Log.CollectionConfigurationUpdating(this.currentConfigurationETag, configurationInfo.ETag, string.Empty);

                this.collectionConfigurationErrors.Clear();

                CollectionConfigurationError[] errors = null;
                try
                {
                    errors = this.onUpdatedConfiguration?.Invoke(configurationInfo);
                }
                catch (Exception e)
                {
                    QuickPulseEventSource.Log.CollectionConfigurationUpdateFailed(this.currentConfigurationETag, configurationInfo.ETag, e.ToInvariantString(), string.Empty);

                    this.collectionConfigurationErrors.Add(
                        CollectionConfigurationError.CreateError(
                            CollectionConfigurationErrorType.CollectionConfigurationFailureToCreateUnexpected,
                            string.Format(CultureInfo.InvariantCulture, "Unexpected error applying configuration. ETag: {0}", configurationInfo.ETag ?? string.Empty),
                            e,
                            Tuple.Create("ETag", configurationInfo.ETag)));
                }

                if (errors != null)
                {
                    this.collectionConfigurationErrors.AddRange(errors);
                }

                this.currentConfigurationETag = configurationInfo.ETag;
            }
        }
        private void UpdatePerformanceCollector(IEnumerable <Tuple <string, string> > performanceCountersToCollect, out CollectionConfigurationError[] errors)
        {
            // all counters that need to be collected according to the new configuration - remove duplicates
            List <Tuple <string, string> > countersToCollect =
                performanceCountersToCollect.GroupBy(counter => counter.Item1, StringComparer.Ordinal)
                .Select(group => group.First())
                .Concat(
                    QuickPulseDefaults.DefaultCountersToCollect.Select(defaultCounter => Tuple.Create(defaultCounter.Value, defaultCounter.Value)))
                .ToList();

            lock (this.performanceCollectorUpdateLock)
            {
                List <PerformanceCounterData> countersCurrentlyCollected = this.performanceCollector.PerformanceCounters.ToList();

                IEnumerable <Tuple <string, string> > countersToRemove =
                    countersCurrentlyCollected.Where(
                        counter => !countersToCollect.Any(c => string.Equals(c.Item1, counter.ReportAs, StringComparison.Ordinal)))
                    .Select(counter => Tuple.Create(counter.ReportAs, counter.OriginalString));

                IEnumerable <Tuple <string, string> > countersToAdd =
                    countersToCollect.Where(
                        counter => !countersCurrentlyCollected.Any(c => string.Equals(c.ReportAs, counter.Item1, StringComparison.Ordinal)));

                // remove counters that should no longer be collected
                foreach (var counter in countersToRemove)
                {
                    this.performanceCollector.RemoveCounter(counter.Item2, counter.Item1);
                }

                var errorsList = new List <CollectionConfigurationError>();

                // add counters that should now be collected
                foreach (var counter in countersToAdd)
                {
                    try
                    {
                        string error;
                        this.performanceCollector.RegisterCounter(counter.Item2, counter.Item1, out error, true);

                        if (!string.IsNullOrWhiteSpace(error))
                        {
                            errorsList.Add(
                                CollectionConfigurationError.CreateError(
                                    CollectionConfigurationErrorType.PerformanceCounterParsing,
                                    string.Format(CultureInfo.InvariantCulture, "Error parsing performance counter: '{0}'. {1}", counter, error),
                                    null,
                                    Tuple.Create("MetricId", counter.Item1)));

                            QuickPulseEventSource.Log.CounterParsingFailedEvent(error, counter.Item2);
                            continue;
                        }

                        QuickPulseEventSource.Log.CounterRegisteredEvent(counter.Item2);
                    }
                    catch (Exception e)
                    {
                        errorsList.Add(
                            CollectionConfigurationError.CreateError(
                                CollectionConfigurationErrorType.PerformanceCounterUnexpected,
                                string.Format(CultureInfo.InvariantCulture, "Unexpected error processing counter '{0}': {1}", counter, e.Message),
                                e,
                                Tuple.Create("MetricId", counter.Item1)));
                        QuickPulseEventSource.Log.CounterRegistrationFailedEvent(e.Message, counter.Item2);
                    }
                }

                errors = errorsList.ToArray();
            }
        }