/// <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()); } } }
public Task SendMetricsAsync(OwnerMetaData owner, DateTimeOffset timestamp, LambdaMetricsRecord record) { return(Task.CompletedTask); }