diff --git a/.gitignore b/.gitignore index 3e593c9a..a927f64f 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ AWS.Lambda.Powertools.sln.DotSettings.user [Oo]bj/** [Bb]in/** .DS_Store +.cache dist/ site/ diff --git a/libraries/src/AWS.Lambda.Powertools.Metrics/Metrics.cs b/libraries/src/AWS.Lambda.Powertools.Metrics/Metrics.cs index 51b86e37..2ddbc539 100644 --- a/libraries/src/AWS.Lambda.Powertools.Metrics/Metrics.cs +++ b/libraries/src/AWS.Lambda.Powertools.Metrics/Metrics.cs @@ -1,12 +1,12 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * + * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at - * + * * http://aws.amazon.com/apache2.0 - * + * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing @@ -31,7 +31,7 @@ public class Metrics : IMetrics, IDisposable /// The instance /// private static IMetrics _instance; - + /// /// The context /// @@ -46,7 +46,7 @@ public class Metrics : IMetrics, IDisposable /// If true, Powertools for AWS Lambda (.NET) will throw an exception on empty metrics when trying to flush /// private readonly bool _raiseOnEmptyMetrics; - + /// /// The capture cold start enabled /// @@ -76,9 +76,8 @@ internal Metrics(IPowertoolsConfigurations powertoolsConfigurations, string name _raiseOnEmptyMetrics = raiseOnEmptyMetrics; _captureColdStartEnabled = captureColdStartEnabled; _context = InitializeContext(nameSpace, service, null); - + _powertoolsConfigurations.SetExecutionEnvironment(this); - } /// @@ -96,9 +95,11 @@ void IMetrics.AddMetric(string key, double value, MetricUnit unit, MetricResolut { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException( - nameof(key), "'AddMetric' method requires a valid metrics key. 'Null' or empty values are not allowed."); - - if (value < 0) { + nameof(key), + "'AddMetric' method requires a valid metrics key. 'Null' or empty values are not allowed."); + + if (value < 0) + { throw new ArgumentException( "'AddMetric' method requires a valid metrics value. Value must be >= 0.", nameof(value)); } @@ -106,8 +107,8 @@ void IMetrics.AddMetric(string key, double value, MetricUnit unit, MetricResolut lock (_lockObj) { var metrics = _context.GetMetrics(); - - if (metrics.Count > 0 && + + if (metrics.Count > 0 && (metrics.Count == PowertoolsConfigurations.MaxMetrics || metrics.FirstOrDefault(x => x.Name == key) ?.Values.Count == PowertoolsConfigurations.MaxMetrics)) @@ -134,7 +135,14 @@ void IMetrics.SetNamespace(string nameSpace) /// Namespace identifier string IMetrics.GetNamespace() { - return _context.GetNamespace(); + try + { + return _context.GetNamespace(); + } + catch + { + return null; + } } /// @@ -143,7 +151,14 @@ string IMetrics.GetNamespace() /// System.String. string IMetrics.GetService() { - return _context.GetService(); + try + { + return _context.GetService(); + } + catch + { + return null; + } } /// @@ -226,7 +241,7 @@ void IMetrics.Flush(bool metricsOverflow) { if (!_captureColdStartEnabled) Console.WriteLine( - "##WARNING## Metrics and Metadata have not been specified. No data will be sent to Cloudwatch Metrics."); + "##User-WARNING## No application metrics to publish. The cold-start metric may be published if enabled. If application metrics should never be empty, consider using 'RaiseOnEmptyMetrics = true'"); } } @@ -283,7 +298,7 @@ public void Dispose() Dispose(true); GC.SuppressFinalize(this); } - + /// /// /// @@ -356,7 +371,7 @@ public static void SetDefaultDimensions(Dictionary defaultDimens { _instance.SetDefaultDimensions(defaultDimensions); } - + /// /// Clears both default dimensions and dimensions lists /// @@ -389,14 +404,14 @@ private void Flush(MetricsContext context) /// Default dimensions list /// Metrics resolution public static void PushSingleMetric(string metricName, double value, MetricUnit unit, string nameSpace = null, - string service = null, Dictionary defaultDimensions = null, MetricResolution metricResolution = MetricResolution.Default) + string service = null, Dictionary defaultDimensions = null, + MetricResolution metricResolution = MetricResolution.Default) { _instance.PushSingleMetric(metricName, value, unit, nameSpace, service, defaultDimensions, metricResolution); } /// - /// Sets global namespace, service name and default dimensions list. Service name is automatically added as a default - /// dimension + /// Sets global namespace, service name and default dimensions list. /// /// Metrics namespace /// Service Name @@ -406,19 +421,26 @@ private MetricsContext InitializeContext(string nameSpace, string service, Dictionary defaultDimensions) { var context = new MetricsContext(); + var defaultDimensionsList = DictionaryToList(defaultDimensions); context.SetNamespace(!string.IsNullOrWhiteSpace(nameSpace) ? nameSpace - : _powertoolsConfigurations.MetricsNamespace); + : _instance.GetNamespace() ?? _powertoolsConfigurations.MetricsNamespace); - context.SetService(!string.IsNullOrWhiteSpace(service) + // this needs to check if service is set through code or env variables + // the default value service_undefined has to be ignored and return null so it is not added as default + // TODO: Check if there is a way to get the default dimensions and if it makes sense + var parsedService = !string.IsNullOrWhiteSpace(service) ? service - : _powertoolsConfigurations.Service); - - var defaultDimensionsList = DictionaryToList(defaultDimensions); + : _powertoolsConfigurations.Service == "service_undefined" + ? null + : _powertoolsConfigurations.Service; - // Add service as a default dimension - defaultDimensionsList.Add(new DimensionSet("Service", context.GetService())); + if (parsedService != null) + { + context.SetService(parsedService); + defaultDimensionsList.Add(new DimensionSet("Service", context.GetService())); + } context.SetDefaultDimensions(defaultDimensionsList); @@ -447,4 +469,4 @@ internal static void ResetForTest() { _instance = null; } -} +} \ No newline at end of file diff --git a/libraries/src/AWS.Lambda.Powertools.Metrics/MetricsAttribute.cs b/libraries/src/AWS.Lambda.Powertools.Metrics/MetricsAttribute.cs index 761bf7bd..0033f560 100644 --- a/libraries/src/AWS.Lambda.Powertools.Metrics/MetricsAttribute.cs +++ b/libraries/src/AWS.Lambda.Powertools.Metrics/MetricsAttribute.cs @@ -66,7 +66,7 @@ namespace AWS.Lambda.Powertools.Metrics; /// /// /// Service -/// string, service name is used for metric dimension across all metrics, by default service_undefined +/// string, service name is used for metric dimension /// /// /// Namespace diff --git a/libraries/src/AWS.Lambda.Powertools.Metrics/Model/MetricDirective.cs b/libraries/src/AWS.Lambda.Powertools.Metrics/Model/MetricDirective.cs index f8ccad76..111f417f 100644 --- a/libraries/src/AWS.Lambda.Powertools.Metrics/Model/MetricDirective.cs +++ b/libraries/src/AWS.Lambda.Powertools.Metrics/Model/MetricDirective.cs @@ -105,7 +105,7 @@ private MetricDirective(string nameSpace, List metrics, List /// All dimension keys. [JsonPropertyName("Dimensions")] - public List AllDimensionKeys + public List> AllDimensionKeys { get { @@ -123,7 +123,8 @@ public List AllDimensionKeys if (defaultKeys.Count == 0) defaultKeys = new List(); - return defaultKeys; + // Wrap the list of strings in another list + return new List> { defaultKeys }; } } diff --git a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/ClearDimensionsTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/ClearDimensionsTests.cs index 1ac09fbe..0a46d6fd 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/ClearDimensionsTests.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/ClearDimensionsTests.cs @@ -22,7 +22,7 @@ public void WhenClearAllDimensions_NoDimensionsInOutput() var metricsOutput = consoleOut.ToString(); // Assert - Assert.Contains("{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Metric Name\",\"Unit\":\"Count\"}],\"Dimensions\":[]", metricsOutput); + Assert.Contains("{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Metric Name\",\"Unit\":\"Count\"}],\"Dimensions\":[[]]", metricsOutput); // Reset MetricsAspect.ResetForTest(); diff --git a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/EMFValidationTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/EMFValidationTests.cs index 79c275ae..0be19d3b 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/EMFValidationTests.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/EMFValidationTests.cs @@ -79,12 +79,12 @@ public void WhenMaxMetricsAreAdded_FlushAutomatically() // flush when it reaches MaxMetrics Assert.Contains( - "{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Metric Name 1\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 2\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 3\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 4\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 5\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 6\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 7\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 8\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 9\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 10\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 11\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 12\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 13\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 14\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 15\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 16\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 17\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 18\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 19\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 20\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 21\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 22\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 23\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 24\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 25\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 26\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 27\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 28\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 29\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 30\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 31\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 32\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 33\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 34\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 35\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 36\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 37\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 38\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 39\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 40\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 41\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 42\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 43\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 44\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 45\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 46\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 47\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 48\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 49\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 50\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 51\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 52\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 53\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 54\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 55\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 56\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 57\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 58\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 59\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 60\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 61\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 62\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 63\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 64\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 65\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 66\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 67\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 68\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 69\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 70\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 71\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 72\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 73\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 74\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 75\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 76\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 77\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 78\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 79\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 80\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 81\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 82\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 83\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 84\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 85\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 86\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 87\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 88\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 89\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 90\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 91\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 92\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 93\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 94\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 95\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 96\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 97\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 98\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 99\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 100\",\"Unit\":\"Count\"}],\"Dimensions\":[\"Service\"]", + "{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Metric Name 1\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 2\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 3\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 4\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 5\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 6\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 7\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 8\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 9\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 10\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 11\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 12\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 13\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 14\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 15\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 16\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 17\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 18\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 19\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 20\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 21\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 22\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 23\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 24\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 25\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 26\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 27\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 28\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 29\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 30\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 31\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 32\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 33\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 34\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 35\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 36\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 37\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 38\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 39\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 40\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 41\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 42\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 43\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 44\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 45\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 46\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 47\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 48\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 49\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 50\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 51\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 52\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 53\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 54\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 55\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 56\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 57\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 58\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 59\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 60\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 61\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 62\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 63\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 64\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 65\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 66\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 67\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 68\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 69\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 70\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 71\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 72\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 73\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 74\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 75\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 76\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 77\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 78\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 79\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 80\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 81\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 82\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 83\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 84\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 85\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 86\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 87\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 88\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 89\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 90\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 91\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 92\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 93\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 94\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 95\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 96\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 97\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 98\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 99\",\"Unit\":\"Count\"},{\"Name\":\"Metric Name 100\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Service\"]]", metricsOutput[0]); // flush the (MaxMetrics + 1) item only Assert.Contains( - "{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Metric Name 101\",\"Unit\":\"Count\"}],\"Dimensions\":[\"Service\"]", + "{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Metric Name 101\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Service\"]]", metricsOutput[1]); } @@ -105,7 +105,7 @@ public void WhenMaxDataPointsAreAddedToTheSameMetric_FlushAutomatically() metricsOutput[0]); // flush the (MaxMetrics + 1) item only - Assert.Contains("[\"Service\"]}]},\"Service\":\"testService\",\"Metric Name\":100}", metricsOutput[1]); + Assert.Contains("\"Dimensions\":[[\"Service\"]]}]},\"Service\":\"testService\",\"Metric Name\":100}", metricsOutput[1]); } [Trait("Category", "EMFLimits")] @@ -141,7 +141,7 @@ public void WhenDimensionsAreAdded_MustExistAsMembers() var metricsOutput = _consoleOut.ToString(); // Assert - Assert.Contains("\"Dimensions\":[\"Service\",\"functionVersion\"]" + Assert.Contains("\"Dimensions\":[[\"Service\",\"functionVersion\"]]" , metricsOutput); Assert.Contains("\"Service\":\"testService\",\"functionVersion\":\"$LATEST\"" , metricsOutput); @@ -157,11 +157,39 @@ public void When_Multiple_DimensionsAreAdded_MustExistAsMembers() var metricsOutput = _consoleOut.ToString(); // Assert - Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"ns1\",\"Metrics\":[{\"Name\":\"Lambda Execute\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[\"Type\",\"Service\"]}]},\"Type\":\"Start\",\"Service\":\"service_undefined\",\"Lambda Execute\":1}", metricsOutput); - - Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"ns2\",\"Metrics\":[{\"Name\":\"Lambda Execute\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[\"Type\",\"SessionId\",\"Service\"]}]},\"Type\":\"Start\",\"SessionId\":\"Unset\",\"Service\":\"service_undefined\",\"Lambda Execute\":1}", metricsOutput); + Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"ColdStart\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Service\"]]}]},\"Service\":\"ServiceName\",\"ColdStart\":1}", metricsOutput); + Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"SingleMetric1\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[[\"Default1\"]]}]},\"Default1\":\"SingleMetric1\",\"SingleMetric1\":1}", metricsOutput); + Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"ns2\",\"Metrics\":[{\"Name\":\"SingleMetric2\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[[\"Default1\",\"Default2\"]]}]},\"Default1\":\"SingleMetric2\",\"Default2\":\"SingleMetric2\",\"SingleMetric2\":1}", metricsOutput); + Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"AddMetric\",\"Unit\":\"Count\",\"StorageResolution\":1},{\"Name\":\"AddMetric2\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[[\"Service\"]]}]},\"Service\":\"ServiceName\",\"AddMetric\":1,\"AddMetric2\":1}", metricsOutput); + } + + [Trait("Category", "SchemaValidation")] + [Fact] + public void When_PushSingleMetric_With_Namespace() + { + // Act + _handler.PushSingleMetricWithNamespace(); + + var metricsOutput = _consoleOut.ToString(); + + // Assert + Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"ExampleApplication\",\"Metrics\":[{\"Name\":\"SingleMetric\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[[\"Default\"]]}]},\"Default\":\"SingleMetric\",\"SingleMetric\":1}", metricsOutput); + } + + [Trait("Category", "SchemaValidation")] + [Fact] + public void When_PushSingleMetric_With_Env_Namespace() + { + // Arrange + Environment.SetEnvironmentVariable("POWERTOOLS_METRICS_NAMESPACE", "EnvNamespace"); - Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Lambda Execute\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[\"Service\",\"Default\",\"SessionId\",\"Type\"]}]},\"Service\":\"testService\",\"Default\":\"Initial\",\"SessionId\":\"MySessionId\",\"Type\":\"Start\",\"Lambda Execute\":1}", metricsOutput); + // Act + _handler.PushSingleMetricWithEnvNamespace(); + + var metricsOutput = _consoleOut.ToString(); + + // Assert + Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"EnvNamespace\",\"Metrics\":[{\"Name\":\"SingleMetric\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[[\"Default\"]]}]},\"Default\":\"SingleMetric\",\"SingleMetric\":1}", metricsOutput); } [Trait("Category", "MetricsImplementation")] @@ -209,7 +237,7 @@ public void WhenDefaultDimensionsSet_ValidInitialization() var result = _consoleOut.ToString(); // Assert - Assert.Contains($"\"Dimensions\":[\"Service\",\"{key}\"]", result); + Assert.Contains($"\"Dimensions\":[[\"Service\",\"{key}\"]]", result); Assert.Contains($"\"CustomDefaultDimension\":\"{value}\"", result); } @@ -246,7 +274,7 @@ public void WhenDefaultDimensionSet_IgnoreDuplicates() var result = _consoleOut.ToString(); // Assert - Assert.Contains("\"Dimensions\":[\"Service\",\"CustomDefaultDimension\"]", result); + Assert.Contains("\"Dimensions\":[[\"Service\",\"CustomDefaultDimension\"]]", result); Assert.Contains("\"CustomDefaultDimension\":\"CustomDefaultDimensionValue\"", result); } @@ -260,7 +288,7 @@ public void WhenMetricsAndMetadataAdded_ValidateOutput() // Assert Assert.Contains( - "CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Time\",\"Unit\":\"Milliseconds\"}],\"Dimensions\":[\"Service\",\"functionVersion\"]}]},\"Service\":\"testService\",\"functionVersion\":\"$LATEST\",\"Time\":100.7,\"env\":\"dev\"}" + "CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Time\",\"Unit\":\"Milliseconds\"}],\"Dimensions\":[[\"Service\",\"functionVersion\"]]}]},\"Service\":\"testService\",\"functionVersion\":\"$LATEST\",\"Time\":100.7,\"env\":\"dev\"}" , result); } @@ -275,7 +303,7 @@ public void When_Metrics_And_Metadata_Added_With_Same_Key_Should_Only_Write_Metr // Assert // No Metadata key was added Assert.Contains( - "CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Time\",\"Unit\":\"Milliseconds\"}],\"Dimensions\":[\"Service\",\"functionVersion\"]}]},\"Service\":\"testService\",\"functionVersion\":\"$LATEST\",\"Time\":100.7}" + "CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Time\",\"Unit\":\"Milliseconds\"}],\"Dimensions\":[[\"Service\",\"functionVersion\"]]}]},\"Service\":\"testService\",\"functionVersion\":\"$LATEST\",\"Time\":100.7}" , result); } @@ -337,7 +365,7 @@ public async Task WhenMetricsAsyncRaceConditionItemSameKeyExists_ValidateLock() // Assert Assert.Contains( - "{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Metric Name\",\"Unit\":\"Count\"}],\"Dimensions\":[\"Service\"]", + "{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Metric Name\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Service\"]]", metricsOutput); } @@ -366,6 +394,7 @@ public void Dispose() { // need to reset instance after each test MetricsAspect.ResetForTest(); + Environment.SetEnvironmentVariable("POWERTOOLS_METRICS_NAMESPACE", null); } } } \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/FunctionHandler.cs b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/FunctionHandler.cs index e29e99a9..f00a8c5f 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/FunctionHandler.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/FunctionHandler.cs @@ -39,25 +39,39 @@ public void AddDimensions() Metrics.AddMetric("TestMetric", 1, MetricUnit.Count); } - [Metrics(Namespace = "dotnet-powertools-test", Service = "testService")] + [Metrics(Namespace = "dotnet-powertools-test", Service = "ServiceName", CaptureColdStart = true)] public void AddMultipleDimensions() { - Metrics.SetDefaultDimensions(new Dictionary { - { "Default", "Initial" } - }); - Metrics.PushSingleMetric("Lambda Execute", 1, MetricUnit.Count, metricResolution: MetricResolution.High, nameSpace: "ns1", + Metrics.PushSingleMetric("SingleMetric1", 1, MetricUnit.Count, metricResolution: MetricResolution.High, defaultDimensions: new Dictionary { - { "Type", "Start" } + { "Default1", "SingleMetric1" } }); - Metrics.PushSingleMetric("Lambda Execute", 1, MetricUnit.Count, metricResolution: MetricResolution.High, nameSpace: "ns2", + Metrics.PushSingleMetric("SingleMetric2", 1, MetricUnit.Count, metricResolution: MetricResolution.High, nameSpace: "ns2", + defaultDimensions: new Dictionary { + { "Default1", "SingleMetric2" }, + { "Default2", "SingleMetric2" } + }); + Metrics.AddMetric("AddMetric", 1, MetricUnit.Count, MetricResolution.High); + Metrics.AddMetric("AddMetric2", 1, MetricUnit.Count, MetricResolution.High); + } + + [Metrics(Namespace = "ExampleApplication")] + public void PushSingleMetricWithNamespace() + { + Metrics.PushSingleMetric("SingleMetric", 1, MetricUnit.Count, metricResolution: MetricResolution.High, + defaultDimensions: new Dictionary { + { "Default", "SingleMetric" } + }); + } + + [Metrics] + public void PushSingleMetricWithEnvNamespace() + { + Metrics.PushSingleMetric("SingleMetric", 1, MetricUnit.Count, metricResolution: MetricResolution.High, defaultDimensions: new Dictionary { - { "Type", "Start" }, - { "SessionId", "Unset" } + { "Default", "SingleMetric" } }); - Metrics.AddMetric("Lambda Execute", 1, MetricUnit.Count, MetricResolution.High); - Metrics.AddDimension("SessionId", "MySessionId"); - Metrics.AddDimension("Type", "Start"); } [Metrics(Namespace = "dotnet-powertools-test", Service = "testService")] diff --git a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/FunctionHandlerTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/FunctionHandlerTests.cs index dd140b40..3469e2e4 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/FunctionHandlerTests.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/FunctionHandlerTests.cs @@ -85,7 +85,7 @@ public void When_LambdaContext_Should_Add_FunctioName_Dimension_CaptureColdStart metricsOutput); Assert.Contains( - "\"Metrics\":[{\"Name\":\"ColdStart\",\"Unit\":\"Count\"}],\"Dimensions\":[\"FunctionName\",\"Service\"]}]}", + "\"Metrics\":[{\"Name\":\"ColdStart\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"FunctionName\",\"Service\"]]}]}", metricsOutput); } @@ -108,7 +108,7 @@ public void When_LambdaContext_And_Parameter_Should_Add_FunctioName_Dimension_Ca metricsOutput); Assert.Contains( - "\"Metrics\":[{\"Name\":\"ColdStart\",\"Unit\":\"Count\"}],\"Dimensions\":[\"FunctionName\",\"Service\"]}]}", + "\"Metrics\":[{\"Name\":\"ColdStart\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"FunctionName\",\"Service\"]]}]}", metricsOutput); } @@ -125,7 +125,7 @@ public void When_No_LambdaContext_Should_Not_Add_FunctioName_Dimension_CaptureCo metricsOutput); Assert.Contains( - "\"Metrics\":[{\"Name\":\"MyMetric\",\"Unit\":\"None\"}],\"Dimensions\":[\"Service\"]}]},\"Service\":\"svc\",\"MyMetric\":1}", + "\"Metrics\":[{\"Name\":\"MyMetric\",\"Unit\":\"None\"}],\"Dimensions\":[[\"Service\"]]}]},\"Service\":\"svc\",\"MyMetric\":1}", metricsOutput); } diff --git a/libraries/tests/e2e/functions/core/metrics/Function/src/Function/TestHelper.cs b/libraries/tests/e2e/functions/core/metrics/Function/src/Function/TestHelper.cs index b7059a62..750c77ab 100644 --- a/libraries/tests/e2e/functions/core/metrics/Function/src/Function/TestHelper.cs +++ b/libraries/tests/e2e/functions/core/metrics/Function/src/Function/TestHelper.cs @@ -17,6 +17,7 @@ public static void TestMethod(APIGatewayProxyRequest apigwProxyEvent, ILambdaCon Metrics.SetDefaultDimensions(DefaultDimensions); Metrics.AddMetric("Invocation", 1, MetricUnit.Count); + Metrics.AddDimension("FunctionName", context.FunctionName); Metrics.AddDimension("Memory","MemoryLimitInMB"); Metrics.AddMetric("Memory with Environment dimension", context.MemoryLimitInMB, MetricUnit.Megabytes); @@ -39,7 +40,7 @@ public static void TestMethod(APIGatewayProxyRequest apigwProxyEvent, ILambdaCon service: "Test", defaultDimensions: new Dictionary { - {"FunctionContext", "$LATEST"} + {"FunctionName", context.FunctionName} }); } } \ No newline at end of file diff --git a/libraries/tests/e2e/functions/core/metrics/Function/test/Function.Tests/Function.Tests.csproj b/libraries/tests/e2e/functions/core/metrics/Function/test/Function.Tests/Function.Tests.csproj index 6881f4cb..aa3f3cb8 100644 --- a/libraries/tests/e2e/functions/core/metrics/Function/test/Function.Tests/Function.Tests.csproj +++ b/libraries/tests/e2e/functions/core/metrics/Function/test/Function.Tests/Function.Tests.csproj @@ -10,6 +10,7 @@ + diff --git a/libraries/tests/e2e/functions/core/metrics/Function/test/Function.Tests/FunctionTests.cs b/libraries/tests/e2e/functions/core/metrics/Function/test/Function.Tests/FunctionTests.cs index 1670dceb..31c91faf 100644 --- a/libraries/tests/e2e/functions/core/metrics/Function/test/Function.Tests/FunctionTests.cs +++ b/libraries/tests/e2e/functions/core/metrics/Function/test/Function.Tests/FunctionTests.cs @@ -1,10 +1,14 @@ using System.Text.Json; +using Amazon.CDK.AWS.CodeDeploy; +using Amazon.CloudWatch; +using Amazon.CloudWatch.Model; using Amazon.Lambda; using Amazon.Lambda.APIGatewayEvents; using Xunit; using Amazon.Lambda.Model; using TestUtils; using Xunit.Abstractions; +using Environment = Amazon.Lambda.Model.Environment; namespace Function.Tests; @@ -13,6 +17,7 @@ public class FunctionTests { private readonly ITestOutputHelper _testOutputHelper; private readonly AmazonLambdaClient _lambdaClient; + private string? _functionName; public FunctionTests(ITestOutputHelper testOutputHelper) { @@ -26,7 +31,9 @@ public FunctionTests(ITestOutputHelper testOutputHelper) [InlineData("E2ETestLambda_ARM_AOT_NET8_metrics")] public async Task AotFunctionTest(string functionName) { - await TestFunction(functionName); + _functionName = functionName; + await ForceColdStart(); + await TestFunction(); } [Theory] @@ -36,61 +43,129 @@ public async Task AotFunctionTest(string functionName) [InlineData("E2ETestLambda_ARM_NET8_metrics")] public async Task FunctionTest(string functionName) { - await TestFunction(functionName); + _functionName = functionName; + await ForceColdStart(); + await TestFunction(); } - internal async Task TestFunction(string functionName) + internal async Task TestFunction() { var request = new InvokeRequest { - FunctionName = functionName, + FunctionName = _functionName, InvocationType = InvocationType.RequestResponse, Payload = await File.ReadAllTextAsync("../../../../../../../../payload.json"), LogType = LogType.Tail }; - // run twice for cold and warm start - for (int i = 0; i < 2; i++) - { - var response = await _lambdaClient.InvokeAsync(request); + // Test cold start + var coldStartResponse = await _lambdaClient.InvokeAsync(request); + ValidateResponse(coldStartResponse, true); - if (string.IsNullOrEmpty(response.LogResult)) - { - Assert.Fail("No LogResult field returned in the response of Lambda invocation."); - } + // Test warm start + var warmStartResponse = await _lambdaClient.InvokeAsync(request); + ValidateResponse(warmStartResponse, false); - var payload = System.Text.Encoding.UTF8.GetString(response.Payload.ToArray()); - var parsedPayload = JsonSerializer.Deserialize(payload); + // Assert cloudwatch + await AssertCloudWatch(); + } - if (parsedPayload == null) - { - Assert.Fail("Failed to parse payload."); - } + private void ValidateResponse(InvokeResponse response, bool isColdStart) + { + if (string.IsNullOrEmpty(response.LogResult)) + { + Assert.Fail("No LogResult field returned in the response of Lambda invocation."); + } - Assert.Equal(200, parsedPayload.StatusCode); - Assert.Equal("HELLO WORLD", parsedPayload.Body); + var payload = System.Text.Encoding.UTF8.GetString(response.Payload.ToArray()); + var parsedPayload = JsonSerializer.Deserialize(payload); - // Assert Output log from Lambda execution - AssertOutputLog(response); + if (parsedPayload == null) + { + Assert.Fail("Failed to parse payload."); } + + Assert.Equal(200, parsedPayload.StatusCode); + Assert.Equal("HELLO WORLD", parsedPayload.Body); + + // Assert Output log from Lambda execution + AssertOutputLog(response, isColdStart); } - private void AssertOutputLog(InvokeResponse response) + private void AssertOutputLog(InvokeResponse response, bool expectedColdStart) { - // Extract and parse log var logResult = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(response.LogResult)); _testOutputHelper.WriteLine(logResult); var output = OutputLogParser.ParseLogSegments(logResult, out var report); var isColdStart = report.initDuration != "N/A"; - var index = 0; + + Assert.Equal(expectedColdStart, isColdStart); + if (isColdStart) { - AssertColdStart(output[index]); - index += 1; + AssertColdStart(output[0]); + AssertSingleMetric(output[1]); + AssertMetricsDimensionsMetadata(output[2]); } + else + { + AssertSingleMetric(output[0]); + AssertMetricsDimensionsMetadata(output[1]); + } + } - AssertSingleMetric(output[index]); - AssertMetricsDimensionsMetadata(output[index + 1]); + private async Task AssertCloudWatch() + { + using var cloudWatchClient = new AmazonCloudWatchClient(); + var request = new ListMetricsRequest + { + Namespace = "Test", + Dimensions = + [ + new DimensionFilter + { + Name = "Service", + Value = "Test" + }, + + new DimensionFilter + { + Name = "FunctionName", + Value = _functionName + } + ] + }; + + var response = await cloudWatchClient.ListMetricsAsync(request); + + Assert.Equal(7, response.Metrics.Count); + + foreach (var metric in response.Metrics) + { + Assert.Equal("Test", metric.Namespace); + + switch (metric.MetricName) + { + case "ColdStart": + case "SingleMetric": + Assert.Equal(2, metric.Dimensions.Count); + Assert.Contains(metric.Dimensions, d => d.Name == "Service" && d.Value == "Test"); + Assert.Contains(metric.Dimensions, d => d.Name == "FunctionName" && d.Value == _functionName); + break; + case "Invocation": + case "Memory with Environment dimension": + case "Standard resolution": + case "High resolution": + case "Default resolution": + Assert.Equal(5, metric.Dimensions.Count); + Assert.Contains(metric.Dimensions, d => d.Name == "Service" && d.Value == "Test"); + Assert.Contains(metric.Dimensions, d => d.Name == "FunctionName" && d.Value == _functionName); + Assert.Contains(metric.Dimensions, d => d.Name == "Memory" && d.Value == "MemoryLimitInMB"); + Assert.Contains(metric.Dimensions, d => d.Name == "Environment" && d.Value == "Prod"); + Assert.Contains(metric.Dimensions, d => d.Name == "Another" && d.Value == "One"); + break; + } + } } private void AssertMetricsDimensionsMetadata(string output) @@ -136,10 +211,11 @@ private void AssertMetricsDimensionsMetadata(string output) Assert.Equal("Count", unitElement5.GetString()); Assert.True(cloudWatchMetricsElement[0].TryGetProperty("Dimensions", out JsonElement dimensionsElement)); - Assert.Equal("Service", dimensionsElement[0].GetString()); - Assert.Equal("Environment", dimensionsElement[1].GetString()); - Assert.Equal("Another", dimensionsElement[2].GetString()); - Assert.Equal("Memory", dimensionsElement[3].GetString()); + Assert.Equal("Service", dimensionsElement[0][0].GetString()); + Assert.Equal("Environment", dimensionsElement[0][1].GetString()); + Assert.Equal("Another", dimensionsElement[0][2].GetString()); + Assert.Equal("FunctionName", dimensionsElement[0][3].GetString()); + Assert.Equal("Memory", dimensionsElement[0][4].GetString()); Assert.True(root.TryGetProperty("Service", out JsonElement serviceElement)); Assert.Equal("Test", serviceElement.GetString()); @@ -150,6 +226,9 @@ private void AssertMetricsDimensionsMetadata(string output) Assert.True(root.TryGetProperty("Another", out JsonElement anotherElement)); Assert.Equal("One", anotherElement.GetString()); + Assert.True(root.TryGetProperty("FunctionName", out JsonElement functionNameElement)); + Assert.Equal(_functionName, functionNameElement.GetString()); + Assert.True(root.TryGetProperty("Memory", out JsonElement memoryElement)); Assert.Equal("MemoryLimitInMB", memoryElement.GetString()); @@ -191,11 +270,11 @@ private void AssertSingleMetric(string output) Assert.Equal("Count", unitElement.GetString()); Assert.True(cloudWatchMetricsElement[0].TryGetProperty("Dimensions", out JsonElement dimensionsElement)); - Assert.Equal("FunctionContext", dimensionsElement[0].GetString()); - Assert.Equal("Service", dimensionsElement[1].GetString()); + Assert.Equal("FunctionName", dimensionsElement[0][0].GetString()); + Assert.Equal("Service", dimensionsElement[0][1].GetString()); - Assert.True(root.TryGetProperty("FunctionContext", out JsonElement functionContextElement)); - Assert.Equal("$LATEST", functionContextElement.GetString()); + Assert.True(root.TryGetProperty("FunctionName", out JsonElement functionNameElement)); + Assert.Equal(_functionName, functionNameElement.GetString()); Assert.True(root.TryGetProperty("Service", out JsonElement serviceElement)); Assert.Equal("Test", serviceElement.GetString()); @@ -221,4 +300,23 @@ private void AssertColdStart(string output) Assert.True(root.TryGetProperty("ColdStart", out JsonElement coldStartElement)); Assert.Equal(1, coldStartElement.GetInt32()); } + + private async Task ForceColdStart() + { + var updateRequest = new UpdateFunctionConfigurationRequest + { + FunctionName = _functionName, + Environment = new Environment + { + Variables = new Dictionary + { + { "ForceColdStart", Guid.NewGuid().ToString() } + } + } + }; + + _ = await _lambdaClient.UpdateFunctionConfigurationAsync(updateRequest); + + await Task.Delay(2000); + } } \ No newline at end of file