diff --git a/.gitignore b/.gitignore index 8d2d5f2ab..dbe6b8a52 100644 --- a/.gitignore +++ b/.gitignore @@ -455,3 +455,4 @@ MigrationBackup/ .ionide/ # End of https://www.toptal.com/developers/gitignore/api/dotnetcore,visualstudio,visualstudiocode,rider +.DS_Store diff --git a/libraries/src/Amazon.LambdaPowertools.Metrics/Amazon.LambdaPowertools.Metrics.csproj b/libraries/src/Amazon.LambdaPowertools.Metrics/Amazon.LambdaPowertools.Metrics.csproj index 8642d92f3..13d8744b4 100644 --- a/libraries/src/Amazon.LambdaPowertools.Metrics/Amazon.LambdaPowertools.Metrics.csproj +++ b/libraries/src/Amazon.LambdaPowertools.Metrics/Amazon.LambdaPowertools.Metrics.csproj @@ -1,7 +1,16 @@ + + + + netcoreapp3.1 + Amazon.LambdaPowertools.Metrics + 0.0.15 + AWSLABS + Amazon Web Services + true diff --git a/libraries/src/Amazon.LambdaPowertools.Metrics/Class1.cs b/libraries/src/Amazon.LambdaPowertools.Metrics/Class1.cs deleted file mode 100644 index 8ac84b377..000000000 --- a/libraries/src/Amazon.LambdaPowertools.Metrics/Class1.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace Amazon.LambdaPowertools.Metrics -{ - public class Class1 - { - } -} \ No newline at end of file diff --git a/libraries/src/Amazon.LambdaPowertools.Metrics/Metrics.cs b/libraries/src/Amazon.LambdaPowertools.Metrics/Metrics.cs new file mode 100644 index 000000000..1a1326df4 --- /dev/null +++ b/libraries/src/Amazon.LambdaPowertools.Metrics/Metrics.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using Amazon.LambdaPowertools.Metrics.Model; + +namespace Amazon.LambdaPowertools.Metrics +{ + public class Metrics : MetricsManager + { + public Metrics( + string metricsNamespace = null, + string serviceName = null, + Dictionary dimensions = null, + Dictionary> metrics = null, + Dictionary metadata = null) + : base(metricsNamespace, + serviceName, + dimensions, + metrics, + metadata) + { + + } + } +} \ No newline at end of file diff --git a/libraries/src/Amazon.LambdaPowertools.Metrics/MetricsManager.cs b/libraries/src/Amazon.LambdaPowertools.Metrics/MetricsManager.cs new file mode 100644 index 000000000..89d7a05e3 --- /dev/null +++ b/libraries/src/Amazon.LambdaPowertools.Metrics/MetricsManager.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text.Json; +using Amazon.LambdaPowertools.Metrics.Model; + +namespace Amazon.LambdaPowertools.Metrics +{ + + public class MetricsManager : IDisposable + { + [MaxLength(9)] + private Dictionary _dimensions; + public Dictionary Dimensions + { + get { return _dimensions; } + } + + [MaxLength(100)] + private Dictionary> _metrics; + public Dictionary> Metrics + { + get { return _metrics; } + } + + private Dictionary _metadata; + + public Dictionary Metadata + { + get { return _metadata; } + } + + private bool disposedValue; + private static int _metricCount = 0; + + public MetricsManager( + string metricsNamespace = null, + string serviceName = null, + Dictionary dimensions = null, + Dictionary> metrics = null, + Dictionary metadata = null) + { + PowertoolsSettings.SetNamespace(metricsNamespace); + PowertoolsSettings.SetService(serviceName); + + _dimensions = dimensions != null ? dimensions : new Dictionary(); + _metrics = metrics != null ? metrics : new Dictionary>(); + _metadata = metadata != null ? metadata : new Dictionary(); + + if(!string.IsNullOrEmpty(PowertoolsSettings.Service)){ + AddDimension("service", PowertoolsSettings.Service); + } + } + + public void AddDimension(string key, string value) + { + if (_dimensions.ContainsKey(key)) + { + Console.WriteLine($"Dimension '{key}' already exists. Skipping..."); + return; + } + + if(_dimensions.Count == 9) + { + Console.WriteLine($"Maximum number of dimensions (9) reached. Cannot add '{key}' to dimensions."); + return; + } + + _dimensions.Add(key, value); + } + + public virtual void AddMetric(Metric metric) + { + if (_metrics.ContainsKey(metric.Name)) + { + _metrics[metric.Name].Add(metric); + _metricCount++; + } + else { + _metrics.Add(metric.Name, new List{metric}); + _metricCount++; + } + + if (_metricCount == 100) + { + Flush(); + } + } + + public void AddMetadata(string key, dynamic value) + { + + if (_metadata.ContainsKey(key)) + { + Console.WriteLine($"Metadata '{key}' already exists. Skipping..."); + return; + } + + _metadata.Add(key, value); + } + + //TODO: Turn this into a private method once decision has been made on the implementation + public void Flush() + { + var metricObj = new MetricsWrapper + { + Root = new MetricsDefinition + { + Timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(), + CloudWatchMetrics = new List{ + new CloudWatchMetrics{ + Namespace = PowertoolsSettings.Namespace, + Dimensions = new List>{ + ExtractDimensions(Dimensions) + }, + Metrics = ExtractMetricDefinitionSet(Metrics) + } + } + } + }; + + var json = JsonSerializer.Serialize(metricObj, typeof(MetricsWrapper)); + + string result = AddToJson(json, _dimensions, _metadata, _metrics); + + Console.WriteLine(result); + + _metrics.Clear(); + } + + private List ExtractDimensions(Dictionary dimensions) + { + List result = new List(); + + result.AddRange(dimensions.Keys); + + return result; + } + + private List ExtractMetricDefinitionSet(Dictionary> metrics) + { + List metricDefinitionSet = new List(); + + foreach (var item in metrics) + { + metricDefinitionSet.Add(new MetricsDefinitionSet + { + Name = item.Value[0].Name, + Unit = item.Value[0].Unit + }); + } + + return metricDefinitionSet; + } + + private string AddToJson(string json, Dictionary dimensions, Dictionary metadata, Dictionary> metrics) + { + string result = ""; + foreach (var item in dimensions) + { + result += $",\"{item.Key}\":\"{item.Value}\""; + } + + foreach (var item in metadata) + { + result += $",\"{item.Key}\":\"{item.Value}\""; + } + + foreach (var item in metrics.Values) + { + if(item.Count > 1){ + string resultArray = $",\"{item[0].Name}\": ["; + + for (int i = 0; i < item.Count; i++) + { + if(i == item.Count - 1){ + resultArray += $"{item[i].Value}]"; + } + else { + resultArray += $"{item[i].Value},"; + } + } + + result += resultArray; + } + else { + result += $",\"{item[0].Name}\": {item[0].Value}"; + } + + } + + return $"{json.Remove(json.Length - 1)}{result}}}"; + } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + Flush(); + } + + _dimensions = null; + _metrics = null; + _metadata = null; + disposedValue = true; + } + } + + ~MetricsManager() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: false); + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/libraries/src/Amazon.LambdaPowertools.Metrics/Model/Metric.cs b/libraries/src/Amazon.LambdaPowertools.Metrics/Model/Metric.cs new file mode 100644 index 000000000..eeb8b2a3c --- /dev/null +++ b/libraries/src/Amazon.LambdaPowertools.Metrics/Model/Metric.cs @@ -0,0 +1,29 @@ +using Amazon.LambdaPowertools.Metrics.Model; + +namespace Amazon.LambdaPowertools.Metrics +{ + public class Metric + { + private string _name; + public string Name + { + get { return _name; } + set { _name = value; } + } + + private string _unit; + public string Unit + { + get { return _unit; } + set { _unit = value; } + } + + private double _value; + public double Value + { + get { return _value; } + set { _value = value; } + } + + } +} \ No newline at end of file diff --git a/libraries/src/Amazon.LambdaPowertools.Metrics/Model/MetricUnit.cs b/libraries/src/Amazon.LambdaPowertools.Metrics/Model/MetricUnit.cs new file mode 100644 index 000000000..68125f118 --- /dev/null +++ b/libraries/src/Amazon.LambdaPowertools.Metrics/Model/MetricUnit.cs @@ -0,0 +1,32 @@ +namespace Amazon.LambdaPowertools.Metrics.Model +{ + public class MetricUnit + { + public static readonly string Seconds = "Seconds"; + public static readonly string Microseconds = "Microseconds"; + public static readonly string Milliseconds = "Milliseconds"; + public static readonly string Bytes = "Bytes"; + public static readonly string Kilobytes = "Kilobytes"; + public static readonly string Megabytes = "Megabytes"; + public static readonly string Gigabytes = "Gigabytes"; + public static readonly string Terabytes = "Terabytes"; + public static readonly string Bits = "Bits"; + public static readonly string Kilobits = "Kilobits"; + public static readonly string Megabits = "Megabits"; + public static readonly string Gigabits = "Gigabits"; + public static readonly string Terabits = "Terabits"; + public static readonly string Percent = "Percent"; + public static readonly string Count = "Count"; + public static readonly string BytesPerSecond = "Bytes/Second"; + public static readonly string KilobytesPerSecond = "Kilobytes/Second"; + public static readonly string MegabytesPerSecond = "Megabytes/Second"; + public static readonly string GigabytesPerSecond = "Gigabytes/Second"; + public static readonly string TerabytesPerSecond = "Terabytes/Second"; + public static readonly string BitsPerSecond = "Bits/Second"; + public static readonly string KilobitsPerSecond = "Kilobits/Second"; + public static readonly string MegabitsPerSecond = "Megabits/Second"; + public static readonly string GigabitsPerSecond = "Gigabits/Second"; + public static readonly string TerabitsPerSecond = "Terabits/Second"; + public static readonly string CountPerSecond = "Count/Second"; + } +} \ No newline at end of file diff --git a/libraries/src/Amazon.LambdaPowertools.Metrics/Model/MetricsWrapper.cs b/libraries/src/Amazon.LambdaPowertools.Metrics/Model/MetricsWrapper.cs new file mode 100644 index 000000000..fe73348cb --- /dev/null +++ b/libraries/src/Amazon.LambdaPowertools.Metrics/Model/MetricsWrapper.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Amazon.LambdaPowertools.Metrics.Model; + +namespace Amazon.LambdaPowertools.Metrics +{ + + public class MetricsWrapper + { + [JsonPropertyName("_aws")] + public MetricsDefinition Root { get; set; } + } + + public class MetricsDefinition + { + public double Timestamp { get; set; } + public List CloudWatchMetrics { get; set; } + } + + public class CloudWatchMetrics + { + public string Namespace { get; set; } + public List> Dimensions { get; set; } + public List Metrics { get; set; } + } + + public class MetricsDefinitionSet + { + public string Name { get; set; } + public string Unit { get; set; } + } +} \ No newline at end of file diff --git a/libraries/src/Amazon.LambdaPowertools.Metrics/SingleMetric.cs b/libraries/src/Amazon.LambdaPowertools.Metrics/SingleMetric.cs new file mode 100644 index 000000000..1593c7b76 --- /dev/null +++ b/libraries/src/Amazon.LambdaPowertools.Metrics/SingleMetric.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using Amazon.LambdaPowertools.Metrics.Model; + +namespace Amazon.LambdaPowertools.Metrics +{ + public class SingleMetric : MetricsManager + { + public SingleMetric( + string metricsNamespace = null, + string serviceName = null, + Metric metric = null) + : base(metricsNamespace: metricsNamespace, + serviceName: serviceName) + { + AddMetric(metric); + } + + public override void AddMetric(Metric metric) + { + Metrics.Clear(); + Metrics.Add(metric.Name, new List{metric}); + } + } +} \ No newline at end of file diff --git a/libraries/src/Amazon.LambdaPowertools/Amazon.LambdaPowertools.csproj b/libraries/src/Amazon.LambdaPowertools/Amazon.LambdaPowertools.csproj index 8642d92f3..92ccb265e 100644 --- a/libraries/src/Amazon.LambdaPowertools/Amazon.LambdaPowertools.csproj +++ b/libraries/src/Amazon.LambdaPowertools/Amazon.LambdaPowertools.csproj @@ -2,6 +2,11 @@ netcoreapp3.1 + Amazon.LambdaPowertools + 1.0.0 + AWSLABS + Amazon Web Services + true diff --git a/libraries/src/Amazon.LambdaPowertools/Class1.cs b/libraries/src/Amazon.LambdaPowertools/Class1.cs deleted file mode 100644 index 5c9a9d0cc..000000000 --- a/libraries/src/Amazon.LambdaPowertools/Class1.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace Amazon.LambdaPowertools -{ - public class Class1 - { - } -} \ No newline at end of file diff --git a/libraries/src/Amazon.LambdaPowertools/PowertoolsSettings.cs b/libraries/src/Amazon.LambdaPowertools/PowertoolsSettings.cs new file mode 100644 index 000000000..199018c21 --- /dev/null +++ b/libraries/src/Amazon.LambdaPowertools/PowertoolsSettings.cs @@ -0,0 +1,55 @@ +using System; + +namespace Amazon.LambdaPowertools +{ + public static class PowertoolsSettings + { + private static string _namespace; + public static string Namespace + { + get + { + if (!String.IsNullOrEmpty(_namespace)) + { + return _namespace; + } + else + { + return Environment.GetEnvironmentVariable("POWERTOOLS_METRICS_NAMESPACE"); + } + } + } + + private static string _service; + public static string Service + { + get + { + if (!String.IsNullOrEmpty(_service)) + { + return _service; + } + else + { + return Environment.GetEnvironmentVariable("POWERTOOLS_SERVICE_NAME"); + } + } + } + + public static void SetNamespace(string powertoolsNamespace) + { + if (powertoolsNamespace != null) + { + _namespace = powertoolsNamespace; + } + } + + public static void SetService(string powertoolsService) + { + if (powertoolsService != null) + { + _service = powertoolsService; + } + } + } +} \ No newline at end of file