/// <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(); }