/// <summary>
        /// Log a CloudWatch metric. The metric is picked up by CloudWatch logs and automatically ingested as a CloudWatch metric.
        /// </summary>
        /// <param name="logger">The <see cref="ILambdaSharpLogger"/> instance to use.</param>
        /// <param name="namespace">Metric namespace.</param>
        /// <param name="metrics">Enumeration of metrics, including their name, value, and unit.</param>
        /// <param name="dimensionNames">Metric dimensions as comma-separated list (e.g. [ "A", "A,B" ]).</param>
        /// <param name="dimensionValues">Dictionary of dimesion name-value pairs.</param>
        public static void LogMetric(
            this ILambdaSharpLogger logger,
            string @namespace,
            IEnumerable <LambdaMetric> metrics,
            IEnumerable <string> dimensionNames,
            Dictionary <string, string> dimensionValues
            )
        {
            if (!metrics.Any())
            {
                // nothing to do
                return;
            }
            if (dimensionNames.Count() > 9)
            {
                throw new ArgumentException("metric cannot exceed 9 dimensions", nameof(dimensionNames));
            }
            var targets = new Dictionary <string, object>();

            // validate and process metrics
            var index = 0;

            foreach (var metric in metrics)
            {
                if (string.IsNullOrEmpty(metric.Name))
                {
                    throw new ArgumentException($"metric name cannot be empty (index {index})");
                }
                AssertNotReservedName(metric.Name, "metric");
                if (double.IsNaN(metric.Value) || double.IsNegativeInfinity(metric.Value) || double.IsPositiveInfinity(metric.Value))
                {
                    // these values are rejected by CloudWatch metrics
                    throw new ArgumentException($"metric '{metric.Name}' has an out-of-range value");
                }
                if (!targets.TryAdd(metric.Name, metric.Value))
                {
                    throw new ArgumentException($"conflicting metric name: {metric.Name}");
                }
                ++index;
            }

            // validate and process dimension values
            index = 0;
            foreach (var dimensionValue in dimensionValues)
            {
                if (string.IsNullOrEmpty(dimensionValue.Key))
                {
                    throw new ArgumentException($"dimension name cannot be empty (index {index})");
                }
                if (string.IsNullOrEmpty(dimensionValue.Value))
                {
                    throw new ArgumentException($"dimension value cannot be empty (index {index})");
                }
                AssertNotReservedName(dimensionValue.Key, "dimension");
                if (!targets.TryAdd(dimensionValue.Key, dimensionValue.Value))
                {
                    throw new ArgumentException($"conflicting dimension name: {dimensionValue.Key}");
                }
                ++index;
            }

            // validate and process metric dimensions
            var metricDimensions = dimensionNames
                                   .Select(dimension => dimension
                                           .Split(',')
                                           .Select(dim => dim.Trim())
                                           .ToList()
                                           )
                                   .ToList();

            foreach (var metricDimension in metricDimensions.SelectMany(dimension => dimension))
            {
                if (!dimensionValues.ContainsKey(metricDimension))
                {
                    throw new ArgumentException($"missing dimension value: {metricDimension}");
                }
            }

            // create embedded metrics data-structure:
            var record = new LambdaMetricsRecord {
                Aws = new EmbeddedCloudWatchMetrics {
                    CloudWatchMetrics =
                    {
                        new CloudWatchMetrics {
                            Namespace  = @namespace,
                            Dimensions = metricDimensions,
                            Metrics    = metrics.Select(metric => new CloudWatchMetricValue {
                                Name = metric.Name,
                                Unit = ConvertUnit(metric.Unit)
                            }).ToList()
                        }
                    }
                },
                TargetMembers = targets
            };

            // log metric record
            logger.LogRecord(record);

            // local functions
            void AssertNotReservedName(string name, string parameterType)
            {
                switch (name)
                {
                case "_aws":
                case "Source":
                case "Version":
                    throw new ArgumentException($"{parameterType} name cannot be named '{name}'");

                default:
                    break;
                }
            }

            string ConvertUnit(LambdaMetricUnit unit)
            {
                switch (unit)
                {
                // these enum names need to be mapped to their correct CloudWatch metrics unit counterpart
                case LambdaMetricUnit.BytesPerSecond:
                    return("Bytes/Second");

                case LambdaMetricUnit.KilobytesPerSecond:
                    return("Kilobytes/Second");

                case LambdaMetricUnit.MegabytesPerSecond:
                    return("Megabytes/Second");

                case LambdaMetricUnit.GigabytesPerSecond:
                    return("Gigabytes/Second");

                case LambdaMetricUnit.TerabytesPerSecond:
                    return("Terabytes/Second");

                case LambdaMetricUnit.BitsPerSecond:
                    return("Bits/Second");

                case LambdaMetricUnit.KilobitsPerSecond:
                    return("Kilobits/Second");

                case LambdaMetricUnit.MegabitsPerSecond:
                    return("Megabits/Second");

                case LambdaMetricUnit.GigabitsPerSecond:
                    return("Gigabits/Second");

                case LambdaMetricUnit.TerabitsPerSecond:
                    return("Terabits/Second");

                case LambdaMetricUnit.CountPerSecond:
                    return("Count/Second");

                // the remaining enums are good as is
                default:
                    return(unit.ToString());
                }
            }
        }
Example #2
0
 public Task SendMetricsAsync(OwnerMetaData owner, DateTimeOffset timestamp, LambdaMetricsRecord record)
 {
     return(Task.CompletedTask);
 }