예제 #1
0
        /// <summary />
        /// <param name="seriesCountLimit"></param>
        /// <param name="valuesPerDimensionLimit"></param>
        /// <param name="seriesConfig"></param>
        public MetricConfiguration(
            int seriesCountLimit,
            int valuesPerDimensionLimit,
            IMetricSeriesConfiguration seriesConfig)
        {
            if (seriesCountLimit < 1)
            {
                throw new ArgumentOutOfRangeException(
                          nameof(seriesCountLimit),
                          $"Metrics must allow at least one data series (but {seriesCountLimit} was specified).");
            }

            if (valuesPerDimensionLimit < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(valuesPerDimensionLimit));
            }

            Util.ValidateNotNull(seriesConfig, nameof(seriesConfig));

            SeriesCountLimit        = seriesCountLimit;
            ValuesPerDimensionLimit = valuesPerDimensionLimit;

            SeriesConfig = seriesConfig;

            _hashCode = Util.CombineHashCodes(
                SeriesCountLimit.GetHashCode(),
                ValuesPerDimensionLimit.GetHashCode(),
                SeriesConfig.GetType().FullName.GetHashCode(),
                SeriesConfig.GetHashCode());
        }
        /// <summary>Gets the specified metric, or creates one if no such metric exists.</summary>
        /// <param name="metricIdentifier">The identity of the metric.</param>
        /// <param name="metricConfiguration">@The configutration of the metric.</param>
        /// <returns>A metric witht he specified identify and configuration.</returns>
        /// <exception cref="ArgumentException">If a metric with the specified identify exists,
        /// but its configuration does not match the specified configuration.
        /// You may not change a metric configurations once a metric was created for the first time.
        /// Either specify the same configuration every time, or specify <c>null</c> during every
        /// invocation except the first one. <c>null</c> will match against any previously specified
        /// configuration when retrieving existing metrics, or fall back to the default when
        /// creating new metrics.</exception>
        public Metric GetOrCreate(
            MetricIdentifier metricIdentifier,
            MetricConfiguration metricConfiguration)
        {
            Util.ValidateNotNull(metricIdentifier, nameof(metricIdentifier));

            Metric metric = this.metrics.GetOrAdd(
                metricIdentifier,
                (key) => new Metric(
                    this.metricManager,
                    metricIdentifier,
                    metricConfiguration ?? MetricConfigurations.Common.Default()));

            if (metricConfiguration != null && false == metric.configuration.Equals(metricConfiguration))
            {
                throw new ArgumentException("A Metric with the specified Namespace, Id and dimension names already exists, but it has a configuration"
                                            + " that is different from the specified configuration. You may not change configurations once a"
                                            + " metric was created for the first time. Either specify the same configuration every time, or"
                                            + " specify 'null' during every invocation except the first one. 'Null' will match against any"
                                            + " previously specified configuration when retrieving existing metrics, or fall back to"
                                            + Invariant($" the default when creating new metrics. ({nameof(metricIdentifier)} = \"{metricIdentifier.ToString()}\".)"));
            }

            return(metric);
        }
예제 #3
0
        /// <summary>
        /// We are working on adding a publically exposed method to a future version of the Core SDK so that the reflection employed here is not necesary.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="target"></param>
        public static void CopyTelemetryContext(TelemetryContext source, TelemetryContext target)
        {
            Util.ValidateNotNull(source, nameof(source));
            Util.ValidateNotNull(target, nameof(target));

            // Copy internal tags:
            Action <TelemetryContext, TelemetryContext, string> initializeDelegate = GetDelegate_TelemetryContextInitialize();

            initializeDelegate(target, source, null);

            // Copy public properties:
            IDictionary <string, string> sourceProperties = source.Properties;
            IDictionary <string, string> targetProperties = target.Properties;

            if (targetProperties != null && sourceProperties != null && sourceProperties.Count > 0)
            {
                foreach (KeyValuePair <string, string> property in sourceProperties)
                {
                    if (!String.IsNullOrEmpty(property.Key) && !targetProperties.ContainsKey(property.Key))
                    {
                        targetProperties[property.Key] = property.Value;
                    }
                }
            }

            // Copy iKey:

            if (source.InstrumentationKey != null)
            {
                target.InstrumentationKey = source.InstrumentationKey;
            }
        }
예제 #4
0
        internal object GetOrCreateExtensionStateUnsafe(Func <MetricManager, object> newExtensionStateInstanceFactory)
        {
            object extensionState = _extensionState;

            if (extensionState != null)
            {
                return(extensionState);
            }

            Util.ValidateNotNull(newExtensionStateInstanceFactory, nameof(newExtensionStateInstanceFactory));

            object newExtensionState = null;

            try
            {
                newExtensionState = newExtensionStateInstanceFactory(this);
            }
            catch
            {
                newExtensionState = null;
            }

            if (newExtensionState != null)
            {
                object prevExtensionState = Interlocked.CompareExchange(ref _extensionState, newExtensionState, null);
                extensionState = prevExtensionState ?? newExtensionState;
            }

            return(extensionState);
        }
        /// <summary>@ToDo: Complete documentation before stable release. {659}.</summary>
        /// <param name="source">@ToDo: Complete documentation before stable release. {688}.</param>
        /// <param name="target">@ToDo: Complete documentation before stable release. {859}.</param>
        public static void CopyTelemetryContext(TelemetryContext source, TelemetryContext target)
        {
            Util.ValidateNotNull(source, nameof(source));
            Util.ValidateNotNull(target, nameof(target));

            // Copy internal tags:
            target.Initialize(source, instrumentationKey: null);

            // Copy public properties:
#pragma warning disable CS0618 // Type or member is obsolete
            Utils.CopyDictionary(source.Properties, target.Properties);
#pragma warning restore CS0618 // Type or member is obsolete

            // This check avoids accessing the public accessor GlobalProperties
            // unless needed, to avoid the penalty of ConcurrentDictionary instantiation.
            if (source.GlobalPropertiesValue != null)
            {
                Utils.CopyDictionary(source.GlobalProperties, target.GlobalProperties);
            }

            // Copy iKey:

            if (source.InstrumentationKey != null)
            {
                target.InstrumentationKey = source.InstrumentationKey;
            }
        }
예제 #6
0
        /// <summary>Creates a new instance of <c>MetricConfiguration</c>.</summary>
        /// <param name="seriesCountLimit">How many data time series a metric can contain as a maximum.
        /// Once this limit is reached, calls to <c>TrackValue(..)</c>, <c>TryGetDataSeries(..)</c> and similar
        /// that would normally result in new series will return <c>false</c>.</param>
        /// <param name="valuesPerDimensionLimit">How many different values each of the dimensions of a metric can
        /// have as a maximum.
        /// Once this limit is reached, calls to <c>TrackValue(..)</c>, <c>TryGetDataSeries(..)</c> and similar
        /// that would normally result in new series will return <c>false</c>.</param>
        /// <param name="seriesConfig">The configuration for how each series of this metric should be aggregated.</param>
        public MetricConfiguration(
            int seriesCountLimit,
            int valuesPerDimensionLimit,
            IMetricSeriesConfiguration seriesConfig)
        {
            if (seriesCountLimit < 1)
            {
                throw new ArgumentOutOfRangeException(
                          nameof(seriesCountLimit),
                          Invariant($"Metrics must allow at least one data series (but {seriesCountLimit} was specified)."));
            }

            this.SeriesCountLimit = seriesCountLimit;

            if (valuesPerDimensionLimit < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(valuesPerDimensionLimit));
            }

            for (int d = 0; d < this.valuesPerDimensionLimits.Length; d++)
            {
                this.valuesPerDimensionLimits[d] = valuesPerDimensionLimit;
            }

            Util.ValidateNotNull(seriesConfig, nameof(seriesConfig));
            this.SeriesConfig = seriesConfig;

            this.hashCode = this.ComputeHashCode();
        }
예제 #7
0
        public MeasurementAggregator(MetricSeriesConfigurationForMeasurement configuration, MetricSeries dataSeries, MetricAggregationCycleKind aggregationCycleKind)
            : base(MetricValuesBufferFactory, configuration, dataSeries, aggregationCycleKind)
        {
            Util.ValidateNotNull(configuration, nameof(configuration));

            this.restrictToUInt32Values = configuration.RestrictToUInt32Values;
            this.ResetAggregate();
        }
        public GaugeAggregator(GaugeMetricSeriesConfiguration configuration, MetricSeries dataSeries, MetricAggregationCycleKind aggregationCycleKind)
            : base(MetricValuesBufferFactory, configuration, dataSeries, aggregationCycleKind)
        {
            Util.ValidateNotNull(configuration, nameof(configuration));

            _restrictToUInt32Values = configuration.RestrictToUInt32Values;
            ResetAggregate();
        }
        /// <summary>@ToDo: Complete documentation before stable release. {569}</summary>
        /// <param name="metricIdentifier">@ToDo: Complete documentation before stable release. {108}</param>
        /// <param name="dimensionNamesAndValues">@ToDo: Complete documentation before stable release. {785}</param>
        /// <param name="config">@ToDo: Complete documentation before stable release. {275}</param>
        /// <returns>@ToDo: Complete documentation before stable release. {908}</returns>
        public MetricSeries CreateNewSeries(MetricIdentifier metricIdentifier, IEnumerable <KeyValuePair <string, string> > dimensionNamesAndValues, IMetricSeriesConfiguration config)
        {
            Util.ValidateNotNull(metricIdentifier, nameof(metricIdentifier));
            Util.ValidateNotNull(config, nameof(config));

            var dataSeries = new MetricSeries(this.aggregationManager, metricIdentifier, dimensionNamesAndValues, config);

            return(dataSeries);
        }
예제 #10
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="metricId"></param>
        /// <param name="config"></param>
        /// <returns></returns>
        public MetricSeries CreateNewSeries(string metricId, IMetricSeriesConfiguration config)
        {
            Util.ValidateNotNull(metricId, nameof(metricId));
            Util.ValidateNotNull(config, nameof(config));

            var dataSeries = new MetricSeries(_aggregationManager, metricId, null, config);

            return(dataSeries);
        }
예제 #11
0
        /// <summary>
        /// We are working on adding a publically exposed method to a future version of the Core SDK so that the reflection employed here is not necesary.
        /// </summary>
        /// <param name="telemetryClient"></param>
        /// <returns></returns>
        internal static TelemetryConfiguration GetTelemetryConfiguration(TelemetryClient telemetryClient)
        {
            Util.ValidateNotNull(telemetryClient, nameof(telemetryClient));

            Func <TelemetryClient, TelemetryConfiguration> getTelemetryConfigurationDelegate = GetDelegate_TelemetryClientGetConfiguration();
            TelemetryConfiguration pipeline = getTelemetryConfigurationDelegate(telemetryClient);

            return(pipeline);
        }
        public DataSeriesAggregatorBase(IMetricSeriesConfiguration configuration, MetricSeries dataSeries, MetricAggregationCycleKind aggregationCycleKind)
        {
            Util.ValidateNotNull(configuration, nameof(configuration));

            _dataSeries           = dataSeries;
            _aggregationCycleKind = aggregationCycleKind;
            _isPersistent         = configuration.RequiresPersistentAggregation;

            Reset(default(DateTimeOffset), default(IMetricValueFilter));
        }
예제 #13
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="telemetryPipeline"></param>
        public MetricManager(IMetricTelemetryPipeline telemetryPipeline)
        {
            Util.ValidateNotNull(telemetryPipeline, nameof(telemetryPipeline));

            _telemetryPipeline  = telemetryPipeline;
            _aggregationManager = new MetricAggregationManager();
            _aggregationCycle   = new DefaultAggregationPeriodCycle(_aggregationManager, this);

            _aggregationCycle.Start();
        }
예제 #14
0
        internal MetricSeries(
            MetricAggregationManager aggregationManager,
            string metricId,
            IEnumerable <KeyValuePair <string, string> > dimensionNamesAndValues,
            IMetricSeriesConfiguration configuration)
        {
            Util.ValidateNotNull(aggregationManager, nameof(aggregationManager));
            Util.ValidateNotNull(metricId, nameof(metricId));
            Util.ValidateNotNull(configuration, nameof(configuration));

            _aggregationManager           = aggregationManager;
            _metricId                     = metricId;
            _configuration                = configuration;
            _requiresPersistentAggregator = configuration.RequiresPersistentAggregation;

            var dimNameVals = new Dictionary <string, string>();

            if (dimensionNamesAndValues != null)
            {
                int dimIndex = 0;
                foreach (KeyValuePair <string, string> dimNameVal in dimensionNamesAndValues)
                {
                    if (dimNameVal.Key == null)
                    {
                        throw new ArgumentNullException($"The name for dimension at 0-based index '{dimIndex}' is null.");
                    }

                    if (String.IsNullOrWhiteSpace(dimNameVal.Key))
                    {
                        throw new ArgumentException($"The name for dimension at 0-based index '{dimIndex}' is empty or white-space");
                    }

                    if (dimNameVal.Value == null)
                    {
                        throw new ArgumentNullException($"The value for dimension '{dimNameVal.Key}' number is null.");
                    }

                    if (String.IsNullOrWhiteSpace(dimNameVal.Value))
                    {
                        throw new ArgumentNullException($"The value for dimension '{dimNameVal.Key}' is empty or white-space.");
                    }

                    dimNameVals[dimNameVal.Key] = dimNameVal.Value;
                    dimIndex++;
                }
            }

            _dimensionNamesAndValues = dimNameVals;

            _aggregatorPersistent = null;
            _aggregatorDefault    = null;
            _aggregatorQuickPulse = null;
            _aggregatorCustom     = null;
        }
        /// <summary>Copies the contents of this collection to the specified array.</summary>
        /// <param name="array">An artay.</param>
        /// <param name="arrayIndex">Array index where to start the copy.</param>
        public void CopyTo(Metric[] array, int arrayIndex)
        {
            Util.ValidateNotNull(array, nameof(array));

            if (arrayIndex < 0 || arrayIndex >= array.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(arrayIndex));
            }

            this.metrics.Values.CopyTo(array, arrayIndex);
        }
예제 #16
0
        public DefaultAggregationPeriodCycle(MetricAggregationManager aggregationManager, MetricManager metricManager)
        {
            Util.ValidateNotNull(aggregationManager, nameof(aggregationManager));
            Util.ValidateNotNull(metricManager, nameof(metricManager));

            this.aggregationManager = aggregationManager;
            this.metricManager      = metricManager;

            this.runningState = RunningState_NotStarted;
            this.workerTaskCompletionControl = new TaskCompletionSource <bool>();
            this.aggregationThread           = null;
        }
        public DefaultAggregationPeriodCycle(MetricAggregationManager aggregationManager, MetricManager metricManager)
        {
            Util.ValidateNotNull(aggregationManager, nameof(aggregationManager));
            Util.ValidateNotNull(metricManager, nameof(metricManager));

            _workerMethod = this.Run;

            _aggregationManager = aggregationManager;
            _metricManager      = metricManager;

            _runningState = RunningState_NotStarted;
            _workerTask   = null;
        }
        /// <summary>
        /// </summary>
        /// <param name="metricId"></param>
        /// <param name="aggregationKindMoniker"></param>
        public MetricAggregate(string metricId, string aggregationKindMoniker)
        {
            Util.ValidateNotNull(metricId, nameof(metricId));
            Util.ValidateNotNull(aggregationKindMoniker, nameof(aggregationKindMoniker));

            MetricId = metricId;
            AggregationKindMoniker = aggregationKindMoniker;

            _aggregationPeriodStart    = default(DateTimeOffset);
            _aggregationPeriodDuration = default(TimeSpan);

            Dimensions = new ConcurrentDictionary <string, string>();
            Data       = new ConcurrentDictionary <string, object>();
        }
예제 #19
0
        /// <summary>Creates a new instance of <c>MetricConfiguration</c>.</summary>
        /// <param name="seriesCountLimit">How many data time series a metric can contain as a maximum.
        /// Once this limit is reached, calls to <c>TrackValue(..)</c>, <c>TryGetDataSeries(..)</c> and similar
        /// that would normally result in new series will return <c>false</c>.</param>
        /// <param name="valuesPerDimensionLimits">How many different values each of the dimensions of a metric can
        /// have as a maximum. If this enumeration contains less elements than the number of supported dimensions,
        /// then the last specified element is replicated for subsequent dimensions. If this enumeration contains
        /// too many elements, superflous elements are ignored.
        /// Once this limit is reached, calls to <c>TrackValue(..)</c>, <c>TryGetDataSeries(..)</c> and similar
        /// that would normally result in new series will return <c>false</c>.</param>
        /// <param name="seriesConfig">The configuration for how each series of this metric should be aggregated.</param>
        public MetricConfiguration(
            int seriesCountLimit,
            IEnumerable <int> valuesPerDimensionLimits,
            IMetricSeriesConfiguration seriesConfig)
        {
            if (seriesCountLimit < 1)
            {
                throw new ArgumentOutOfRangeException(
                          nameof(seriesCountLimit),
                          Invariant($"Metrics must allow at least one data series (but {seriesCountLimit} was specified)."));
            }

            this.SeriesCountLimit = seriesCountLimit;

            if (valuesPerDimensionLimits == null)
            {
                throw new ArgumentNullException(nameof(valuesPerDimensionLimits));
            }

            int lastLim = 0, d = 0;

            foreach (int lim in valuesPerDimensionLimits)
            {
                lastLim = lim;

                if (lastLim < 0)
                {
                    throw new ArgumentOutOfRangeException(nameof(valuesPerDimensionLimits) + "[" + d + "]");
                }

                this.valuesPerDimensionLimits[d] = lastLim;

                d++;
                if (d >= MetricIdentifier.MaxDimensionsCount)
                {
                    break;
                }
            }

            for (; d < this.valuesPerDimensionLimits.Length; d++)
            {
                this.valuesPerDimensionLimits[d] = lastLim;
            }

            Util.ValidateNotNull(seriesConfig, nameof(seriesConfig));
            this.SeriesConfig = seriesConfig;

            this.hashCode = this.ComputeHashCode();
        }
예제 #20
0
        public MetricSeriesAggregatorBase(
            Func <MetricValuesBufferBase <TBufferedValue> > metricValuesBufferFactory,
            IMetricSeriesConfiguration configuration,
            MetricSeries dataSeries,
            MetricAggregationCycleKind aggregationCycleKind)
        {
            Util.ValidateNotNull(metricValuesBufferFactory, nameof(metricValuesBufferFactory));
            Util.ValidateNotNull(configuration, nameof(configuration));

            _dataSeries           = dataSeries;
            _aggregationCycleKind = aggregationCycleKind;
            _isPersistent         = configuration.RequiresPersistentAggregation;

            _metricValuesBufferFactory = metricValuesBufferFactory;
            _metricValuesBuffer        = InvokeMetricValuesBufferFactory();

            Reset(default(DateTimeOffset), default(IMetricValueFilter));
        }
예제 #21
0
        /// <summary>@ToDo: Complete documentation before stable release. {659}</summary>
        /// <param name="source">@ToDo: Complete documentation before stable release. {688}</param>
        /// <param name="target">@ToDo: Complete documentation before stable release. {859}</param>
        public static void CopyTelemetryContext(TelemetryContext source, TelemetryContext target)
        {
            Util.ValidateNotNull(source, nameof(source));
            Util.ValidateNotNull(target, nameof(target));

            // Copy internal tags:
            target.Initialize(source, instrumentationKey: null);

            // Copy public properties:
#pragma warning disable CS0618 // Type or member is obsolete
            Utils.CopyDictionary(source.Properties, target.Properties);
#pragma warning restore CS0618 // Type or member is obsolete
            Utils.CopyDictionary(source.GlobalProperties, target.GlobalProperties);

            // Copy iKey:

            if (source.InstrumentationKey != null)
            {
                target.InstrumentationKey = source.InstrumentationKey;
            }
        }
예제 #22
0
        public SimpleUInt32DataSeriesAggregator(IMetricSeriesConfiguration configuration, MetricSeries dataSeries, MetricAggregationCycleKind aggregationCycleKind)
            : base(configuration, dataSeries, aggregationCycleKind)
        {
            Util.ValidateNotNull(configuration, nameof(configuration));

            SimpleMetricSeriesConfiguration simpleSeriesConfig = configuration as SimpleMetricSeriesConfiguration;

            if (simpleSeriesConfig == null)
            {
                throw new ArgumentException(
                          $"{nameof(SimpleUInt32DataSeriesAggregator)} expects a configuration of type {nameof(SimpleMetricSeriesConfiguration)},"
                          + $" however the specified configuration is {configuration?.GetType()?.FullName ?? Util.NullString}.",
                          nameof(configuration));
            }

            if (false == simpleSeriesConfig.RestrictToUInt32Values)
            {
                throw new ArgumentException(
                          $"{nameof(SimpleUInt32DataSeriesAggregator)} expects a configuration of type {nameof(SimpleMetricSeriesConfiguration)}"
                          + $" where 'RestrictToUInt32Values' is TRUE, however it is False.",
                          nameof(configuration));
            }
        }
        internal bool AddAggregator(IMetricSeriesAggregator aggregator, MetricAggregationCycleKind aggregationCycleKind)
        {
            Util.ValidateNotNull(aggregator, nameof(aggregator));

            if (aggregator.DataSeries._configuration.RequiresPersistentAggregation)
            {
                return(AddAggregator(aggregator, _aggregatorsForPersistent));
            }

            switch (aggregationCycleKind)
            {
            case CycleKind.Default:
                return(AddAggregator(aggregator, _aggregatorsForDefault));

            case CycleKind.QuickPulse:
                return(AddAggregator(aggregator, _aggregatorsForQuickPulse));

            case CycleKind.Custom:
                return(AddAggregator(aggregator, _aggregatorsForCustom));

            default:
                throw new ArgumentException($"Unexpected value of {nameof(aggregationCycleKind)}: {aggregationCycleKind}.");
            }
        }
 public void ValidateNotNull()
 {
     Util.ValidateNotNull("foo", "specified name");
     Assert.ThrowsException <ArgumentNullException>(() => Util.ValidateNotNull(null, "specified name"));
 }
 private MetricsCache(MetricManager metricManager)
 {
     Util.ValidateNotNull(metricManager, nameof(metricManager));
     _metricManager = metricManager;
 }
 /// <summary>Initializes a metric collection.</summary>
 /// <param name="metricManager">The manager that owns the scope of this metric collection.</param>
 internal MetricsCollection(MetricManager metricManager)
 {
     Util.ValidateNotNull(metricManager, nameof(metricManager));
     this.metricManager = metricManager;
 }
        /// <summary>Gets the metric with the specified identify, if it exists.</summary>
        /// <param name="metricIdentifier">A metric identity.</param>
        /// <param name="metric">The metric (if it exists) or <c>null</c>.</param>
        /// <returns><c>true</c> if the metric was retrieved, or <c>false</c> otherwise.</returns>
        public bool TryGet(MetricIdentifier metricIdentifier, out Metric metric)
        {
            Util.ValidateNotNull(metricIdentifier, nameof(metricIdentifier));

            return(this.metrics.TryGetValue(metricIdentifier, out metric));
        }
예제 #28
0
        internal MetricSeries(
            MetricAggregationManager aggregationManager,
            MetricIdentifier metricIdentifier,
            IEnumerable <KeyValuePair <string, string> > dimensionNamesAndValues,
            IMetricSeriesConfiguration configuration)
        {
            // Validate and store aggregationManager:
            Util.ValidateNotNull(aggregationManager, nameof(aggregationManager));
            this.aggregationManager = aggregationManager;

            // Validate and store metricIdentifier:
            Util.ValidateNotNull(metricIdentifier, nameof(metricIdentifier));
            this.MetricIdentifier = metricIdentifier;

            // Copy dimensionNamesAndValues, validate values (keys are implicitly validated as they need to match the keys in the identifier):
            var dimNameVals = new Dictionary <string, string>();

            if (dimensionNamesAndValues != null)
            {
                int dimIndex = 0;
                foreach (KeyValuePair <string, string> dimNameVal in dimensionNamesAndValues)
                {
                    if (dimNameVal.Value == null)
                    {
                        throw new ArgumentNullException(Invariant($"The value for dimension '{dimNameVal.Key}' number is null."));
                    }

                    if (String.IsNullOrWhiteSpace(dimNameVal.Value))
                    {
                        throw new ArgumentNullException(Invariant($"The value for dimension '{dimNameVal.Key}' is empty or white-space."));
                    }

                    dimNameVals[dimNameVal.Key] = dimNameVal.Value;
                    dimIndex++;
                }
            }

            // Validate that metricIdentifier and dimensionNamesAndValues contain consistent dimension names:
            if (metricIdentifier.DimensionsCount != dimNameVals.Count)
            {
                throw new ArgumentException(Invariant($"The specified {nameof(metricIdentifier)} contains {metricIdentifier.DimensionsCount} dimensions,")
                                            + Invariant($" however the specified {nameof(dimensionNamesAndValues)} contains {dimNameVals.Count} name-value pairs with unique names."));
            }

            foreach (string dimName in metricIdentifier.GetDimensionNames())
            {
                if (false == dimNameVals.ContainsKey(dimName))
                {
                    throw new ArgumentException(Invariant($"The specified {nameof(metricIdentifier)} contains a dimension named \"{dimName}\",")
                                                + Invariant($" however the specified {nameof(dimensionNamesAndValues)} does not contain an entry for that name."));
                }
            }

            // Store copied dimensionNamesAndValues:
            this.dimensionNamesAndValues = dimNameVals;

            // Validate and store configuration:
            Util.ValidateNotNull(configuration, nameof(configuration));
            this.configuration = configuration;
            this.requiresPersistentAggregator = configuration.RequiresPersistentAggregation;

            // Init other instance vars:
            this.aggregatorPersistent = null;
            this.aggregatorDefault    = null;
            this.aggregatorQuickPulse = null;
            this.aggregatorCustom     = null;
        }