Esempio n. 1
0
        /// <summary>
        /// Extracts appropriate data points for auto-collected, pre-aggregated metrics from a single <c>DependencyTelemetry</c> item.
        /// </summary>
        /// <param name="fromItem">The telemetry item from which to extract the metric data points.</param>
        /// <param name="isItemProcessed">Whether of not the specified item was processed (aka not ignored) by this extractor.</param>
        public void ExtractMetrics(ITelemetry fromItem, out bool isItemProcessed)
        {
            //// If this item is not a DependencyTelemetry, we will not process it:
            DependencyTelemetry dependencyCall = fromItem as DependencyTelemetry;

            if (dependencyCall == null)
            {
                isItemProcessed = false;
                return;
            }

            Metric dependencyCallMetric = this.dependencyCallDurationMetric;

            //// If there is no Metric, then this extractor has not been properly initialized yet:
            if (dependencyCallMetric == null)
            {
                //// This should be caught and properly logged by the base class:
                throw new InvalidOperationException(Invariant($"Cannot execute {nameof(this.ExtractMetrics)}.")
                                                    + Invariant($" There is no {nameof(this.dependencyCallDurationMetric)}.")
                                                    + Invariant($" Either this metrics extractor has not been initialized, or it has been disposed."));
            }

            //// Get dependency call success status:
            bool   dependencyFailed        = (dependencyCall.Success != null) && (dependencyCall.Success == false);
            string dependnecySuccessString = dependencyFailed ? Boolean.FalseString : Boolean.TrueString;

            //// Now we need to determine which data series to use:
            MetricSeries seriesToTrack = null;

            if (this.MaxDependencyTypesToDiscover == 0)
            {
                // (MaxDependencyTypesToDiscover == 0) means we do not group by Dependency Type.
                // Then, always use "Other" as Dependency Type:

                dependencyCallMetric.TryGetDataSeries(
                    out seriesToTrack,
                    MetricTerms.Autocollection.Metric.DependencyCallDuration.Id,
                    dependnecySuccessString,
                    MetricTerms.Autocollection.DependencyCall.TypeNames.Other);
            }
            else
            {
                // We group by Dependency Type and if dim limit is reached fall back to using "Other":

                string dependencyType = dependencyCall.Type;

                //// If dependency type is not set, we use "Unknown":
                if (string.IsNullOrEmpty(dependencyType))
                {
                    dependencyType = MetricTerms.Autocollection.DependencyCall.TypeNames.Unknown;
                }

                bool canTrack = dependencyCallMetric.TryGetDataSeries(
                    out seriesToTrack,
                    MetricTerms.Autocollection.Metric.DependencyCallDuration.Id,
                    dependnecySuccessString,
                    dependencyType);

                if (false == canTrack)
                {
                    // If dimension cap was reached, use "Other" as dependency type:
                    // (that series has been pre-created, so cap will not apply)
                    dependencyCallMetric.TryGetDataSeries(
                        out seriesToTrack,
                        MetricTerms.Autocollection.Metric.DependencyCallDuration.Id,
                        dependnecySuccessString,
                        MetricTerms.Autocollection.DependencyCall.TypeNames.Other);
                }
            }

            seriesToTrack.TrackValue(dependencyCall.Duration.TotalMilliseconds);
            isItemProcessed = true;
        }
        private void RecordNormalMetric(TelemetryConfiguration telemetryPipeline)
        {
            MetricSeries durationMeric = telemetryPipeline.GetMetricManager().CreateNewSeries(
                "Item Add duration",
                new MetricSeriesConfigurationForMeasurement(restrictToUInt32Values: false));

            MockContainerDataStructure dataStructure = new MockContainerDataStructure((c) => TimeSpan.FromSeconds(c));

            DateTimeOffset experimentStart = new DateTimeOffset(2017, 9, 14, 0, 0, 0, TimeSpan.Zero);

            // Stop the default minute-ly cycle so that it does not interfere with our virtual time debugging:
            Task fireAndForget = telemetryPipeline.GetMetricManager().StopDefaultAggregationCycleAsync();

            telemetryPipeline.GetMetricManager().StartOrCycleAggregators(CycleKind.Custom, experimentStart, futureFilter: null);

            const int ExperimentLengthSecs = 60 * 10;
            const int IntervalLengthSecs   = 60;

            int totalSecs    = 0;
            int intervalSecs = 0;

            int       itemsThisTime   = 0;
            const int maxItemsAtATime = 4;

            int operationsCount = 0;

            while (totalSecs < ExperimentLengthSecs)
            {
                itemsThisTime = (itemsThisTime + 1) % maxItemsAtATime;

                int addItemCount    = 1 + (itemsThisTime + 1) % maxItemsAtATime;
                int removeItemCount = 1 + itemsThisTime % maxItemsAtATime;

                Trace.WriteLine($"{totalSecs})");
                Trace.WriteLine(addItemCount);
                Trace.WriteLine(removeItemCount);
                Trace.WriteLine("");

                TimeSpan duration;

                dataStructure.AddItems(addItemCount, out duration);
                operationsCount++;

                int durationSecs = (int)duration.TotalSeconds;
                durationMeric.TrackValue(durationSecs);

                totalSecs    += durationSecs;
                intervalSecs += durationSecs;

                dataStructure.RemoveItems(removeItemCount, out duration);
                operationsCount++;

                durationSecs = (int)duration.TotalSeconds;
                durationMeric.TrackValue(durationSecs);

                totalSecs    += durationSecs;
                intervalSecs += durationSecs;

                if (intervalSecs >= IntervalLengthSecs)
                {
                    AggregationPeriodSummary aggregatedMetrics = telemetryPipeline.GetMetricManager().StartOrCycleAggregators(
                        CycleKind.Custom,
                        experimentStart.AddSeconds(totalSecs),
                        futureFilter: null);
                    Assert.IsNotNull(aggregatedMetrics);

                    IReadOnlyList <MetricAggregate> aggregates = aggregatedMetrics.NonpersistentAggregates;
                    Assert.IsNotNull(aggregates);
                    Assert.AreEqual(1, aggregates.Count);

                    MetricAggregate aggregate = aggregates[0];
                    Assert.IsNotNull(aggregates);

                    Assert.AreEqual(1.0, aggregate.Data["Min"]);
                    Assert.AreEqual(4.0, aggregate.Data["Max"]);
                    Assert.AreEqual(operationsCount, aggregate.Data["Count"]);
                    Assert.AreEqual("Item Add duration", aggregate.MetricId);
                    Assert.IsNotNull(aggregate.Dimensions);
                    Assert.AreEqual(0, aggregate.Dimensions.Count);
                    Assert.AreEqual((double)intervalSecs, aggregate.Data["Sum"]);
                    Assert.AreEqual(experimentStart.AddSeconds(totalSecs - intervalSecs), aggregate.AggregationPeriodStart);

                    intervalSecs   %= IntervalLengthSecs;
                    operationsCount = 0;

                    Assert.AreEqual(0, intervalSecs, "For the above to work, the number of wirtual secs must exactly fit into IntervalLengthSecs.");
                }
            }
            {
                AggregationPeriodSummary aggregatedMetrics = telemetryPipeline.GetMetricManager().StartOrCycleAggregators(
                    CycleKind.Custom,
                    experimentStart.AddSeconds(totalSecs),
                    futureFilter: null);
                Assert.IsNotNull(aggregatedMetrics);

                IReadOnlyList <MetricAggregate> aggregates = aggregatedMetrics.NonpersistentAggregates;
                Assert.IsNotNull(aggregates);
                Assert.AreEqual(0, aggregates.Count);
            }
            {
                durationMeric.TrackValue("7");
                durationMeric.TrackValue("8");
                durationMeric.TrackValue("9.0");
                totalSecs += 24;
            }
            {
                AggregationPeriodSummary aggregatedMetrics = telemetryPipeline.GetMetricManager().StopAggregators(
                    CycleKind.Custom,
                    experimentStart.AddSeconds(totalSecs));
                Assert.IsNotNull(aggregatedMetrics);

                IReadOnlyList <MetricAggregate> aggregates = aggregatedMetrics.NonpersistentAggregates;
                Assert.IsNotNull(aggregates);

                MetricAggregate aggregate = aggregates[0];
                Assert.IsNotNull(aggregates);

                Assert.AreEqual(7.0, aggregate.Data["Min"]);
                Assert.AreEqual(9.0, aggregate.Data["Max"]);
                Assert.AreEqual(3, aggregate.Data["Count"]);
                Assert.AreEqual("Item Add duration", aggregate.MetricId);
                Assert.IsNotNull(aggregate.Dimensions);
                Assert.AreEqual(0, aggregate.Dimensions.Count);
                Assert.AreEqual(24.0, aggregate.Data["Sum"]);
                Assert.AreEqual(experimentStart.AddSeconds(totalSecs - 24), aggregate.AggregationPeriodStart);
            }
        }
        internal Metric(MetricManager metricManager, string metricId, string dimension1Name, string dimension2Name, IMetricConfiguration configuration)
        {
            Util.ValidateNotNull(metricManager, nameof(metricManager));
            Util.ValidateNotNullOrWhitespace(metricId, nameof(metricId));

            int dimCount;

            EnsureDimensionNamesValid(out dimCount, ref dimension1Name, ref dimension2Name);

            EnsureConfigurationValid(dimCount, configuration);

            _metricManager = metricManager;

            MetricId        = metricId;
            DimensionsCount = dimCount;
            _configuration  = configuration;

            _objectId = GetObjectId(metricId, dimension1Name, dimension2Name);
            _hashCode = _objectId.GetHashCode();

            switch (dimCount)
            {
            case 0:
                _dimensionNames = new string[0];
                _metricSeries   = null;
                break;

            case 1:
                _dimensionNames = new string[1] {
                    dimension1Name
                };

                //_metricSeries = new MultidimensionalCube<string, MetricSeries>(
                //        totalPointsCountLimit:      configuration.SeriesCountLimit - 1,
                //        pointsFactory:              CreateNewMetricSeries,
                //        subdimensionsCountLimits:   new int[1] { configuration.ValuesPerDimensionLimit });

                _metricSeries = new MultidimensionalCube2 <MetricSeries>(
                    totalPointsCountLimit:      configuration.SeriesCountLimit - 1,
                    pointsFactory:              CreateNewMetricSeries,
                    dimensionValuesCountLimits: new int[1] {
                    configuration.ValuesPerDimensionLimit
                });
                break;

            case 2:
                _dimensionNames = new string[2] {
                    dimension1Name, dimension2Name
                };

                //_metricSeries = new MultidimensionalCube<string, MetricSeries>(
                //        totalPointsCountLimit:      configuration.SeriesCountLimit - 1,
                //        pointsFactory:              CreateNewMetricSeries,
                //        subdimensionsCountLimits:   new int[2] { configuration.ValuesPerDimensionLimit, configuration.ValuesPerDimensionLimit });

                _metricSeries = new MultidimensionalCube2 <MetricSeries>(
                    totalPointsCountLimit: configuration.SeriesCountLimit - 1,
                    pointsFactory: CreateNewMetricSeries,
                    dimensionValuesCountLimits: new int[2] {
                    configuration.ValuesPerDimensionLimit, configuration.ValuesPerDimensionLimit
                });
                break;

            default:
                throw new Exception("Internal coding bug. Please report!");
            }

            _zeroDimSeries = CreateNewMetricSeries(dimensionValues: null);

            _zeroDimSeriesList = (dimCount == 0)
                    ? new KeyValuePair <string[], MetricSeries>[1] {
                new KeyValuePair <string[], MetricSeries>(new string[0], _zeroDimSeries)
            }
                    : null;
        }
 /// <summary>
 /// Exposes the <c>Configuration</c> property for users who imported the <c>Microsoft.ApplicationInsights.Metrics.Extensibility</c> namespace.
 /// </summary>
 /// <param name="metricSeries">@ToDo: Complete documentation before stable release. {509}</param>
 /// <returns>@ToDo: Complete documentation before stable release. {867}</returns>
 public static IMetricSeriesConfiguration GetConfiguration(this MetricSeries metricSeries)
 {
     return(metricSeries.configuration);
 }
 /// <summary>
 /// Gets or creates the <c>MetricSeries</c> associated with the specified dimension values.<br />
 /// This overload may only be used with 2-dimensional metrics. Use other overloads to specify a matching number of dimension values for this metric.
 /// </summary>
 /// <param name="series">If this method returns <c>True</c>: Will be set to the <c>MetricSeries</c> associated with the specified dimension value.<br />
 /// Otherwise: Will be set to <c>null</c>.</param>
 /// <param name="dimension1Value">The value of the 1st dimension.</param>
 /// <param name="dimension2Value">The value of the 2nd dimension.</param>
 /// <param name="createIfNotExists">Whether to attempt creating a metric series for the specified dimension values if it does not exist.</param>
 /// <returns><c>True</c> if the <c>MetricSeries</c> indicated by the specified dimension name could be retrieved or created;
 /// <c>False</c> if the indicated series does not could not be retrieved or created because <c>createIfNotExists</c> is <c>false</c>
 /// or because a dimension cap or a metric series cap was reached.</returns>
 /// <exception cref="InvalidOperationException">If the number of specified dimension names does not match the dimensionality of this <c>Metric</c>.</exception>
 public bool TryGetDataSeries(out MetricSeries series, string dimension1Value, string dimension2Value, bool createIfNotExists)
 {
     series = GetMetricSeries(createIfNotExists, dimension1Value, dimension2Value);
     return(series != null);
 }
 /// <summary>
 /// Gets or creates the <c>MetricSeries</c> associated with the specified dimension values.<br />
 /// This overload may only be used with 2-dimensional metrics. Use other overloads to specify a matching number of dimension values for this metric.
 /// </summary>
 /// <param name="series">If this method returns <c>True</c>: Will be set to the <c>MetricSeries</c> associated with the specified dimension value.<br />
 /// Otherwise: Will be set to <c>null</c>.</param>
 /// <param name="dimension1Value">The value of the 1st dimension.</param>
 /// <param name="dimension2Value">The value of the 2nd dimension.</param>
 /// <returns><c>True</c> if the <c>MetricSeries</c> indicated by the specified dimension name could be retrieved (or created);
 /// <c>False</c> if the indicated series could not be retrieved or created because a dimension cap or a metric series cap was reached.</returns>
 /// <exception cref="InvalidOperationException">If the number of specified dimension names does not match the dimensionality of this <c>Metric</c>.</exception>
 public bool TryGetDataSeries(out MetricSeries series, string dimension1Value, string dimension2Value)
 {
     return(TryGetDataSeries(out series, dimension1Value, dimension2Value, createIfNotExists: true));
 }
 /// <summary>
 /// Gets a <c>MetricSeries</c> associated with this metric.<br />
 /// This overload gets the zero-dimensional <c>MetricSeries</c> associated with this metric.
 /// Every metric, regardless of its dimensionality, has such a zero-dimensional <c>MetricSeries</c>.
 /// </summary>
 /// <param name="series">Will be set to the zero-dimensional <c>MetricSeries</c> associated with this metric</param>
 /// <returns><c>True</c>.</returns>
 public bool TryGetDataSeries(out MetricSeries series)
 {
     series = _zeroDimSeries;
     return(true);
 }
 public IMetricSeriesAggregator CreateNewAggregator(MetricSeries dataSeries, MetricAggregationCycleKind aggregationCycleKind)
 {
     return(new MetricSeriesConfigurationForTestingAccumulatorBehavior.Aggregator(this, dataSeries, aggregationCycleKind));
 }
 public Aggregator(MetricSeriesConfigurationForTestingAccumulatorBehavior configuration, MetricSeries dataSeries, MetricAggregationCycleKind aggregationCycleKind)
     : base(MetricValuesBufferFactory, configuration, dataSeries, aggregationCycleKind)
 {
     Util.ValidateNotNull(configuration, nameof(configuration));
     ResetAggregate();
 }