diff --git a/docs/core/metrics.md b/docs/core/metrics.md
index 272c40b1..65fb5f50 100644
--- a/docs/core/metrics.md
+++ b/docs/core/metrics.md
@@ -13,6 +13,7 @@ These metrics can be visualized through [Amazon CloudWatch Console](https://aws.
* Validating your metrics against common metric definitions mistakes (for example, metric unit, values, max dimensions, max metrics)
* Metrics are created asynchronously by the CloudWatch service. You do not need any custom stacks, and there is no impact to Lambda function latency
* Context manager to create a one off metric with a different dimension
+* Ahead-of-Time compilation to native code support [AOT](https://docs.aws.amazon.com/lambda/latest/dg/dotnet-native-aot.html) from version 1.7.0
diff --git a/libraries/src/AWS.Lambda.Powertools.Common/Aspects/UniversalWrapperAspect.cs b/libraries/src/AWS.Lambda.Powertools.Common/Aspects/UniversalWrapperAspect.cs
index 8061ef7d..c4b01468 100644
--- a/libraries/src/AWS.Lambda.Powertools.Common/Aspects/UniversalWrapperAspect.cs
+++ b/libraries/src/AWS.Lambda.Powertools.Common/Aspects/UniversalWrapperAspect.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
@@ -15,6 +15,7 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
@@ -32,19 +33,19 @@ public class UniversalWrapperAspect
///
/// The delegate cache
///
- private static readonly Dictionary _delegateCache = new();
+ private static readonly Dictionary DelegateCache = new();
///
/// The asynchronous generic handler
///
- private static readonly MethodInfo _asyncGenericHandler =
+ private static readonly MethodInfo AsyncGenericHandler =
typeof(UniversalWrapperAttribute).GetMethod(nameof(UniversalWrapperAttribute.WrapAsync),
BindingFlags.NonPublic | BindingFlags.Instance);
///
/// The synchronize generic handler
///
- private static readonly MethodInfo _syncGenericHandler =
+ private static readonly MethodInfo SyncGenericHandler =
typeof(UniversalWrapperAttribute).GetMethod(nameof(UniversalWrapperAttribute.WrapSync),
BindingFlags.NonPublic | BindingFlags.Instance);
@@ -94,6 +95,7 @@ public object Handle(
/// Type of the return.
/// The wrappers.
/// Handler.
+ [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "")]
private static Handler CreateMethodHandler(Type returnType, IEnumerable wrappers)
{
var targetParam = Expression.Parameter(typeof(Func), "orig");
@@ -107,13 +109,13 @@ private static Handler CreateMethodHandler(Type returnType, IEnumerable).MakeGenericType(taskType);
- wrapperMethod = _asyncGenericHandler.MakeGenericMethod(taskType);
+ wrapperMethod = AsyncGenericHandler.MakeGenericMethod(taskType);
}
else
{
if (returnType == typeof(void))
returnType = typeof(object);
- wrapperMethod = _syncGenericHandler.MakeGenericMethod(returnType);
+ wrapperMethod = SyncGenericHandler.MakeGenericMethod(returnType);
}
var converArgs = Expression.Parameter(typeof(object[]), "args");
@@ -128,9 +130,9 @@ private static Handler CreateMethodHandler(Type returnType, IEnumerable(Expression.Convert(Expression.Invoke(next, orig_args), typeof(object)),
- targetParam, orig_args, eventArgsParam);
+ var origArgs = Expression.Parameter(typeof(object[]), "orig_args");
+ var handler = Expression.Lambda(Expression.Convert(Expression.Invoke(next, origArgs), typeof(object)),
+ targetParam, origArgs, eventArgsParam);
var handlerCompiled = handler.Compile();
@@ -147,14 +149,14 @@ private static Handler CreateMethodHandler(Type returnType, IEnumerable wrappers)
{
- if (!_delegateCache.TryGetValue(method, out var handler))
- lock (method)
- {
- if (!_delegateCache.TryGetValue(method, out handler))
- _delegateCache[method] = handler = CreateMethodHandler(returnType, wrappers);
- }
-
- return handler;
+ lock (method)
+ {
+ if (!DelegateCache.TryGetValue(method, out var handler))
+ if (!DelegateCache.TryGetValue(method, out handler))
+ DelegateCache[method] = handler = CreateMethodHandler(returnType, wrappers);
+
+ return handler;
+ }
}
///
diff --git a/libraries/src/AWS.Lambda.Powertools.Common/Core/ISystemWrapper.cs b/libraries/src/AWS.Lambda.Powertools.Common/Core/ISystemWrapper.cs
index 98abe1b5..8a035984 100644
--- a/libraries/src/AWS.Lambda.Powertools.Common/Core/ISystemWrapper.cs
+++ b/libraries/src/AWS.Lambda.Powertools.Common/Core/ISystemWrapper.cs
@@ -13,6 +13,8 @@
* permissions and limitations under the License.
*/
+using System.IO;
+
namespace AWS.Lambda.Powertools.Common;
///
@@ -57,4 +59,15 @@ public interface ISystemWrapper
///
///
void SetExecutionEnvironment(T type);
+
+ ///
+ /// Sets console output
+ /// Useful for testing and checking the console output
+ ///
+ /// var consoleOut = new StringWriter();
+ /// SystemWrapper.Instance.SetOut(consoleOut);
+ ///
+ ///
+ /// The TextWriter instance where to write to
+ void SetOut(TextWriter writeTo);
}
\ No newline at end of file
diff --git a/libraries/src/AWS.Lambda.Powertools.Common/Core/SystemWrapper.cs b/libraries/src/AWS.Lambda.Powertools.Common/Core/SystemWrapper.cs
index 6389b3a0..8f42bda4 100644
--- a/libraries/src/AWS.Lambda.Powertools.Common/Core/SystemWrapper.cs
+++ b/libraries/src/AWS.Lambda.Powertools.Common/Core/SystemWrapper.cs
@@ -126,6 +126,12 @@ public void SetExecutionEnvironment(T type)
SetEnvironmentVariable(envName, envValue.ToString());
}
+ ///
+ public void SetOut(TextWriter writeTo)
+ {
+ Console.SetOut(writeTo);
+ }
+
///
/// Parsing the name to conform with the required naming convention for the UserAgent header (PTFeature/Name/Version)
/// Fallback to Assembly Name on exception
diff --git a/libraries/src/AWS.Lambda.Powertools.Metrics/AWS.Lambda.Powertools.Metrics.csproj b/libraries/src/AWS.Lambda.Powertools.Metrics/AWS.Lambda.Powertools.Metrics.csproj
index 77110d09..0e90e99b 100644
--- a/libraries/src/AWS.Lambda.Powertools.Metrics/AWS.Lambda.Powertools.Metrics.csproj
+++ b/libraries/src/AWS.Lambda.Powertools.Metrics/AWS.Lambda.Powertools.Metrics.csproj
@@ -11,8 +11,4 @@
-
-
-
-
diff --git a/libraries/src/AWS.Lambda.Powertools.Metrics/Internal/MetricsAspect.cs b/libraries/src/AWS.Lambda.Powertools.Metrics/Internal/MetricsAspect.cs
new file mode 100644
index 00000000..abbefc5f
--- /dev/null
+++ b/libraries/src/AWS.Lambda.Powertools.Metrics/Internal/MetricsAspect.cs
@@ -0,0 +1,147 @@
+/*
+ * 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
+ * permissions and limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using AspectInjector.Broker;
+using AWS.Lambda.Powertools.Common;
+
+namespace AWS.Lambda.Powertools.Metrics;
+
+///
+/// MetricsAspect class is responsible for capturing ColdStart metric and flushing metrics on function exit.
+/// Scope.Global - means aspect will operate as singleton.
+///
+[Aspect(Scope.Global)]
+public class MetricsAspect
+{
+ ///
+ /// The is cold start
+ ///
+ private static bool _isColdStart;
+
+ ///
+ /// Specify to clear Lambda Context on exit
+ ///
+ private bool _clearLambdaContext;
+
+ ///
+ /// Gets the metrics instance.
+ ///
+ /// The metrics instance.
+ private static IMetrics _metricsInstance;
+
+ static MetricsAspect()
+ {
+ _isColdStart = true;
+ }
+
+ ///
+ /// Runs before the execution of the method marked with the Metrics Attribute
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [Advice(Kind.Before)]
+ public void Before(
+ [Argument(Source.Instance)] object instance,
+ [Argument(Source.Name)] string name,
+ [Argument(Source.Arguments)] object[] args,
+ [Argument(Source.Type)] Type hostType,
+ [Argument(Source.Metadata)] MethodBase method,
+ [Argument(Source.ReturnType)] Type returnType,
+ [Argument(Source.Triggers)] Attribute[] triggers)
+ {
+ // Before running Function
+
+ var trigger = triggers.OfType().First();
+
+ _metricsInstance ??= new Metrics(
+ PowertoolsConfigurations.Instance,
+ trigger.Namespace,
+ trigger.Service,
+ trigger.RaiseOnEmptyMetrics,
+ trigger.CaptureColdStart
+ );
+
+ var eventArgs = new AspectEventArgs
+ {
+ Instance = instance,
+ Type = hostType,
+ Method = method,
+ Name = name,
+ Args = args,
+ ReturnType = returnType,
+ Triggers = triggers
+ };
+
+ if (trigger.CaptureColdStart && _isColdStart)
+ {
+ _isColdStart = false;
+
+ var nameSpace = _metricsInstance.GetNamespace();
+ var service = _metricsInstance.GetService();
+ Dictionary dimensions = null;
+
+ _clearLambdaContext = PowertoolsLambdaContext.Extract(eventArgs);
+
+ if (PowertoolsLambdaContext.Instance is not null)
+ {
+ dimensions = new Dictionary
+ {
+ { "FunctionName", PowertoolsLambdaContext.Instance.FunctionName }
+ };
+ }
+
+ _metricsInstance.PushSingleMetric(
+ "ColdStart",
+ 1.0,
+ MetricUnit.Count,
+ nameSpace,
+ service,
+ dimensions
+ );
+ }
+ }
+
+ ///
+ /// OnExit runs after the execution of the method marked with the Metrics Attribute
+ ///
+ [Advice(Kind.After)]
+ public void Exit()
+ {
+ _metricsInstance.Flush();
+ if (_clearLambdaContext)
+ PowertoolsLambdaContext.Clear();
+ }
+
+
+ ///
+ /// Reset the aspect for testing purposes.
+ ///
+ internal static void ResetForTest()
+ {
+ _metricsInstance = null;
+ _isColdStart = true;
+ Metrics.ResetForTest();
+ PowertoolsLambdaContext.Clear();
+ }
+}
\ No newline at end of file
diff --git a/libraries/src/AWS.Lambda.Powertools.Metrics/Internal/MetricsAspectHandler.cs b/libraries/src/AWS.Lambda.Powertools.Metrics/Internal/MetricsAspectHandler.cs
deleted file mode 100644
index 6209f97a..00000000
--- a/libraries/src/AWS.Lambda.Powertools.Metrics/Internal/MetricsAspectHandler.cs
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * 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
- * permissions and limitations under the License.
- */
-
-using System;
-using System.Collections.Generic;
-using System.Runtime.ExceptionServices;
-using AWS.Lambda.Powertools.Common;
-
-namespace AWS.Lambda.Powertools.Metrics;
-
-///
-/// Class MetricsAspectHandler.
-/// Implements the
-///
-///
-internal class MetricsAspectHandler : IMethodAspectHandler
-{
- ///
- /// The is cold start
- ///
- private static bool _isColdStart = true;
-
- ///
- /// The capture cold start enabled
- ///
- private readonly bool _captureColdStartEnabled;
-
- ///
- /// The metrics
- ///
- private readonly IMetrics _metrics;
-
- ///
- /// Specify to clear Lambda Context on exit
- ///
- private bool _clearLambdaContext;
-
- ///
- /// Creates MetricsAspectHandler to supports running code before and after marked methods.
- ///
- /// AWS.Lambda.Powertools.Metrics Instance
- /// If 'true', captures cold start during Lambda execution
- internal MetricsAspectHandler
- (
- IMetrics metricsInstance,
- bool captureColdStartEnabled
- )
- {
- _metrics = metricsInstance;
- _captureColdStartEnabled = captureColdStartEnabled;
- }
-
- ///
- /// OnExit runs after the execution of the method marked with the Metrics Attribute
- ///
- /// Aspect Arguments
- public void OnExit(AspectEventArgs eventArgs)
- {
- _metrics.Flush();
- if (_clearLambdaContext)
- PowertoolsLambdaContext.Clear();
- }
-
- ///
- /// OnEntry runs before the execution of the method marked with the Metrics Attribute
- ///
- /// Aspect Arguments
- public void OnEntry(AspectEventArgs eventArgs)
- {
- if (!_isColdStart)
- return;
-
- _isColdStart = false;
-
- if (!_captureColdStartEnabled)
- return;
-
- var nameSpace = _metrics.GetNamespace();
- var service = _metrics.GetService();
- Dictionary dimensions = null;
-
- _clearLambdaContext = PowertoolsLambdaContext.Extract(eventArgs);
-
- if (PowertoolsLambdaContext.Instance is not null)
- {
- dimensions = new Dictionary
- {
- {"FunctionName", PowertoolsLambdaContext.Instance.FunctionName}
- };
- }
-
- _metrics.PushSingleMetric(
- "ColdStart",
- 1.0,
- MetricUnit.Count,
- nameSpace,
- service,
- dimensions
- );
- }
-
- ///
- /// OnSuccess run after successful execution of the method marked with the Metrics Attribute
- ///
- /// Aspect Arguments
- /// Object returned by the method marked with Metrics Attribute
- public void OnSuccess(AspectEventArgs eventArgs, object result)
- {
- }
-
- ///
- /// OnException runs when an unhandled exception occurs inside the method marked with the Metrics Attribute
- ///
- /// Aspect Arguments
- /// Exception thrown by the method marked with Metrics Attribute
- /// Generic unhandled exception
- public void OnException(AspectEventArgs eventArgs, Exception exception)
- {
- // The purpose of ExceptionDispatchInfo.Capture is to capture a potentially mutating exception's StackTrace at a point in time:
- // https://learn.microsoft.com/en-us/dotnet/standard/exceptions/best-practices-for-exceptions#capture-exceptions-to-rethrow-later
- ExceptionDispatchInfo.Capture(exception).Throw();
- }
-
- ///
- /// Helper method for testing purposes. Clears static instance between test execution
- ///
- internal void ResetForTest()
- {
- _isColdStart = true;
- Metrics.ResetForTest();
- PowertoolsLambdaContext.Clear();
- }
-}
\ 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 d0025fc4..761bf7bd 100644
--- a/libraries/src/AWS.Lambda.Powertools.Metrics/MetricsAttribute.cs
+++ b/libraries/src/AWS.Lambda.Powertools.Metrics/MetricsAttribute.cs
@@ -14,6 +14,7 @@
*/
using System;
+using AspectInjector.Broker;
using AWS.Lambda.Powertools.Common;
namespace AWS.Lambda.Powertools.Metrics;
@@ -96,13 +97,13 @@ namespace AWS.Lambda.Powertools.Metrics;
/// }
///
///
-[AttributeUsage(AttributeTargets.Method)]
-public class MetricsAttribute : MethodAspectAttribute
+[Injection(typeof(MetricsAspect))]
+public class MetricsAttribute : Attribute
{
- ///
- /// The metrics instance
- ///
- private IMetrics _metricsInstance;
+ // ///
+ // /// The metrics instance
+ // ///
+ // private IMetrics _metricsInstance;
///
/// Set namespace.
@@ -129,30 +130,4 @@ public class MetricsAttribute : MethodAspectAttribute
///
/// true if [raise on empty metrics]; otherwise, false .
public bool RaiseOnEmptyMetrics { get; set; }
-
- ///
- /// Gets the metrics instance.
- ///
- /// The metrics instance.
- private IMetrics MetricsInstance =>
- _metricsInstance ??= new Metrics(
- PowertoolsConfigurations.Instance,
- Namespace,
- Service,
- RaiseOnEmptyMetrics,
- CaptureColdStart
- );
-
- ///
- /// Creates the handler.
- ///
- /// IMethodAspectHandler.
- protected override IMethodAspectHandler CreateHandler()
- {
- return new MetricsAspectHandler
- (
- MetricsInstance,
- CaptureColdStart
- );
- }
}
diff --git a/libraries/src/AWS.Lambda.Powertools.Metrics/Model/MetricResolution.cs b/libraries/src/AWS.Lambda.Powertools.Metrics/Model/MetricResolution.cs
index 3c6536ff..2112c1c5 100644
--- a/libraries/src/AWS.Lambda.Powertools.Metrics/Model/MetricResolution.cs
+++ b/libraries/src/AWS.Lambda.Powertools.Metrics/Model/MetricResolution.cs
@@ -6,7 +6,6 @@ namespace AWS.Lambda.Powertools.Metrics;
///
/// Enum MetricResolution
///
-// [JsonConverter(typeof(StringEnumConverter))]
public enum MetricResolution
{
///
diff --git a/libraries/src/AWS.Lambda.Powertools.Metrics/Model/MetricUnit.cs b/libraries/src/AWS.Lambda.Powertools.Metrics/Model/MetricUnit.cs
index fd4b9760..132b10f3 100644
--- a/libraries/src/AWS.Lambda.Powertools.Metrics/Model/MetricUnit.cs
+++ b/libraries/src/AWS.Lambda.Powertools.Metrics/Model/MetricUnit.cs
@@ -21,7 +21,11 @@ namespace AWS.Lambda.Powertools.Metrics;
///
/// Enum MetricUnit
///
+#if NET8_0_OR_GREATER
+[JsonConverter(typeof(JsonStringEnumConverter))]
+#else
[JsonConverter(typeof(StringEnumConverter))]
+#endif
public enum MetricUnit
{
///
diff --git a/libraries/src/AWS.Lambda.Powertools.Metrics/Model/RootNode.cs b/libraries/src/AWS.Lambda.Powertools.Metrics/Model/RootNode.cs
index a7783819..496606cd 100644
--- a/libraries/src/AWS.Lambda.Powertools.Metrics/Model/RootNode.cs
+++ b/libraries/src/AWS.Lambda.Powertools.Metrics/Model/RootNode.cs
@@ -65,6 +65,11 @@ public string Serialize()
{
if (string.IsNullOrWhiteSpace(AWS.GetNamespace())) throw new SchemaValidationException("namespace");
+#if NET8_0_OR_GREATER
+
+ return JsonSerializer.Serialize(this, typeof(RootNode), MetricsSerializationContext.Default);
+#else
return JsonSerializer.Serialize(this);
+#endif
}
}
\ No newline at end of file
diff --git a/libraries/src/AWS.Lambda.Powertools.Metrics/Serializer/MetricsSerializationContext.cs b/libraries/src/AWS.Lambda.Powertools.Metrics/Serializer/MetricsSerializationContext.cs
new file mode 100644
index 00000000..e8a421ac
--- /dev/null
+++ b/libraries/src/AWS.Lambda.Powertools.Metrics/Serializer/MetricsSerializationContext.cs
@@ -0,0 +1,40 @@
+/*
+ * 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
+ * permissions and limitations under the License.
+ */
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace AWS.Lambda.Powertools.Metrics;
+
+#if NET8_0_OR_GREATER
+///
+/// Source generator for Metrics types
+///
+[JsonSerializable(typeof(string))]
+[JsonSerializable(typeof(double))]
+[JsonSerializable(typeof(List))]
+[JsonSerializable(typeof(MetricUnit))]
+[JsonSerializable(typeof(MetricDefinition))]
+[JsonSerializable(typeof(DimensionSet))]
+[JsonSerializable(typeof(Metadata))]
+[JsonSerializable(typeof(MetricDirective))]
+[JsonSerializable(typeof(MetricResolution))]
+[JsonSerializable(typeof(MetricsContext))]
+[JsonSerializable(typeof(RootNode))]
+public partial class MetricsSerializationContext : JsonSerializerContext
+{
+
+}
+#endif
\ No newline at end of file
diff --git a/libraries/src/AWS.Lambda.Powertools.Metrics/Serializer/StringEnumConverter.cs b/libraries/src/AWS.Lambda.Powertools.Metrics/Serializer/StringEnumConverter.cs
index 28c0d54b..242ee90b 100644
--- a/libraries/src/AWS.Lambda.Powertools.Metrics/Serializer/StringEnumConverter.cs
+++ b/libraries/src/AWS.Lambda.Powertools.Metrics/Serializer/StringEnumConverter.cs
@@ -22,9 +22,12 @@
namespace AWS.Lambda.Powertools.Metrics;
+#if NET6_0
+
///
/// Class StringEnumConverter.
/// Implements the
+/// .NET 6 only
///
///
public class StringEnumConverter : JsonConverterFactory
@@ -95,4 +98,5 @@ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializer
_allowIntegerValues).CreateConverter(typeToConvert, options)
: _baseConverter.CreateConverter(typeToConvert, options);
}
-}
\ No newline at end of file
+}
+#endif
\ No newline at end of file
diff --git a/libraries/src/Directory.Build.props b/libraries/src/Directory.Build.props
index 0260344d..3a1d85f2 100644
--- a/libraries/src/Directory.Build.props
+++ b/libraries/src/Directory.Build.props
@@ -19,6 +19,14 @@
+
+
+
+ true
+ true
+ true
+
+
diff --git a/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/Utilities/SystemWrapperMock.cs b/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/Utilities/SystemWrapperMock.cs
index 4af6e593..9293f6e1 100644
--- a/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/Utilities/SystemWrapperMock.cs
+++ b/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/Utilities/SystemWrapperMock.cs
@@ -13,6 +13,7 @@
* permissions and limitations under the License.
*/
+using System.IO;
using AWS.Lambda.Powertools.Common;
namespace AWS.Lambda.Powertools.Logging.Tests.Utilities;
@@ -59,4 +60,9 @@ public void SetEnvironmentVariable(string variable, string value)
public void SetExecutionEnvironment(T type)
{
}
+
+ public void SetOut(TextWriter writeTo)
+ {
+
+ }
}
\ No newline at end of file
diff --git a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/AWS.Lambda.Powertools.Metrics.Tests.csproj b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/AWS.Lambda.Powertools.Metrics.Tests.csproj
index f6fd9f8d..16a4e0ce 100644
--- a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/AWS.Lambda.Powertools.Metrics.Tests.csproj
+++ b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/AWS.Lambda.Powertools.Metrics.Tests.csproj
@@ -10,6 +10,8 @@
+
+
diff --git a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/ClearDimensionsTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/ClearDimensionsTests.cs
index 879fdfac..0a46d6fd 100644
--- a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/ClearDimensionsTests.cs
+++ b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/ClearDimensionsTests.cs
@@ -1,7 +1,6 @@
-using System;
using System.IO;
using AWS.Lambda.Powertools.Common;
-using NSubstitute;
+using AWS.Lambda.Powertools.Metrics.Tests.Handlers;
using Xunit;
namespace AWS.Lambda.Powertools.Metrics.Tests;
@@ -13,39 +12,19 @@ public class ClearDimensionsTests
public void WhenClearAllDimensions_NoDimensionsInOutput()
{
// Arrange
- var methodName = Guid.NewGuid().ToString();
var consoleOut = new StringWriter();
- Console.SetOut(consoleOut);
+ SystemWrapper.Instance.SetOut(consoleOut);
- var configurations = Substitute.For();
-
- var metrics = new Metrics(
- configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService"
- );
-
- var handler = new MetricsAspectHandler(
- metrics,
- false
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
// Act
- handler.OnEntry(eventArgs);
-
- Metrics.ClearDefaultDimensions();
- Metrics.AddMetric($"Metric Name", 1, MetricUnit.Count);
-
- handler.OnExit(eventArgs);
+ var handler = new FunctionHandler();
+ handler.ClearDimensions();
var metricsOutput = consoleOut.ToString();
// Assert
Assert.Contains("{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Metric Name\",\"Unit\":\"Count\"}],\"Dimensions\":[[]]", metricsOutput);
-
+
// Reset
- handler.ResetForTest();
+ MetricsAspect.ResetForTest();
}
}
\ No newline at end of file
diff --git a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/EMFValidationTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/EMFValidationTests.cs
index 72fd1a54..cd89ced5 100644
--- a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/EMFValidationTests.cs
+++ b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/EMFValidationTests.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
@@ -18,7 +18,7 @@
using System.IO;
using System.Threading.Tasks;
using AWS.Lambda.Powertools.Common;
-using NSubstitute;
+using AWS.Lambda.Powertools.Metrics.Tests.Handlers;
using Xunit;
[assembly: CollectionBehavior(DisableTestParallelization = true)]
@@ -26,372 +26,154 @@
namespace AWS.Lambda.Powertools.Metrics.Tests
{
[Collection("Sequential")]
- public class EmfValidationTests
+ public class EmfValidationTests : IDisposable
{
+ private readonly CustomConsoleWriter _consoleOut;
+ private readonly FunctionHandler _handler;
+
+ public EmfValidationTests()
+ {
+ _handler = new FunctionHandler();
+ _consoleOut = new CustomConsoleWriter();
+ SystemWrapper.Instance.SetOut(_consoleOut);
+ }
+
[Trait("Category", value: "SchemaValidation")]
[Fact]
public void WhenCaptureColdStart_CreateSeparateBlob()
{
- // Arrange
- var methodName = Guid.NewGuid().ToString();
- var consoleOut = new StringWriter();
- const bool captureColdStartEnabled = true;
- Console.SetOut(consoleOut);
-
- var configurations = Substitute.For();
-
- var metrics = new Metrics(
- configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService",
- captureColdStartEnabled: captureColdStartEnabled
- );
-
- var handler = new MetricsAspectHandler(
- metrics,
- captureColdStartEnabled
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
// Act
- handler.OnEntry(eventArgs);
- Metrics.AddMetric("TestMetric", 1, MetricUnit.Count);
- handler.OnExit(eventArgs);
+ _handler.AddMetric();
- var metricsOutput = consoleOut.ToString();
+ var metricsOutput = _consoleOut.ToString();
// Assert
var metricBlobs = AllIndexesOf(metricsOutput, "_aws");
-
Assert.Equal(2, metricBlobs.Count);
-
- // Reset
- handler.ResetForTest();
}
[Trait("Category", "SchemaValidation")]
[Fact]
public void WhenCaptureColdStartEnabled_ValidateExists()
{
- // Arrange
- var methodName = Guid.NewGuid().ToString();
- var consoleOut = new StringWriter();
- const bool captureColdStartEnabled = true;
- Console.SetOut(consoleOut);
-
- var configurations = Substitute.For();
-
- var logger = new Metrics(
- configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService",
- captureColdStartEnabled: captureColdStartEnabled
- );
-
- var handler = new MetricsAspectHandler(
- logger,
- captureColdStartEnabled
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
// Act
- handler.OnEntry(eventArgs);
- Metrics.AddMetric("TestMetric", 1, MetricUnit.Count);
- handler.OnExit(eventArgs);
+ _handler.AddMetric();
- var result = consoleOut.ToString();
+ var result = _consoleOut.ToString();
// Assert
Assert.Contains("\"Metrics\":[{\"Name\":\"ColdStart\",\"Unit\":\"Count\"}]", result);
Assert.Contains("\"ColdStart\":1", result);
-
- handler.ResetForTest();
}
[Trait("Category", "EMFLimits")]
[Fact]
public void WhenMaxMetricsAreAdded_FlushAutomatically()
{
- // Arrange
- var methodName = Guid.NewGuid().ToString();
- var consoleOut = new StringWriter();
- Console.SetOut(consoleOut);
-
- var configurations = Substitute.For();
-
- var metrics = new Metrics(
- configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService"
- );
-
- var handler = new MetricsAspectHandler(
- metrics,
- false
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
// Act
- handler.OnEntry(eventArgs);
+ _handler.MaxMetrics(PowertoolsConfigurations.MaxMetrics);
- for (var i = 0; i <= PowertoolsConfigurations.MaxMetrics; i++)
- {
- Metrics.AddMetric($"Metric Name {i + 1}", i, MetricUnit.Count);
+ var metricsOutput = _consoleOut.OutputValues;
- if (i == PowertoolsConfigurations.MaxMetrics)
- {
- // 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\"]]", consoleOut.ToString());
- }
- }
- handler.OnExit(eventArgs);
+ // Assert
- var metricsOutput = consoleOut.ToString();
+ // 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\"]]",
+ metricsOutput[0]);
- // Assert
// flush the (MaxMetrics + 1) item only
- Assert.Contains("{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Metric Name 101\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Service\"]", metricsOutput);
-
- // Reset
- handler.ResetForTest();
+ Assert.Contains(
+ "{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Metric Name 101\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Service\"]",
+ metricsOutput[1]);
}
[Trait("Category", "EMFLimits")]
[Fact]
public void WhenMaxDataPointsAreAddedToTheSameMetric_FlushAutomatically()
{
- // Arrange
- var methodName = Guid.NewGuid().ToString();
- var consoleOut = new StringWriter();
- Console.SetOut(consoleOut);
-
- var configurations = Substitute.For();
-
- var metrics = new Metrics(
- configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService"
- );
-
- var handler = new MetricsAspectHandler(
- metrics,
- false
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
// Act
- handler.OnEntry(eventArgs);
+ _handler.MaxMetricsSameName(PowertoolsConfigurations.MaxMetrics);
- for (var i = 0; i <= PowertoolsConfigurations.MaxMetrics; i++)
- {
- Metrics.AddMetric($"Metric Name", i, MetricUnit.Count);
- if(i == PowertoolsConfigurations.MaxMetrics)
- {
- // flush when it reaches MaxMetrics
- Assert.Contains(
- "\"Service\":\"testService\",\"Metric Name\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99]}",
- consoleOut.ToString());
- }
- }
+ var metricsOutput = _consoleOut.OutputValues;
- handler.OnExit(eventArgs);
+ // Assert
- var metricsOutput = consoleOut.ToString();
+ // flush when it reaches MaxMetrics
+ Assert.Contains(
+ "\"Service\":\"testService\",\"Metric Name\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99]}",
+ metricsOutput[0]);
- // Assert
// flush the (MaxMetrics + 1) item only
- Assert.Contains("[[\"Service\"]]}]},\"Service\":\"testService\",\"Metric Name\":100}", metricsOutput);
-
- // Reset
- handler.ResetForTest();
+ Assert.Contains("[[\"Service\"]]}]},\"Service\":\"testService\",\"Metric Name\":100}", metricsOutput[1]);
}
[Trait("Category", "EMFLimits")]
[Fact]
public void WhenMoreThan9DimensionsAdded_ThrowArgumentOutOfRangeException()
{
- // Arrange
- var methodName = Guid.NewGuid().ToString();
- var configurations = Substitute.For();
-
- var metrics = new Metrics(
- configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService"
- );
-
- var handler = new MetricsAspectHandler(
- metrics,
- false
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
// Act
- handler.OnEntry(eventArgs);
-
- var act = () =>
- {
- for (var i = 0; i <= 9; i++)
- {
- Metrics.AddDimension($"Dimension Name {i + 1}", $"Dimension Value {i + 1}");
- }
- };
-
- handler.OnExit(eventArgs);
+ var act = () => { _handler.MaxDimensions(9); };
// Assert
Assert.Throws(act);
-
- // Reset
- handler.ResetForTest();
}
[Trait("Category", "SchemaValidation")]
[Fact]
public void WhenNamespaceNotDefined_ThrowSchemaValidationException()
{
- // Arrange
- var methodName = Guid.NewGuid().ToString();
- var configurations = Substitute.For();
-
- var metrics = new Metrics(
- configurations
- );
-
- var handler = new MetricsAspectHandler(
- metrics,
- false
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
// Act
- var act = () =>
- {
- handler.OnEntry(eventArgs);
- Metrics.AddMetric("TestMetric", 1, MetricUnit.Count);
- handler.OnExit(eventArgs);
- };
+ var act = () => { _handler.NoNamespace(); };
// Assert
var exception = Assert.Throws(act);
Assert.Equal("EMF schema is invalid. 'namespace' is mandatory and not specified.", exception.Message);
-
- // RESET
- handler.ResetForTest();
}
[Trait("Category", "SchemaValidation")]
[Fact]
public void WhenDimensionsAreAdded_MustExistAsMembers()
{
- // Arrange
- var methodName = Guid.NewGuid().ToString();
- var consoleOut = new StringWriter();
- Console.SetOut(consoleOut);
-
- var configurations = Substitute.For();
-
- var metrics = new Metrics(
- configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService"
- );
-
- var handler = new MetricsAspectHandler(
- metrics,
- false
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
// Act
- handler.OnEntry(eventArgs);
- Metrics.AddDimension("functionVersion", "$LATEST");
- Metrics.AddMetric("TestMetric", 1, MetricUnit.Count);
- handler.OnExit(eventArgs);
+ _handler.AddDimensions();
- var result = consoleOut.ToString();
+ var metricsOutput = _consoleOut.ToString();
// Assert
Assert.Contains("\"Dimensions\":[[\"Service\"],[\"functionVersion\"]]"
- , result);
+ , metricsOutput);
Assert.Contains("\"Service\":\"testService\",\"functionVersion\":\"$LATEST\""
- , result);
-
- // Reset
- handler.ResetForTest();
+ , metricsOutput);
}
[Trait("Category", "MetricsImplementation")]
[Fact]
public void WhenNamespaceIsDefined_AbleToRetrieveNamespace()
{
- // Arrange
- var methodName = Guid.NewGuid().ToString();
- var configurations = Substitute.For();
- var metrics = new Metrics(configurations);
-
- var handler = new MetricsAspectHandler(
- metrics,
- false
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
// Act
- handler.OnEntry(eventArgs);
- Metrics.SetNamespace("dotnet-powertools-test");
+ _handler.AddMetric();
+
+ var metricsOutput = _consoleOut.ToString();
var result = Metrics.GetNamespace();
// Assert
Assert.Equal("dotnet-powertools-test", result);
-
- // Reset
- handler.ResetForTest();
+ Assert.Contains("\"Namespace\":\"dotnet-powertools-test\"", metricsOutput);
}
[Trait("Category", "MetricsImplementation")]
[Fact]
public void WhenMetricsDefined_AbleToAddMetadata()
{
- // Arrange
- var methodName = Guid.NewGuid().ToString();
- var consoleOut = new StringWriter();
- Console.SetOut(consoleOut);
-
- var configurations = Substitute.For();
- var metrics = new Metrics(
- configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService"
- );
-
- var handler = new MetricsAspectHandler(
- metrics,
- false
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
// Act
- handler.OnEntry(eventArgs);
- Metrics.AddMetadata("test_metadata", "test_value");
- handler.OnExit(eventArgs);
+ _handler.AddMetadata();
- var result = consoleOut.ToString();
+ var result = _consoleOut.ToString();
// Assert
Assert.Contains("\"test_metadata\":\"test_value\"", result);
-
- // Reset
- handler.ResetForTest();
}
[Trait("Category", "MetricsImplementation")]
@@ -399,78 +181,36 @@ public void WhenMetricsDefined_AbleToAddMetadata()
public void WhenDefaultDimensionsSet_ValidInitialization()
{
// Arrange
- var methodName = Guid.NewGuid().ToString();
- var consoleOut = new StringWriter();
- Console.SetOut(consoleOut);
-
- var defaultDimensions = new Dictionary { { "CustomDefaultDimension", "CustomDefaultDimensionValue" } };
- var configurations = Substitute.For();
-
- var metrics = new Metrics(
- configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService"
- );
+ var key = "CustomDefaultDimension";
+ var value = "CustomDefaultDimensionValue";
- var handler = new MetricsAspectHandler(
- metrics,
- false
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
+ var defaultDimensions = new Dictionary { { key, value } };
// Act
- handler.OnEntry(eventArgs);
- Metrics.SetDefaultDimensions(defaultDimensions);
- Metrics.AddMetric("TestMetric", 1, MetricUnit.Count);
- handler.OnExit(eventArgs);
+ _handler.AddDefaultDimensions(defaultDimensions);
- var result = consoleOut.ToString();
+ var result = _consoleOut.ToString();
// Assert
- Assert.Contains("\"Dimensions\":[[\"Service\"],[\"CustomDefaultDimension\"]", result);
- Assert.Contains("\"CustomDefaultDimension\":\"CustomDefaultDimensionValue\"", result);
-
- // Reset
- handler.ResetForTest();
+ Assert.Contains($"\"Dimensions\":[[\"Service\"],[\"{key}\"]", result);
+ Assert.Contains($"\"CustomDefaultDimension\":\"{value}\"", result);
}
[Trait("Category", "MetricsImplementation")]
[Fact]
public void WhenMetricIsNegativeValue_ThrowException()
{
- // Arrange
- var methodName = Guid.NewGuid().ToString();
- var configurations = Substitute.For();
-
- var metrics = new Metrics(
- configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService"
- );
-
- var handler = new MetricsAspectHandler(
- metrics,
- false
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
// Act
var act = () =>
{
const int metricValue = -1;
- handler.OnEntry(eventArgs);
- Metrics.AddMetric("TestMetric", metricValue, MetricUnit.Count);
- handler.OnExit(eventArgs);
+ _handler.AddMetric("TestMetric", metricValue);
};
// Assert
var exception = Assert.Throws(act);
- Assert.Equal("'AddMetric' method requires a valid metrics value. Value must be >= 0. (Parameter 'value')", exception.Message);
-
- // RESET
- handler.ResetForTest();
+ Assert.Equal("'AddMetric' method requires a valid metrics value. Value must be >= 0. (Parameter 'value')",
+ exception.Message);
}
[Trait("Category", "SchemaValidation")]
@@ -478,248 +218,113 @@ public void WhenMetricIsNegativeValue_ThrowException()
public void WhenDefaultDimensionSet_IgnoreDuplicates()
{
// Arrange
- var methodName = Guid.NewGuid().ToString();
- var consoleOut = new StringWriter();
- Console.SetOut(consoleOut);
- var configurations = Substitute.For();
- var defaultDimensions = new Dictionary { { "CustomDefaultDimension", "CustomDefaultDimensionValue" } };
-
- var metrics = new Metrics(
- configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService"
- );
- var handler = new MetricsAspectHandler(
- metrics,
- false
- );
+ var defaultDimensions = new Dictionary
+ { { "CustomDefaultDimension", "CustomDefaultDimensionValue" } };
- var eventArgs = new AspectEventArgs { Name = methodName };
// Act
- handler.OnEntry(eventArgs);
- Metrics.SetDefaultDimensions(defaultDimensions);
- Metrics.SetDefaultDimensions(defaultDimensions);
- Metrics.AddMetric("TestMetric", 1, MetricUnit.Count);
- handler.OnExit(eventArgs);
+ _handler.AddDefaultDimensionsTwice(defaultDimensions);
- var result = consoleOut.ToString();
+ var result = _consoleOut.ToString();
// Assert
Assert.Contains("\"Dimensions\":[[\"Service\"],[\"CustomDefaultDimension\"]", result);
Assert.Contains("\"CustomDefaultDimension\":\"CustomDefaultDimensionValue\"", result);
-
- // Reset
- handler.ResetForTest();
}
[Fact]
public void WhenMetricsAndMetadataAdded_ValidateOutput()
{
- // Arrange
- var methodName = Guid.NewGuid().ToString();
- var consoleOut = new StringWriter();
- Console.SetOut(consoleOut);
- var configurations = Substitute.For();
-
- var metrics = new Metrics(
- configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService"
- );
-
- var handler = new MetricsAspectHandler(
- metrics,
- false
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
// Act
- handler.OnEntry(eventArgs);
- Metrics.AddDimension("functionVersion", "$LATEST");
- Metrics.AddMetric("Time", 100.7, MetricUnit.Milliseconds);
- Metrics.AddMetadata("env", "dev");
- handler.OnExit(eventArgs);
+ _handler.AddDimensionMetricMetadata("Time", "env");
- var result = consoleOut.ToString();
+ var result = _consoleOut.ToString();
// 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\"}"
+ Assert.Contains(
+ "CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Time\",\"Unit\":\"Milliseconds\"}],\"Dimensions\":[[\"Service\"],[\"functionVersion\"]]}]},\"Service\":\"testService\",\"functionVersion\":\"$LATEST\",\"Time\":100.7,\"env\":\"dev\"}"
, result);
-
- // Reset
- handler.ResetForTest();
}
-
+
[Fact]
public void When_Metrics_And_Metadata_Added_With_Same_Key_Should_Only_Write_Metrics()
{
- // Arrange
- var methodName = Guid.NewGuid().ToString();
- var consoleOut = new StringWriter();
- Console.SetOut(consoleOut);
- var configurations = Substitute.For();
-
- var metrics = new Metrics(
- configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService"
- );
-
- var handler = new MetricsAspectHandler(
- metrics,
- false
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
// Act
- handler.OnEntry(eventArgs);
- Metrics.AddDimension("functionVersion", "$LATEST");
- Metrics.AddMetric("Time", 100.7, MetricUnit.Milliseconds);
- Metrics.AddMetadata("Time", "dev");
- handler.OnExit(eventArgs);
+ _handler.AddDimensionMetricMetadata("Time", "Time");
- var result = consoleOut.ToString();
+ var result = _consoleOut.ToString();
// 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}"
+ Assert.Contains(
+ "CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Time\",\"Unit\":\"Milliseconds\"}],\"Dimensions\":[[\"Service\"],[\"functionVersion\"]]}]},\"Service\":\"testService\",\"functionVersion\":\"$LATEST\",\"Time\":100.7}"
, result);
-
- // Reset
- handler.ResetForTest();
}
[Trait("Category", "MetricsImplementation")]
[Fact]
public void WhenMetricsWithSameNameAdded_ValidateMetricArray()
{
- // Arrange
- var methodName = Guid.NewGuid().ToString();
- var consoleOut = new StringWriter();
- Console.SetOut(consoleOut);
-
- var configurations = Substitute.For();
-
- var metrics = new Metrics(
- configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService"
- );
-
- var handler = new MetricsAspectHandler(
- metrics,
- false
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
// Act
- handler.OnEntry(eventArgs);
- Metrics.AddDimension("functionVersion", "$LATEST");
- Metrics.AddMetric("Time", 100.5, MetricUnit.Milliseconds);
- Metrics.AddMetric("Time", 200, MetricUnit.Milliseconds);
- handler.OnExit(eventArgs);
+ _handler.AddMetricSameName();
- var result = consoleOut.ToString();
+ var result = _consoleOut.ToString();
// Assert
Assert.Contains("\"Metrics\":[{\"Name\":\"Time\",\"Unit\":\"Milliseconds\"}]"
, result);
Assert.Contains("\"Time\":[100.5,200]"
, result);
-
- // Reset
- handler.ResetForTest();
}
-
+
[Trait("Category", "MetricsImplementation")]
[Fact]
public void WhenMetricsWithStandardResolutionAdded_ValidateMetricArray()
{
- // Arrange
- var methodName = Guid.NewGuid().ToString();
- var consoleOut = new StringWriter();
- Console.SetOut(consoleOut);
-
- var configurations = Substitute.For();
-
- var metrics = new Metrics(
- configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService"
- );
-
- var handler = new MetricsAspectHandler(
- metrics,
- false
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
// Act
- handler.OnEntry(eventArgs);
- Metrics.AddDimension("functionVersion", "$LATEST");
- Metrics.AddMetric("Time", 100.5, MetricUnit.Milliseconds, MetricResolution.Standard);
- handler.OnExit(eventArgs);
+ _handler.AddMetric("Time", 100.5, MetricUnit.Milliseconds, MetricResolution.Standard);
- var result = consoleOut.ToString();
+ var result = _consoleOut.ToString();
// Assert
Assert.Contains("\"Metrics\":[{\"Name\":\"Time\",\"Unit\":\"Milliseconds\",\"StorageResolution\":60}]"
, result);
Assert.Contains("\"Time\":100.5"
, result);
-
- // Reset
- handler.ResetForTest();
}
-
+
[Trait("Category", "MetricsImplementation")]
[Fact]
public void WhenMetricsWithHighResolutionAdded_ValidateMetricArray()
{
- // Arrange
- var methodName = Guid.NewGuid().ToString();
- var consoleOut = new StringWriter();
- Console.SetOut(consoleOut);
-
- var configurations = Substitute.For();
-
- var metrics = new Metrics(
- configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService"
- );
-
- var handler = new MetricsAspectHandler(
- metrics,
- false
- );
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
// Act
- handler.OnEntry(eventArgs);
- Metrics.AddDimension("functionVersion", "$LATEST");
- Metrics.AddMetric("Time", 100.5, MetricUnit.Milliseconds, MetricResolution.High);
- handler.OnExit(eventArgs);
+ _handler.AddMetric("Time", 100.5, MetricUnit.Milliseconds, MetricResolution.High);
- var result = consoleOut.ToString();
+ var result = _consoleOut.ToString();
// Assert
Assert.Contains("\"Metrics\":[{\"Name\":\"Time\",\"Unit\":\"Milliseconds\",\"StorageResolution\":1}]"
, result);
Assert.Contains("\"Time\":100.5"
, result);
+ }
+
+ [Fact]
+ public async Task WhenMetricsAsyncRaceConditionItemSameKeyExists_ValidateLock()
+ {
+ // Act
+ await _handler.RaceConditon();
- // Reset
- handler.ResetForTest();
+ var metricsOutput = _consoleOut.ToString();
+
+ // Assert
+ Assert.Contains(
+ "{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Metric Name\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Service\"]]",
+ metricsOutput);
}
+
#region Helpers
private List AllIndexesOf(string str, string value)
@@ -728,7 +333,7 @@ private List AllIndexesOf(string str, string value)
if (string.IsNullOrEmpty(value)) return indexes;
- for (var index = 0; ; index += value.Length)
+ for (var index = 0;; index += value.Length)
{
index = str.IndexOf(value, index, StringComparison.Ordinal);
if (index == -1)
@@ -736,53 +341,14 @@ private List AllIndexesOf(string str, string value)
indexes.Add(index);
}
}
-
- #endregion
- [Fact]
- public async Task WhenMetricsAsyncRaceConditionItemSameKeyExists_ValidateLock()
- {
- // Arrange
- var methodName = Guid.NewGuid().ToString();
- var consoleOut = new StringWriter();
- Console.SetOut(consoleOut);
-
- var configurations = Substitute.For();
-
- var metrics = new Metrics(configurations,
- nameSpace: "dotnet-powertools-test",
- service: "testService");
-
- var handler = new MetricsAspectHandler(metrics,
- false);
-
- var eventArgs = new AspectEventArgs { Name = methodName };
-
- // Act
- handler.OnEntry(eventArgs);
-
- var tasks = new List();
- for (var i = 0; i < 100; i++)
- {
- tasks.Add(Task.Run(() =>
- {
- Metrics.AddMetric($"Metric Name", 0, MetricUnit.Count);
- }));
- }
-
- await Task.WhenAll(tasks);
-
-
- handler.OnExit(eventArgs);
+ #endregion
- var metricsOutput = consoleOut.ToString();
- // Assert
- Assert.Contains("{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Metric Name\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Service\"]]",
- metricsOutput);
-
- // Reset
- handler.ResetForTest();
+ public void Dispose()
+ {
+ // need to reset instance after each test
+ MetricsAspect.ResetForTest();
}
}
-}
+}
\ No newline at end of file
diff --git a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/ExceptionFunctionHandlerTests.cs b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/ExceptionFunctionHandlerTests.cs
index 0a1aad17..cdda3c52 100644
--- a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/ExceptionFunctionHandlerTests.cs
+++ b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/ExceptionFunctionHandlerTests.cs
@@ -5,7 +5,7 @@
namespace AWS.Lambda.Powertools.Metrics.Tests.Handlers;
[Collection("Sequential")]
-public sealed class ExceptionFunctionHandlerTests
+public sealed class ExceptionFunctionHandlerTests : IDisposable
{
[Fact]
public async Task Stack_Trace_Included_When_Decorator_Present()
@@ -34,7 +34,7 @@ public async Task Stack_Trace_Included_When_Decorator_Present_In_Method()
// Assert
var tracedException = await Assert.ThrowsAsync(Handle);
- Assert.StartsWith("at AWS.Lambda.Powertools.Metrics.Tests.Handlers.ExceptionFunctionHandler.__a$_around_ThisThrows", tracedException.StackTrace?.TrimStart());
+ Assert.StartsWith("at AWS.Lambda.Powertools.Metrics.Tests.Handlers.ExceptionFunctionHandler.ThisThrowsDecorated()", tracedException.StackTrace?.TrimStart());
}
[Fact]
@@ -51,4 +51,9 @@ public async Task Decorator_In_Non_Handler_Method_Does_Not_Throw_Exception()
var tracedException = await Record.ExceptionAsync(Handle);
Assert.Null(tracedException);
}
+
+ public void Dispose()
+ {
+ MetricsAspect.ResetForTest();
+ }
}
\ 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 271b2e21..8ae59b9f 100644
--- a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/FunctionHandler.cs
+++ b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/FunctionHandler.cs
@@ -14,14 +14,120 @@
*/
using System;
+using System.Collections;
+using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
+using Amazon.Lambda.Core;
+using Amazon.Lambda.TestUtilities;
namespace AWS.Lambda.Powertools.Metrics.Tests.Handlers;
public class FunctionHandler
{
+ [Metrics(Namespace = "dotnet-powertools-test", Service = "testService", CaptureColdStart = true)]
+ public void AddMetric(string name = "TestMetric", double value =1, MetricUnit unit = MetricUnit.Count,MetricResolution resolution = MetricResolution.Default)
+ {
+ Metrics.AddMetric(name, value, unit, resolution);
+ }
+
+ [Metrics(Namespace = "dotnet-powertools-test", Service = "testService")]
+ public void AddDimensions()
+ {
+ Metrics.AddDimension("functionVersion", "$LATEST");
+ Metrics.AddMetric("TestMetric", 1, MetricUnit.Count);
+ }
+
+ [Metrics(Namespace = "dotnet-powertools-test", Service = "testService")]
+ public void ClearDimensions()
+ {
+ Metrics.ClearDefaultDimensions();
+ Metrics.AddMetric("Metric Name", 1, MetricUnit.Count);
+ }
+
+ [Metrics(Namespace = "dotnet-powertools-test", Service = "testService")]
+ public void MaxMetrics(int maxMetrics)
+ {
+ for (var i = 0; i <= maxMetrics; i++)
+ {
+ Metrics.AddMetric($"Metric Name {i + 1}", i, MetricUnit.Count);
+ }
+ }
+
+ [Metrics(Namespace = "dotnet-powertools-test", Service = "testService")]
+ public void MaxMetricsSameName(int maxMetrics)
+ {
+ for (var i = 0; i <= maxMetrics; i++)
+ {
+ Metrics.AddMetric("Metric Name", i, MetricUnit.Count);
+ }
+ }
+
+ [Metrics(Namespace = "dotnet-powertools-test", Service = "testService")]
+ public void MaxDimensions(int maxDimensions)
+ {
+ for (var i = 0; i <= maxDimensions; i++)
+ {
+ Metrics.AddDimension($"Dimension Name {i + 1}", $"Dimension Value {i + 1}");
+ }
+ }
+
+ [Metrics(Service = "testService")]
+ public void NoNamespace()
+ {
+ Metrics.AddMetric("TestMetric", 1, MetricUnit.Count);
+ }
+
+ [Metrics(Namespace = "dotnet-powertools-test", Service = "testService")]
+ public void AddMetadata()
+ {
+ Metrics.AddMetadata("test_metadata", "test_value");
+ }
+
+ [Metrics(Namespace = "dotnet-powertools-test", Service = "testService")]
+ public void AddDefaultDimensions(Dictionary defaultDimensions)
+ {
+ Metrics.SetDefaultDimensions(defaultDimensions);
+ Metrics.AddMetric("TestMetric", 1, MetricUnit.Count);
+ }
+
+ [Metrics(Namespace = "dotnet-powertools-test", Service = "testService")]
+ public void AddDefaultDimensionsTwice(Dictionary defaultDimensions)
+ {
+ Metrics.SetDefaultDimensions(defaultDimensions);
+ Metrics.SetDefaultDimensions(defaultDimensions);
+ Metrics.AddMetric("TestMetric", 1, MetricUnit.Count);
+ }
+
+ [Metrics(Namespace = "dotnet-powertools-test", Service = "testService")]
+ public void AddDimensionMetricMetadata(string metricKey, string metadataKey)
+ {
+ Metrics.AddDimension("functionVersion", "$LATEST");
+ Metrics.AddMetric(metricKey, 100.7, MetricUnit.Milliseconds);
+ Metrics.AddMetadata(metadataKey, "dev");
+ }
+
+ [Metrics(Namespace = "dotnet-powertools-test", Service = "testService")]
+ public void AddMetricSameName()
+ {
+ Metrics.AddDimension("functionVersion", "$LATEST");
+ Metrics.AddMetric("Time", 100.5, MetricUnit.Milliseconds);
+ Metrics.AddMetric("Time", 200, MetricUnit.Milliseconds);
+ }
+
+ [Metrics(Namespace = "dotnet-powertools-test", Service = "testService")]
+ public async Task RaceConditon()
+ {
+ var tasks = new List();
+ for (var i = 0; i < 100; i++)
+ {
+ tasks.Add(Task.Run(() => { Metrics.AddMetric($"Metric Name", 0, MetricUnit.Count); }));
+ }
+
+ await Task.WhenAll(tasks);
+ }
+
[Metrics(Namespace = "ns", Service = "svc")]
public async Task HandleSameKey(string input)
{
@@ -32,18 +138,18 @@ public async Task HandleSameKey(string input)
return input.ToUpper(CultureInfo.InvariantCulture);
}
-
+
[Metrics(Namespace = "ns", Service = "svc")]
public async Task HandleTestSecondCall(string input)
{
Metrics.AddMetric("MyMetric", 1);
Metrics.AddMetadata("MyMetadata", "meta");
-
+
await Task.Delay(1);
return input.ToUpper(CultureInfo.InvariantCulture);
}
-
+
[Metrics(Namespace = "ns", Service = "svc")]
public async Task HandleMultipleThreads(string input)
{
@@ -55,4 +161,16 @@ await Parallel.ForEachAsync(Enumerable.Range(0, Environment.ProcessorCount * 2),
return input.ToUpper(CultureInfo.InvariantCulture);
}
+
+ [Metrics(Namespace = "ns", Service = "svc", CaptureColdStart = true)]
+ public void HandleWithLambdaContext(ILambdaContext context)
+ {
+
+ }
+
+ [Metrics(Namespace = "ns", Service = "svc")]
+ public void HandleWithLambdaContextAndMetrics(TestLambdaContext context)
+ {
+ Metrics.AddMetric("MyMetric", 1);
+ }
}
\ No newline at end of file
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 4afba753..556d6cce 100644
--- a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/FunctionHandlerTests.cs
+++ b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/FunctionHandlerTests.cs
@@ -13,23 +13,35 @@
* permissions and limitations under the License.
*/
+using System;
using System.Threading.Tasks;
+using Amazon.Lambda.TestUtilities;
+using AWS.Lambda.Powertools.Common;
using Xunit;
namespace AWS.Lambda.Powertools.Metrics.Tests.Handlers;
[Collection("Sequential")]
-public class FunctionHandlerTests
+public class FunctionHandlerTests : IDisposable
{
+ private readonly FunctionHandler _handler;
+ private readonly CustomConsoleWriter _consoleOut;
+
+ public FunctionHandlerTests()
+ {
+ _handler = new FunctionHandler();
+ _consoleOut = new CustomConsoleWriter();
+ SystemWrapper.Instance.SetOut(_consoleOut);
+ }
+
[Fact]
public async Task When_Metrics_Add_Metadata_Same_Key_Should_Ignore_Metadata()
{
// Arrange
- Metrics.ResetForTest();
- var handler = new FunctionHandler();
+
// Act
- var exception = await Record.ExceptionAsync( () => handler.HandleSameKey("whatever"));
+ var exception = await Record.ExceptionAsync( () => _handler.HandleSameKey("whatever"));
// Assert
Assert.Null(exception);
@@ -38,27 +50,48 @@ public async Task When_Metrics_Add_Metadata_Same_Key_Should_Ignore_Metadata()
[Fact]
public async Task When_Metrics_Add_Metadata_Second_Invocation_Should_Not_Throw_Exception()
{
- // Arrange
- Metrics.ResetForTest();
- var handler = new FunctionHandler();
-
// Act
- var exception = await Record.ExceptionAsync( () => handler.HandleTestSecondCall("whatever"));
+ var exception = await Record.ExceptionAsync( () => _handler.HandleTestSecondCall("whatever"));
Assert.Null(exception);
- exception = await Record.ExceptionAsync( () => handler.HandleTestSecondCall("whatever"));
+ exception = await Record.ExceptionAsync( () => _handler.HandleTestSecondCall("whatever"));
Assert.Null(exception);
}
[Fact]
public async Task When_Metrics_Add_Metadata_FromMultipleThread_Should_Not_Throw_Exception()
{
- // Arrange
- Metrics.ResetForTest();
- var handler = new FunctionHandler();
-
// Act
- var exception = await Record.ExceptionAsync(() => handler.HandleMultipleThreads("whatever"));
+ var exception = await Record.ExceptionAsync(() => _handler.HandleMultipleThreads("whatever"));
Assert.Null(exception);
}
+
+ [Fact]
+ public void When_LambdaContext_Should_Add_FunctioName_Dimension_CaptureColdStart()
+ {
+ // Arrange
+ var context = new TestLambdaContext
+ {
+ FunctionName = "My Function with context"
+ };
+
+ // Act
+ _handler.HandleWithLambdaContext(context);
+ var metricsOutput = _consoleOut.ToString();
+
+ // Assert
+ Assert.Contains(
+ "\"FunctionName\":\"My Function with context\"",
+ metricsOutput);
+
+ Assert.Contains(
+ "\"Metrics\":[{\"Name\":\"ColdStart\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"FunctionName\"],[\"Service\"]]}]}",
+ metricsOutput);
+ }
+
+ public void Dispose()
+ {
+ Metrics.ResetForTest();
+ MetricsAspect.ResetForTest();
+ }
}
\ No newline at end of file
diff --git a/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Utils.cs b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Utils.cs
new file mode 100644
index 00000000..63fa1d4d
--- /dev/null
+++ b/libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Utils.cs
@@ -0,0 +1,30 @@
+/*
+ * 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
+ * permissions and limitations under the License.
+ */
+
+using System.Collections.Generic;
+using System.IO;
+
+namespace AWS.Lambda.Powertools.Metrics.Tests;
+
+public class CustomConsoleWriter : StringWriter
+{
+ public readonly List OutputValues = new();
+
+ public override void WriteLine(string value)
+ {
+ OutputValues.Add(value);
+ base.WriteLine(value);
+ }
+}
\ No newline at end of file
diff --git a/version.json b/version.json
index 7f866299..ec7a6148 100644
--- a/version.json
+++ b/version.json
@@ -1,7 +1,7 @@
{
"Core": {
"Logging": "1.5.1",
- "Metrics": "1.6.2",
+ "Metrics": "1.7.0",
"Tracing": "1.4.2"
},
"Utilities": {