public void ExtractMetrics(ITelemetry fromItem, out bool isItemProcessed)
        {
            RequestTelemetry request = fromItem as RequestTelemetry;

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

            bool isFailed = request.Success.HasValue
                                ? (request.Success.Value == false)
                                : false;

            MetricV1 metric = isFailed
                                ? this.responseFailureTimeMetric
                                : this.responseSuccessTimeMetric;

            if (metric != null)
            {
                isItemProcessed = true;
                metric.Track(request.Duration.TotalMilliseconds);
            }
            else
            {
                isItemProcessed = false;
            }
        }
        public void InitializeExtractor(MetricManagerV1 metricManager)
        {
            this.responseSuccessTimeMetric = metricManager.CreateMetric(
                MetricTerms.Autocollection.Metric.RequestDuration.Name,
                new Dictionary <string, string>()
            {
                [MetricTerms.Autocollection.Request.PropertyNames.Success] = Boolean.TrueString,
                [MetricTerms.Autocollection.MetricId.Moniker.Key]          = MetricTerms.Autocollection.Metric.RequestDuration.Id,
            });

            this.responseFailureTimeMetric = metricManager.CreateMetric(
                MetricTerms.Autocollection.Metric.RequestDuration.Name,
                new Dictionary <string, string>()
            {
                [MetricTerms.Autocollection.Request.PropertyNames.Success] = Boolean.FalseString,
                [MetricTerms.Autocollection.MetricId.Moniker.Key]          = MetricTerms.Autocollection.Metric.RequestDuration.Id,
            });
        }
 public SucceessAndFailureMetrics(MetricV1 successMetric, MetricV1 failureMetric)
 {
     this.Success = successMetric;
     this.Failure = failureMetric;
 }
        /// <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;
            }

            MetricManagerV1 thisMetricManager = this.metricManager;
            MetricsCache    thisMetrics       = this.metrics;

            //// If there is no MetricManager, then this extractor has not been properly initialized yet:
            if (thisMetricManager == null)
            {
                //// This will be caught and properly logged by the base class:
                throw new InvalidOperationException("Cannot execute ExtractMetrics because this metrics extractor has not been initialized (no metrics manager).");
            }

            //// Get dependency call success status:
            bool dependencyFailed = (dependencyCall.Success != null) && (dependencyCall.Success == false);

            //// Now we need to determine which data series to use:
            MetricV1 metricToTrack = null;

            if (thisMetrics.MaxDependencyTypesToDiscover == 0)
            {
                //// If auto-discovering dependency types is disabled, just pick series based on success status:
                metricToTrack = dependencyFailed
                                    ? thisMetrics.Default.Failure
                                    : thisMetrics.Default.Success;
            }
            else
            {
                //// Pick series based on dependency type (and success status):
                string dependencyType = dependencyCall.Type;

                if (dependencyType == null || dependencyType.Equals(string.Empty, StringComparison.OrdinalIgnoreCase))
                {
                    //// If dependency type is not set, we use "Unknown":
                    metricToTrack = dependencyFailed
                                        ? thisMetrics.Unknown.Failure
                                        : thisMetrics.Unknown.Success;
                }
                else
                {
                    //// See if we have already discovered the current dependency type:
                    bool previouslyDiscovered = thisMetrics.ByType.TryGetValue(dependencyType, out SucceessAndFailureMetrics typeMetrics);

                    if (previouslyDiscovered)
                    {
                        metricToTrack = dependencyFailed
                                    ? typeMetrics.Failure
                                    : typeMetrics.Success;
                    }
                    else
                    {
                        //// We have not seen the current dependency type yet:

                        if (thisMetrics.ByType.Count >= thisMetrics.MaxDependencyTypesToDiscover)
                        {
                            //// If the limit of types to discover is already reached, just use "Other":
                            metricToTrack = dependencyFailed
                                    ? thisMetrics.Default.Failure
                                    : thisMetrics.Default.Success;
                        }
                        else
                        {
                            //// So we have not yet reached the limit.
                            //// We will need to take a lock to make sure that the number of discovered types is used correctly as a limit.
                            //// Note: this is a very rare case. It is expected to occur only MaxDependencyTypesToDiscover times.
                            //// In case of very high contention, this may happen a little more often,
                            //// but will no longer happen once the MaxDependencyTypesToDiscover limit is reached.

                            const string TypeDiscoveryLimitReachedMessage = "Cannot discover more dependency types because the MaxDependencyTypesToDiscover limit"
                                                                            + " is reached. This is a control-flow exception that should not propagate outside "
                                                                            + "the metric extraction logic.";

                            try
                            {
                                typeMetrics = thisMetrics.ByType.GetOrAdd(
                                    dependencyType,
                                    (depType) =>
                                {
                                    lock (thisMetrics.TypeDiscoveryLock)
                                    {
                                        if (thisMetrics.DependencyTypesDiscoveredCount >= thisMetrics.MaxDependencyTypesToDiscover)
                                        {
                                            throw new InvalidOperationException(TypeDiscoveryLimitReachedMessage);
                                        }

                                        thisMetrics.DependencyTypesDiscoveredCount++;

                                        return(new SucceessAndFailureMetrics(
                                                   thisMetricManager.CreateMetric(
                                                       MetricTerms.Autocollection.Metric.DependencyCallDuration.Name,
                                                       new Dictionary <string, string>()
                                        {
                                            [MetricTerms.Autocollection.DependencyCall.PropertyNames.Success] = Boolean.TrueString,                      // SUCCESS metric
                                            [MetricTerms.Autocollection.MetricId.Moniker.Key] = MetricTerms.Autocollection.Metric.DependencyCallDuration.Id,
                                            [MetricTerms.Autocollection.DependencyCall.PropertyNames.TypeName] = depType,
                                        }),
                                                   thisMetricManager.CreateMetric(
                                                       MetricTerms.Autocollection.Metric.DependencyCallDuration.Name,
                                                       new Dictionary <string, string>()
                                        {
                                            [MetricTerms.Autocollection.DependencyCall.PropertyNames.Success] = Boolean.FalseString,                     // FAILURE metric
                                            [MetricTerms.Autocollection.MetricId.Moniker.Key] = MetricTerms.Autocollection.Metric.DependencyCallDuration.Id,
                                            [MetricTerms.Autocollection.DependencyCall.PropertyNames.TypeName] = depType,
                                        })));
                                    }
                                });
                            }
                            catch (InvalidOperationException ex)
                            {
                                if (!ex.Message.Equals(TypeDiscoveryLimitReachedMessage, StringComparison.Ordinal))
                                {
                                    ExceptionDispatchInfo.Capture(ex).Throw();
                                }

                                //// Limit was reached concurrently. We will use "Other" after all:
                                metricToTrack = dependencyFailed
                                    ? thisMetrics.Default.Failure
                                    : thisMetrics.Default.Success;
                            }

                            //// Use the newly created metric for this newly discovered dependency type:
                            metricToTrack = dependencyFailed
                                    ? typeMetrics.Failure
                                    : typeMetrics.Success;
                        }
                    }
                }
            }

            //// Now that we selected the right metric, track the value:
            isItemProcessed = true;
            metricToTrack.Track(dependencyCall.Duration.TotalMilliseconds);
        }