コード例 #1
0
 // Creates a TableQuery for each named metric and returns a dictionary mapping each name to its query
 // Note: The overall start and end times are used in each query since this reduces processing and the query will still work the same on each Nday table
 private static Dictionary <string, TableQuery> GenerateMetricNameQueries(IEnumerable <string> names, string partitionKey, DateTime startTime, DateTime endTime)
 {
     return(names.ToDictionary(name => ShoeboxHelper.TrimAndEscapeKey(name) + "__").ToDictionary(kvp => kvp.Value, kvp =>
                                                                                                 GenerateMetricQuery(
                                                                                                     partitionKey,
                                                                                                     kvp.Key + (DateTime.MaxValue.Ticks - endTime.Ticks).ToString("D19"),
                                                                                                     kvp.Key + (DateTime.MaxValue.Ticks - startTime.Ticks).ToString("D19"))));
 }
コード例 #2
0
        private static string GenerateNamelessMetricFilterString(MetricFilter filter)
        {
            MetricFilter nameless = new MetricFilter()
            {
                TimeGrain = filter.TimeGrain,
                StartTime = filter.StartTime,
                EndTime   = filter.EndTime
            };

            return(ShoeboxHelper.GenerateMetricFilterString(nameless));
        }
コード例 #3
0
 private string GetFilterStringForDefinitions(MetricFilter filter, IEnumerable <MetricDefinition> definitions)
 {
     return(ShoeboxHelper.GenerateMetricFilterString(new MetricFilter()
     {
         TimeGrain = filter.TimeGrain,
         StartTime = filter.StartTime,
         EndTime = filter.EndTime,
         DimensionFilters = filter.DimensionFilters.Where(df =>
                                                          definitions.Any(d => string.Equals(df.Name, d.Name.Value, StringComparison.OrdinalIgnoreCase)))
     }));
 }
コード例 #4
0
        private static string RemoveNamesFromFilterString(string filterString)
        {
            MetricFilter filter   = MetricFilterExpressionParser.Parse(filterString);
            MetricFilter nameless = new MetricFilter()
            {
                TimeGrain = filter.TimeGrain,
                StartTime = filter.StartTime,
                EndTime   = filter.EndTime
            };

            return(ShoeboxHelper.GenerateMetricFilterString(nameless));
        }
コード例 #5
0
        public async Task <MetricListResponse> GetMetricsAsync(string resourceUri, string filterString, CancellationToken cancellationToken)
        {
            // Ensure exactly one '/' at the start
            resourceUri = '/' + resourceUri.TrimStart('/');

            // Generate filter strings
            string metricFilter     = RemoveNamesFromFilterString(filterString);
            string definitionFilter = ShoeboxHelper.GenerateMetricDefinitionFilterString(MetricFilterExpressionParser.Parse(filterString).Names);

            // Get definitions for requested metrics
            IList <MetricDefinition> definitions =
                (await this.Client.MetricDefinitionOperations.GetMetricDefinitionsAsync(
                     resourceUri,
                     definitionFilter,
                     cancellationToken).ConfigureAwait(false)).MetricDefinitionCollection.Value;

            // Get Metrics by definitions
            return(await this.GetMetricsAsync(resourceUri, metricFilter, definitions, cancellationToken).ConfigureAwait(false));
        }
コード例 #6
0
        public async Task <MetricListResponse> GetMetricsAsync(string resourceUri, string filterString, CancellationToken cancellationToken)
        {
            if (resourceUri == null)
            {
                throw new ArgumentNullException("resourceUri");
            }

            // Generate filter strings
            MetricFilter filter = MetricFilterExpressionParser.Parse(filterString);
            string       filterStringNamesOnly = filter.DimensionFilters == null ? null
                : ShoeboxHelper.GenerateMetricDefinitionFilterString(filter.DimensionFilters.Select(df => df.Name));

            // Get definitions for requested metrics
            IList <MetricDefinition> definitions =
                (await this.Client.MetricDefinitionOperations.GetMetricDefinitionsAsync(
                     resourceUri,
                     filterStringNamesOnly,
                     cancellationToken).ConfigureAwait(false)).MetricDefinitionCollection.Value;

            // Get Metrics with definitions
            return(await this.GetMetricsAsync(resourceUri, filterString, definitions, cancellationToken));
        }
コード例 #7
0
        // Generates queries for all metrics by timestamp (timestamp-name rowKey format) and filters the results to the requested metrics (if any)
        // Note: Does not populate Metric fields unrelated to query (i.e. "display name", resourceUri, and properties)
        private static async Task <MetricCollection> GetMetricsByTimestampAsync(MetricFilter filter, MetricLocation location, string invocationId)
        {
            // Find all the tables that fall partially or fully within the timerange
            IEnumerable <CloudTable> tables = GetNdayTables(filter, location);

            // Generate a query for the partition key and time range
            TableQuery query = GenerateMetricTimestampQuery(location.PartitionKey, filter.StartTime, filter.EndTime);

            // Get all the entities for the query
            IEnumerable <DynamicTableEntity> entities = await GetEntitiesAsync(tables, query, invocationId).ConfigureAwait(false);

            // Group entities by (encoded) name
            IEnumerable <IGrouping <string, DynamicTableEntity> > groups = entities.GroupBy(entity => entity.RowKey.Substring(entity.RowKey.LastIndexOf('_') + 1));

            // if names are specified, filter the results to those metrics only
            if (filter.DimensionFilters != null)
            {
                groups = groups.Where(g => filter.DimensionFilters.Select(df => ShoeboxHelper.TrimAndEscapeKey(df.Name)).Contains(g.Key));
            }

            // Construct MetricCollection (list of metrics) by taking each group and converting the entities in that group to MetricValue objects
            return(new MetricCollection()
            {
                Value =
                    groups.Select(g => new Metric()
                {
                    Name = new LocalizableString()
                    {
                        Value = FindMetricName(g.Key, filter.DimensionFilters.Select(df => df.Name))
                    },
                    StartTime = filter.StartTime,
                    EndTime = filter.EndTime,
                    TimeGrain = filter.TimeGrain,
                    MetricValues = g.Select(ResolveMetricEntity).ToList()
                }).ToList()
            });
        }
コード例 #8
0
        public async Task <MetricListResponse> GetMetricsAsync(string resourceUri, string filterString, CancellationToken cancellationToken)
        {
            // Ensure exactly one '/' at the start
            resourceUri = '/' + resourceUri.TrimStart('/');

            // Generate filter strings
            MetricFilter filter                 = MetricFilterExpressionParser.Parse(filterString);
            string       metricFilterString     = GenerateNamelessMetricFilterString(filter);
            string       definitionFilterString = filter.DimensionFilters == null ? null
                : ShoeboxHelper.GenerateMetricDefinitionFilterString(filter.DimensionFilters.Select(df => df.Name));

            // Get definitions for requested metrics
            IList <MetricDefinition> definitions =
                (await this.Client.MetricDefinitionOperations.GetMetricDefinitionsAsync(
                     resourceUri,
                     definitionFilterString,
                     cancellationToken).ConfigureAwait(false)).MetricDefinitionCollection.Value;

            // Separate passthrough metrics with dimensions specified
            IEnumerable <MetricDefinition> passthruDefinitions = definitions.Where(d => !IsShoebox(d, filter.TimeGrain));
            IEnumerable <MetricDefinition> shoeboxDefinitions  = definitions.Where(d => IsShoebox(d, filter.TimeGrain));

            // Get Passthru definitions
            List <Metric> passthruMetrics = new List <Metric>();
            string        invocationId    = TracingAdapter.NextInvocationId.ToString(CultureInfo.InvariantCulture);

            this.LogStartGetMetrics(invocationId, resourceUri, filterString, passthruDefinitions);
            if (passthruDefinitions.Any())
            {
                // Create new filter for passthru metrics
                List <MetricDimension> passthruDimensionFilters = filter.DimensionFilters == null ? new List <MetricDimension>() :
                                                                  filter.DimensionFilters.Where(df => passthruDefinitions.Any(d => string.Equals(d.Name.Value, df.Name, StringComparison.OrdinalIgnoreCase))).ToList();

                foreach (MetricDefinition def in passthruDefinitions
                         .Where(d => !passthruDimensionFilters.Any(pdf => string.Equals(pdf.Name, d.Name.Value, StringComparison.OrdinalIgnoreCase))))
                {
                    passthruDimensionFilters.Add(new MetricDimension()
                    {
                        Name = def.Name.Value
                    });
                }

                MetricFilter passthruFilter = new MetricFilter()
                {
                    TimeGrain        = filter.TimeGrain,
                    StartTime        = filter.StartTime,
                    EndTime          = filter.EndTime,
                    DimensionFilters = passthruDimensionFilters
                };

                // Create passthru filter string
                string passthruFilterString = ShoeboxHelper.GenerateMetricFilterString(passthruFilter);

                // Get Metrics from passthrough (hydra) client
                MetricListResponse passthruResponse = await this.GetMetricsInternalAsync(resourceUri, passthruFilterString, cancellationToken).ConfigureAwait(false);

                passthruMetrics = passthruResponse.MetricCollection.Value.ToList();

                this.LogMetricCountFromResponses(invocationId, passthruMetrics.Count());

                // Fill in values (resourceUri, displayName, unit) from definitions
                CompleteShoeboxMetrics(passthruMetrics, passthruDefinitions, resourceUri);

                // Add empty objects for metrics that had no values come back, ensuring a metric is returned for each definition
                IEnumerable <Metric> emptyMetrics = passthruDefinitions
                                                    .Where(d => !passthruMetrics.Any(m => string.Equals(m.Name.Value, d.Name.Value, StringComparison.OrdinalIgnoreCase)))
                                                    .Select(d => new Metric()
                {
                    Name         = d.Name,
                    Unit         = d.Unit,
                    ResourceId   = resourceUri,
                    StartTime    = filter.StartTime,
                    EndTime      = filter.EndTime,
                    TimeGrain    = filter.TimeGrain,
                    MetricValues = new List <MetricValue>(),
                    Properties   = new Dictionary <string, string>()
                });

                passthruMetrics.AddRange(emptyMetrics);
            }

            // Get Metrics by definitions
            MetricListResponse shoeboxResponse = await this.GetMetricsAsync(resourceUri, metricFilterString, shoeboxDefinitions, cancellationToken).ConfigureAwait(false);

            // Create response (merge and wrap metrics)
            MetricListResponse result = new MetricListResponse()
            {
                RequestId        = Guid.NewGuid().ToString("D"),
                StatusCode       = HttpStatusCode.OK,
                MetricCollection = new MetricCollection()
                {
                    Value = passthruMetrics.Union(shoeboxResponse.MetricCollection.Value).ToList()
                }
            };

            this.LogEndGetMetrics(invocationId, result);

            return(result);
        }
コード例 #9
0
        // Alternate method for getting metrics by passing in the definitions
        // TODO [davmc]: Revisit - this method cannot support dimensions
        public async Task <MetricListResponse> GetMetricsAsync(
            string resourceUri, string filterString, IEnumerable <MetricDefinition> definitions, CancellationToken cancellationToken)
        {
            MetricListResponse result;

            if (definitions == null)
            {
                throw new ArgumentNullException("definitions");
            }

            string invocationId = TracingAdapter.NextInvocationId.ToString(CultureInfo.InvariantCulture);

            this.LogStartGetMetrics(invocationId, resourceUri, filterString, definitions);

            // If no definitions provided, return empty collection
            if (!definitions.Any())
            {
                result = new MetricListResponse()
                {
                    RequestId  = Guid.NewGuid().ToString("D"),
                    StatusCode = HttpStatusCode.OK
                };

                this.LogEndGetMetrics(invocationId, result);

                return(result);
            }

            // Parse MetricFilter
            MetricFilter filter = MetricFilterExpressionParser.Parse(filterString);

            // Names not allowed in filter since the names are in the definitions
            if (filter.DimensionFilters != null && filter.DimensionFilters.Any())
            {
                throw new ArgumentException("Cannot specify names (or dimensions) when MetricDefinitions are included", "filterString");
            }

            // Ensure every definition has at least one availability matching the filter timegrain
            if (!definitions.All(d => d.MetricAvailabilities.Any(a => a.TimeGrain == filter.TimeGrain)))
            {
                throw new ArgumentException("Definition contains no availability for the timeGrain requested", "definitions");
            }

            // Group definitions by location so we can make one request to each location
            Dictionary <MetricAvailability, MetricFilter> groups =
                definitions.GroupBy(d => d.MetricAvailabilities.First(a => a.TimeGrain == filter.TimeGrain),
                                    new AvailabilityComparer()).ToDictionary(g => g.Key, g => new MetricFilter()
            {
                TimeGrain        = filter.TimeGrain,
                StartTime        = filter.StartTime,
                EndTime          = filter.EndTime,
                DimensionFilters = g.Select(d => new MetricDimension()
                {
                    Name = d.Name.Value
                })
            });

            // Get Metrics from each location (group)
            IEnumerable <Task <MetricListResponse> > locationTasks = groups.Select(g => g.Key.Location == null
                    ? this.GetMetricsInternalAsync(resourceUri, ShoeboxHelper.GenerateMetricFilterString(g.Value), cancellationToken)
                    : ShoeboxClient.GetMetricsInternalAsync(g.Value, g.Key.Location, invocationId));

            // Aggregate metrics from all groups
            MetricListResponse[] results = (await Task.Factory.ContinueWhenAll(locationTasks.ToArray(), tasks => tasks.Select(t => t.Result))).ToArray();
            IEnumerable <Metric> metrics = results.Aggregate <MetricListResponse, IEnumerable <Metric> >(
                new List <Metric>(), (list, response) => list.Union(response.MetricCollection.Value));

            this.LogMetricCountFromResponses(invocationId, metrics.Count());

            // Fill in values (resourceUri, displayName, unit) from definitions
            CompleteShoeboxMetrics(metrics, definitions, resourceUri);

            // Add empty objects for metrics that had no values come back, ensuring a metric is returned for each definition
            IEnumerable <Metric> emptyMetrics = definitions
                                                .Where(d => !metrics.Any(m => string.Equals(m.Name.Value, d.Name.Value, StringComparison.OrdinalIgnoreCase)))
                                                .Select(d => new Metric()
            {
                Name         = d.Name,
                Unit         = d.Unit,
                ResourceId   = resourceUri,
                StartTime    = filter.StartTime,
                EndTime      = filter.EndTime,
                TimeGrain    = filter.TimeGrain,
                MetricValues = new List <MetricValue>(),
                Properties   = new Dictionary <string, string>()
            });

            // Create response (merge and wrap metrics)
            result = new MetricListResponse()
            {
                RequestId        = Guid.NewGuid().ToString("D"),
                StatusCode       = HttpStatusCode.OK,
                MetricCollection = new MetricCollection()
                {
                    Value = metrics.Union(emptyMetrics).ToList()
                }
            };

            this.LogEndGetMetrics(invocationId, result);

            return(result);
        }
コード例 #10
0
        public async Task <MetricDefinitionListResponse> GetMetricDefinitionsAsync(string resourceUri, string filterString, CancellationToken cancellationToken)
        {
            MetricDefinitionListResponse result;

            string invocationId = Tracing.NextInvocationId.ToString(CultureInfo.InvariantCulture);

            this.LogStartGetMetricDefinitions(invocationId, resourceUri, filterString);

            // Ensure exactly one '/' at the start
            resourceUri = '/' + resourceUri.TrimStart('/');
            IEnumerable <MetricDefinition> definitions;

            // If no filter string, must request all metric definiitons since we don't know if we have them all
            if (string.IsNullOrWhiteSpace(filterString))
            {
                // request all definitions
                definitions = (await this.GetMetricDefinitionsInternalAsync(resourceUri, string.Empty, CancellationToken.None).ConfigureAwait(false))
                              .MetricDefinitionCollection.Value;

                // cache definitions
                this.Client.Cache[resourceUri] = definitions;

                // wrap and return definitions
                result = new MetricDefinitionListResponse()
                {
                    StatusCode = HttpStatusCode.OK,
                    MetricDefinitionCollection = new MetricDefinitionCollection()
                    {
                        Value = definitions.ToList()
                    }
                };

                this.LogEndGetMetricDefinitions(invocationId, result);

                return(result);
            }

            // Parse the filter and retrieve cached definitions
            IEnumerable <string> names = MetricDefinitionFilterParser.Parse(filterString);

            definitions = this.Client.Cache[resourceUri];

            // Find the names in the filter that don't appear on any of the cached definitions
            IEnumerable <string> missing = definitions == null
                ? names
                : names.Where((n => !definitions.Any(d => string.Equals(d.Name.Value, n, StringComparison.OrdinalIgnoreCase))));

            // Request any missing definitions and update cache (if any)
            if (missing.Any())
            {
                string missingFilter = ShoeboxHelper.GenerateMetricDefinitionFilterString(missing);

                // Request missing definitions
                var missingDefinitions = (await this.GetMetricDefinitionsInternalAsync(resourceUri, missingFilter, cancellationToken).ConfigureAwait(false))
                                         .MetricDefinitionCollection.Value;

                // merge definitions
                definitions = (definitions ?? new MetricDefinition[0]).Union(missingDefinitions);

                // Store the new set of definitions
                this.Client.Cache[resourceUri] = definitions;
            }

            // Filter out the metrics that were cached but not requested and wrap
            result = new MetricDefinitionListResponse()
            {
                StatusCode = HttpStatusCode.OK,
                MetricDefinitionCollection = new MetricDefinitionCollection()
                {
                    Value = definitions.Where(d => names.Contains(d.Name.Value)).ToList()
                }
            };

            this.LogEndGetMetricDefinitions(invocationId, result);

            return(result);
        }
コード例 #11
0
 // This method tries to figure out the original name of the metric from the encoded name
 // Note: Will unescape the name if it is not in the list, but it will not be able to unhash it if it was hashed
 private static string FindMetricName(string encodedName, IEnumerable <string> names)
 {
     return(names.FirstOrDefault(n => string.Equals(ShoeboxHelper.TrimAndEscapeKey(n), encodedName, StringComparison.OrdinalIgnoreCase)) ??
            ShoeboxHelper.UnEscapeKey(encodedName));
 }
コード例 #12
0
        // Generates queries for all metrics by timestamp (timestamp-name rowKey format) and filters the results to the requested metrics (if any)
        // Note: Does not populate Metric fields unrelated to query (i.e. "display name", resourceUri, and properties)
        private static async Task <MetricCollection> GetMetricsByTimestampAsync(MetricFilter filter, MetricLocation location, string invocationId)
        {
            // Find all the tables that fall partially or fully within the timerange
            IEnumerable <CloudTable> tables = GetNdayTables(filter, location);

            // Generate a query for the partition key and time range
            TableQuery query = GenerateMetricTimestampQuery(location.PartitionKey, filter.StartTime, filter.EndTime);

            // Get all the entities for the query
            IEnumerable <DynamicTableEntity> entities = await GetEntitiesAsync(tables, query, invocationId).ConfigureAwait(false);

            ICollection <string> dimensionFilterNames = null;

            if (filter.DimensionFilters != null)
            {
                dimensionFilterNames = new HashSet <string>(filter.DimensionFilters.Select(df => ShoeboxHelper.TrimAndEscapeKey(df.Name)));
            }

            var metricWraps = new Dictionary <string, MetricWrap>();
            var metrics     = new List <Metric>();

            // Iterate over the instances to do conversion and aggregation when needed.
            foreach (var entity in entities)
            {
                string encodedName = GetMetricNameFromRowKeyByTimestampByMetricName(entity.RowKey);

                // When there is filter, skip entities not included in the filter.
                if (dimensionFilterNames != null && !dimensionFilterNames.Contains(encodedName, StringComparer.OrdinalIgnoreCase))
                {
                    continue;
                }

                MetricWrap metricWrap;
                if (!metricWraps.TryGetValue(encodedName, out metricWrap))
                {
                    metricWrap = new MetricWrap
                    {
                        Metric = new Metric()
                        {
                            Name = new LocalizableString()
                            {
                                Value = encodedName
                            },
                            StartTime    = filter.StartTime,
                            EndTime      = filter.EndTime,
                            TimeGrain    = filter.TimeGrain,
                            MetricValues = new List <MetricValue>()
                        },
                        InstanceMetrics = new List <MetricValue>(),
                        GlobalMetrics   = new List <DynamicTableEntity>()
                    };

                    metricWraps[encodedName] = metricWrap;
                    metrics.Add(metricWrap.Metric);
                }

                // Skip aggregated entities
                if (!IsInstanceMetric(entity.RowKey))
                {
                    // We ignore the aggergated metrics if there are instance metrics.
                    if (metricWrap.InstanceMetrics.Count == 0)
                    {
                        metricWrap.GlobalMetrics.Add(entity);
                    }

                    continue;
                }

                MetricValue lastMetricValue = metricWrap.InstanceMetrics.LastOrDefault();
                if (lastMetricValue == null)
                {
                    metricWrap.InstanceMetrics.Add(ResolveMetricEntity(entity));
                }
                else
                {
                    if (lastMetricValue.Timestamp.Ticks == GetTimestampFromIndexTimestampMetricName(entity))
                    {
                        Aggregate(lastMetricValue, entity);
                    }
                    else
                    {
                        metricWrap.InstanceMetrics.Add(ResolveMetricEntity(entity));
                    }
                }
            }

            foreach (var metricWrap in metricWraps.Values)
            {
                // Decide whether to return the aggregation of the instance metrics on the fly or the final value in the storage account
                // If there are instance metrics, the aggregation on the fly is used.
                Metric metric = metricWrap.Metric;
                metric.Name.Value = FindMetricName(metric.Name.Value, dimensionFilterNames);
                if (metricWrap.InstanceMetrics.Count > 0)
                {
                    foreach (var metricValue in metricWrap.InstanceMetrics)
                    {
                        metricValue.Average = metricValue.Total / metricValue.Count;
                    }

                    metric.MetricValues = metricWrap.InstanceMetrics;
                }
                else
                {
                    metric.MetricValues = metricWrap.GlobalMetrics.Select(me => ResolveMetricEntity(me)).ToList();
                }
            }

            return(new MetricCollection()
            {
                Value = metrics
            });
        }