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