diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index cfc6045b..a5a12869 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -20,27 +20,33 @@ permissions:
jobs:
build:
runs-on: ubuntu-latest
-
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- - name: Setup .NET 6.0 & 8.0
+
+ - name: Setup .NET SDK
uses: actions/setup-dotnet@3951f0dfe7a07e2313ec93c75700083e2005cbab # 4.3.0
with:
dotnet-version: |
- 6.0.405
- 8.0.101
+ 6.0.x
+ 8.0.x
+
+ - name: Install dependencies
+ run: dotnet restore
+
- name: Build
- run: dotnet build --configuration Release
- - name: Test Examples
- run: dotnet test ../examples/
+ run: dotnet build --configuration Release --no-restore /tl
+
- name: Test & Code Coverage
- run: dotnet test --filter "Category!=E2E" --collect:"XPlat Code Coverage" --results-directory ./codecov --verbosity normal
+ run: dotnet test --no-restore --filter "Category!=E2E" --collect:"XPlat Code Coverage" --results-directory ./codecov --verbosity normal
+
+ - name: Test Examples
+ run: dotnet test ../examples/ --verbosity normal
+
- name: Codecov
uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # 5.3.1
with:
token: ${{ secrets.CODECOV_TOKEN }}
- flags: unittests
fail_ci_if_error: false
name: codecov-lambda-powertools-dotnet
verbose: true
- directory: ./libraries/codecov
+ directory: ./libraries/codecov
\ No newline at end of file
diff --git a/docs/core/metrics-v2.md b/docs/core/metrics-v2.md
new file mode 100644
index 00000000..ec6c536e
--- /dev/null
+++ b/docs/core/metrics-v2.md
@@ -0,0 +1,890 @@
+---
+title: Metrics V2
+description: Core utility
+---
+
+Metrics creates custom metrics asynchronously by logging metrics to standard output following [Amazon CloudWatch Embedded Metric Format (EMF)](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format.html).
+
+These metrics can be visualized through [Amazon CloudWatch Console](https://aws.amazon.com/cloudwatch/).
+
+## Key features
+
+* Aggregate up to 100 metrics using a single [CloudWatch EMF](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html){target="_blank"} object (large JSON blob)
+* 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
+* Support for AspNetCore middleware and filters to capture metrics for HTTP requests
+
+## Breaking changes from V1
+
+* **`Dimensions`** outputs as an array of arrays instead of an array of objects. Example: `Dimensions: [["service", "Environment"]]` instead of `Dimensions: ["service", "Environment"]`
+* **`FunctionName`** is not added as default dimension and only to cold start metric.
+* **`Default Dimensions`** can now be included in Cold Start metrics, this is a potential breaking change if you were relying on the absence of default dimensions in Cold Start metrics when searching.
+
+
+
+
+
+ Metrics showcase - Metrics Explorer
+
+
+## Installation
+
+Powertools for AWS Lambda (.NET) are available as NuGet packages. You can install the packages from [NuGet Gallery](https://www.nuget.org/packages?q=AWS+Lambda+Powertools*){target="_blank"} or from Visual Studio editor by searching `AWS.Lambda.Powertools*` to see various utilities available.
+
+* [AWS.Lambda.Powertools.Metrics](https://www.nuget.org/packages?q=AWS.Lambda.Powertools.Metrics):
+
+ `dotnet nuget add AWS.Lambda.Powertools.Metrics`
+
+## Terminologies
+
+If you're new to Amazon CloudWatch, there are two terminologies you must be aware of before using this utility:
+
+* **Namespace**. It's the highest level container that will group multiple metrics from multiple services for a given application, for example `ServerlessEcommerce`.
+* **Dimensions**. Metrics metadata in key-value format. They help you slice and dice metrics visualization, for example `ColdStart` metric by Payment `service`.
+* **Metric**. It's the name of the metric, for example: SuccessfulBooking or UpdatedBooking.
+* **Unit**. It's a value representing the unit of measure for the corresponding metric, for example: Count or Seconds.
+* **Resolution**. It's a value representing the storage resolution for the corresponding metric. Metrics can be either Standard or High resolution. Read more [here](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html#Resolution_definition).
+
+Visit the AWS documentation for a complete explanation for [Amazon CloudWatch concepts](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html).
+
+
+
+ Metric terminology, visually explained
+
+
+## Getting started
+
+**`Metrics`** is implemented as a Singleton to keep track of your aggregate metrics in memory and make them accessible anywhere in your code. To guarantee that metrics are flushed properly the **`MetricsAttribute`** must be added on the lambda handler.
+
+Metrics has two global settings that will be used across all metrics emitted. Use your application or main service as the metric namespace to easily group all metrics:
+
+Setting | Description | Environment variable | Constructor parameter
+------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------- | -------------------------------------------------
+**Service** | Optionally, sets **service** metric dimension across all metrics e.g. `payment` | `POWERTOOLS_SERVICE_NAME` | `Service`
+**Metric namespace** | Logical container where all metrics will be placed e.g. `MyCompanyEcommerce` | `POWERTOOLS_METRICS_NAMESPACE` | `Namespace`
+
+!!! info "Autocomplete Metric Units"
+ All parameters in **`Metrics Attribute`** are optional. Following rules apply:
+
+ - **Namespace:** **`Empty`** string by default. You can either specify it in code or environment variable. If not present before flushing metrics, a **`SchemaValidationException`** will be thrown.
+ - **Service:** **`service_undefined`** by default. You can either specify it in code or environment variable.
+ - **CaptureColdStart:** **`false`** by default.
+ - **RaiseOnEmptyMetrics:** **`false`** by default.
+
+### Full list of environment variables
+
+| Environment variable | Description | Default |
+| ------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------- |
+| **POWERTOOLS_SERVICE_NAME** | Sets service name used for tracing namespace, metrics dimension and structured logging | `"service_undefined"` |
+| **POWERTOOLS_METRICS_NAMESPACE** | Sets namespace used for metrics | `None` |
+
+### Metrics object
+
+#### Attribute
+
+The **`MetricsAttribute`** is a class-level attribute that can be used to set the namespace and service for all metrics emitted by the lambda handler.
+
+```csharp hl_lines="3"
+using AWS.Lambda.Powertools.Metrics;
+
+[Metrics(Namespace = "ExampleApplication", Service = "Booking")]
+public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+{
+ ...
+}
+```
+
+#### Methods
+
+The **`Metrics`** class provides methods to add metrics, dimensions, and metadata to the metrics object.
+
+```csharp hl_lines="5-7"
+using AWS.Lambda.Powertools.Metrics;
+
+public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+{
+ Metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
+ Metrics.AddDimension("Environment", "Prod");
+ Metrics.AddMetadata("BookingId", "683EEB2D-B2F3-4075-96EE-788E6E2EED45");
+ ...
+}
+```
+
+#### Initialization
+
+The **`Metrics`** object is initialized as a Singleton and can be accessed anywhere in your code.
+
+But can also be initialize with `Configure` or `Builder` patterns in your Lambda constructor, this the best option for testing.
+
+Configure:
+
+```csharp
+using AWS.Lambda.Powertools.Metrics;
+
+public Function()
+{
+ Metrics.Configure(options =>
+ {
+ options.Namespace = "dotnet-powertools-test";
+ options.Service = "testService";
+ options.CaptureColdStart = true;
+ options.DefaultDimensions = new Dictionary
+ {
+ { "Environment", "Prod" },
+ { "Another", "One" }
+ };
+ });
+}
+
+[Metrics]
+public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+{
+ Metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
+ ...
+}
+```
+
+Builder:
+
+```csharp
+using AWS.Lambda.Powertools.Metrics;
+
+private readonly IMetrics _metrics;
+
+public Function()
+{
+ _metrics = new MetricsBuilder()
+ .WithCaptureColdStart(true)
+ .WithService("testService")
+ .WithNamespace("dotnet-powertools-test")
+ .WithDefaultDimensions(new Dictionary
+ {
+ { "Environment", "Prod1" },
+ { "Another", "One" }
+ }).Build();
+}
+
+[Metrics]
+public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+{
+ _metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
+ ...
+}
+```
+
+
+### Creating metrics
+
+You can create metrics using **`AddMetric`**, and you can create dimensions for all your aggregate metrics using **`AddDimension`** method.
+
+=== "Metrics"
+
+ ```csharp hl_lines="5 8"
+ using AWS.Lambda.Powertools.Metrics;
+
+ public class Function {
+
+ [Metrics(Namespace = "ExampleApplication", Service = "Booking")]
+ public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+ {
+ Metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
+ }
+ }
+ ```
+=== "Metrics with custom dimensions"
+
+ ```csharp hl_lines="8-9"
+ using AWS.Lambda.Powertools.Metrics;
+
+ public class Function {
+
+ [Metrics(Namespace = "ExampleApplication", Service = "Booking")]
+ public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+ {
+ Metrics.AddDimension("Environment","Prod");
+ Metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
+ }
+ }
+ ```
+
+!!! tip "Autocomplete Metric Units"
+ `MetricUnit` enum facilitates finding a supported metric unit by CloudWatch.
+
+!!! note "Metrics overflow"
+ CloudWatch EMF supports a max of 100 metrics per batch. Metrics utility will flush all metrics when adding the 100th metric. Subsequent metrics, e.g. 101th, will be aggregated into a new EMF object, for your convenience.
+
+!!! warning "Metric value must be a positive number"
+ Metric values must be a positive number otherwise an `ArgumentException` will be thrown.
+
+!!! warning "Do not create metrics or dimensions outside the handler"
+ Metrics or dimensions added in the global scope will only be added during cold start. Disregard if that's the intended behavior.
+
+### Adding high-resolution metrics
+
+You can create [high-resolution metrics](https://aws.amazon.com/about-aws/whats-new/2023/02/amazon-cloudwatch-high-resolution-metric-extraction-structured-logs/) passing `MetricResolution` as parameter to `AddMetric`.
+
+!!! tip "When is it useful?"
+ High-resolution metrics are data with a granularity of one second and are very useful in several situations such as telemetry, time series, real-time incident management, and others.
+
+=== "Metrics with high resolution"
+
+ ```csharp hl_lines="9 12 15"
+ using AWS.Lambda.Powertools.Metrics;
+
+ public class Function {
+
+ [Metrics(Namespace = "ExampleApplication", Service = "Booking")]
+ public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+ {
+ // Publish a metric with standard resolution i.e. StorageResolution = 60
+ Metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count, MetricResolution.Standard);
+
+ // Publish a metric with high resolution i.e. StorageResolution = 1
+ Metrics.AddMetric("FailedBooking", 1, MetricUnit.Count, MetricResolution.High);
+
+ // The last parameter (storage resolution) is optional
+ Metrics.AddMetric("SuccessfulUpgrade", 1, MetricUnit.Count);
+ }
+ }
+ ```
+
+!!! tip "Autocomplete Metric Resolutions"
+ Use the `MetricResolution` enum to easily find a supported metric resolution by CloudWatch.
+
+### Adding default dimensions
+
+You can use **`SetDefaultDimensions`** method to persist dimensions across Lambda invocations.
+
+=== "SetDefaultDimensions method"
+
+ ```csharp hl_lines="4 5 6 7 12"
+ using AWS.Lambda.Powertools.Metrics;
+
+ public class Function {
+ private Dictionary _defaultDimensions = new Dictionary{
+ {"Environment", "Prod"},
+ {"Another", "One"}
+ };
+
+ [Metrics(Namespace = "ExampleApplication", Service = "Booking")]
+ public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+ {
+ Metrics.SetDefaultDimensions(_defaultDimensions);
+ Metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
+ }
+ }
+ ```
+
+### Adding default dimensions with cold start metric
+
+You can use the Builder or Configure patterns in your Lambda class constructor to set default dimensions.
+
+=== "Builder pattern"
+
+ ```csharp hl_lines="12-16"
+ using AWS.Lambda.Powertools.Metrics;
+
+ public class Function {
+ private readonly IMetrics _metrics;
+
+ public Function()
+ {
+ _metrics = new MetricsBuilder()
+ .WithCaptureColdStart(true)
+ .WithService("testService")
+ .WithNamespace("dotnet-powertools-test")
+ .WithDefaultDimensions(new Dictionary
+ {
+ { "Environment", "Prod1" },
+ { "Another", "One" }
+ }).Build();
+ }
+
+ [Metrics]
+ public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+ {
+ _metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
+ ...
+ }
+ ```
+=== "Configure pattern"
+
+ ```csharp hl_lines="12-16"
+ using AWS.Lambda.Powertools.Metrics;
+
+ public class Function {
+
+ public Function()
+ {
+ Metrics.Configure(options =>
+ {
+ options.Namespace = "dotnet-powertools-test";
+ options.Service = "testService";
+ options.CaptureColdStart = true;
+ options.DefaultDimensions = new Dictionary
+ {
+ { "Environment", "Prod" },
+ { "Another", "One" }
+ };
+ });
+ }
+
+ [Metrics]
+ public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+ {
+ Metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
+ ...
+ }
+ ```
+### Adding dimensions
+
+You can add dimensions to your metrics using **`AddDimension`** method.
+
+=== "Function.cs"
+
+ ```csharp hl_lines="8"
+ using AWS.Lambda.Powertools.Metrics;
+
+ public class Function {
+
+ [Metrics(Namespace = "ExampleApplication", Service = "Booking")]
+ public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+ {
+ Metrics.AddDimension("Environment","Prod");
+ Metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
+ }
+ }
+ ```
+=== "Example CloudWatch Logs excerpt"
+
+ ```json hl_lines="11 24"
+ {
+ "SuccessfulBooking": 1.0,
+ "_aws": {
+ "Timestamp": 1592234975665,
+ "CloudWatchMetrics": [
+ {
+ "Namespace": "ExampleApplication",
+ "Dimensions": [
+ [
+ "service",
+ "Environment"
+ ]
+ ],
+ "Metrics": [
+ {
+ "Name": "SuccessfulBooking",
+ "Unit": "Count"
+ }
+ ]
+ }
+ ]
+ },
+ "service": "ExampleService",
+ "Environment": "Prod"
+ }
+ ```
+
+### Flushing metrics
+
+With **`MetricsAttribute`** all your metrics are validated, serialized and flushed to standard output when lambda handler completes execution or when you had the 100th metric to memory.
+
+You can also flush metrics manually by calling **`Flush`** method.
+
+During metrics validation, if no metrics are provided then a warning will be logged, but no exception will be raised.
+
+=== "Function.cs"
+
+ ```csharp hl_lines="9"
+ using AWS.Lambda.Powertools.Metrics;
+
+ public class Function {
+
+ [Metrics(Namespace = "ExampleApplication", Service = "Booking")]
+ public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+ {
+ Metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
+ Metrics.Flush();
+ }
+ }
+ ```
+=== "Example CloudWatch Logs excerpt"
+
+ ```json hl_lines="2 7 10 15 22"
+ {
+ "BookingConfirmation": 1.0,
+ "_aws": {
+ "Timestamp": 1592234975665,
+ "CloudWatchMetrics": [
+ {
+ "Namespace": "ExampleApplication",
+ "Dimensions": [
+ [
+ "service"
+ ]
+ ],
+ "Metrics": [
+ {
+ "Name": "BookingConfirmation",
+ "Unit": "Count"
+ }
+ ]
+ }
+ ]
+ },
+ "service": "ExampleService"
+ }
+ ```
+
+!!! tip "Metric validation"
+ If metrics are provided, and any of the following criteria are not met, **`SchemaValidationException`** will be raised:
+
+ * Maximum of 30 dimensions
+ * Namespace is set
+ * Metric units must be [supported by CloudWatch](https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_MetricDatum.html)
+
+!!! info "We do not emit 0 as a value for ColdStart metric for cost reasons. [Let us know](https://github.com/aws-powertools/powertools-lambda-dotnet/issues/new?assignees=&labels=feature-request%2Ctriage&template=feature_request.yml&title=Feature+request%3A+TITLE) if you'd prefer a flag to override it"
+
+### Raising SchemaValidationException on empty metrics
+
+If you want to ensure that at least one metric is emitted, you can pass **`RaiseOnEmptyMetrics`** to the Metrics attribute:
+
+=== "Function.cs"
+
+ ```python hl_lines="5"
+ using AWS.Lambda.Powertools.Metrics;
+
+ public class Function {
+
+ [Metrics(RaiseOnEmptyMetrics = true)]
+ public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+ {
+ ...
+ ```
+
+### Capturing cold start metric
+
+You can optionally capture cold start metrics by setting **`CaptureColdStart`** parameter to `true`.
+
+=== "Function.cs"
+
+ ```csharp hl_lines="5"
+ using AWS.Lambda.Powertools.Metrics;
+
+ public class Function {
+
+ [Metrics(CaptureColdStart = true)]
+ public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+ {
+ ...
+ ```
+=== "Builder pattern"
+
+ ```csharp hl_lines="9"
+ using AWS.Lambda.Powertools.Metrics;
+
+ public class Function {
+ private readonly IMetrics _metrics;
+
+ public Function()
+ {
+ _metrics = new MetricsBuilder()
+ .WithCaptureColdStart(true)
+ .WithService("testService")
+ .WithNamespace("dotnet-powertools-test")
+ }
+
+ [Metrics]
+ public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+ {
+ _metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
+ ...
+ }
+ ```
+=== "Configure pattern"
+
+ ```csharp hl_lines="11"
+ using AWS.Lambda.Powertools.Metrics;
+
+ public class Function {
+
+ public Function()
+ {
+ Metrics.Configure(options =>
+ {
+ options.Namespace = "dotnet-powertools-test";
+ options.Service = "testService";
+ options.CaptureColdStart = true;
+ });
+ }
+
+ [Metrics]
+ public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+ {
+ Metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
+ ...
+ }
+ ```
+
+If it's a cold start invocation, this feature will:
+
+* Create a separate EMF blob solely containing a metric named `ColdStart`
+* Add `FunctionName` and `Service` dimensions
+
+This has the advantage of keeping cold start metric separate from your application metrics, where you might have unrelated dimensions.
+
+## Advanced
+
+### Adding metadata
+
+You can add high-cardinality data as part of your Metrics log with `AddMetadata` method. This is useful when you want to search highly contextual information along with your metrics in your logs.
+
+!!! info
+ **This will not be available during metrics visualization** - Use **dimensions** for this purpose
+
+!!! info
+ Adding metadata with a key that is the same as an existing metric will be ignored
+
+=== "Function.cs"
+
+ ```csharp hl_lines="9"
+ using AWS.Lambda.Powertools.Metrics;
+
+ public class Function {
+
+ [Metrics(Namespace = ExampleApplication, Service = "Booking")]
+ public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+ {
+ Metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
+ Metrics.AddMetadata("BookingId", "683EEB2D-B2F3-4075-96EE-788E6E2EED45");
+ ...
+ ```
+
+=== "Example CloudWatch Logs excerpt"
+
+ ```json hl_lines="23"
+ {
+ "SuccessfulBooking": 1.0,
+ "_aws": {
+ "Timestamp": 1592234975665,
+ "CloudWatchMetrics": [
+ {
+ "Namespace": "ExampleApplication",
+ "Dimensions": [
+ [
+ "service"
+ ]
+ ],
+ "Metrics": [
+ {
+ "Name": "SuccessfulBooking",
+ "Unit": "Count"
+ }
+ ]
+ }
+ ]
+ },
+ "Service": "Booking",
+ "BookingId": "683EEB2D-B2F3-4075-96EE-788E6E2EED45"
+ }
+ ```
+
+### Single metric with a different dimension
+
+CloudWatch EMF uses the same dimensions across all your metrics. Use **`PushSingleMetric`** if you have a metric that should have different dimensions.
+
+!!! info
+ Generally, this would be an edge case since you [pay for unique metric](https://aws.amazon.com/cloudwatch/pricing). Keep the following formula in mind:
+
+ **unique metric = (metric_name + dimension_name + dimension_value)**
+
+=== "Function.cs"
+
+ ```csharp hl_lines="8-17"
+ using AWS.Lambda.Powertools.Metrics;
+
+ public class Function {
+
+ [Metrics(Namespace = ExampleApplication, Service = "Booking")]
+ public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
+ {
+ Metrics.PushSingleMetric(
+ metricName: "ColdStart",
+ value: 1,
+ unit: MetricUnit.Count,
+ nameSpace: "ExampleApplication",
+ service: "Booking",
+ dimensions: new Dictionary
+ {
+ {"FunctionContext", "$LATEST"}
+ });
+ ...
+ ```
+
+## AspNetCore
+
+### Installation
+
+To use the Metrics middleware in an ASP.NET Core application, you need to install the `AWS.Lambda.Powertools.Metrics.AspNetCore` NuGet package.
+
+```bash
+dotnet add package AWS.Lambda.Powertools.Metrics.AspNetCore
+```
+
+### UseMetrics() Middleware
+
+The `UseMetrics` middleware is an extension method for the `IApplicationBuilder` interface.
+
+It adds a metrics middleware to the specified application builder, which captures cold start metrics (if enabled) and flushes metrics on function exit.
+
+#### Example
+
+```csharp hl_lines="21"
+
+using AWS.Lambda.Powertools.Metrics.AspNetCore.Http;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// Configure metrics
+builder.Services.AddSingleton(_ => new MetricsBuilder()
+ .WithNamespace("MyApi") // Namespace for the metrics
+ .WithService("WeatherService") // Service name for the metrics
+ .WithCaptureColdStart(true) // Capture cold start metrics
+ .WithDefaultDimensions(new Dictionary // Default dimensions for the metrics
+ {
+ {"Environment", "Prod"},
+ {"Another", "One"}
+ })
+ .Build()); // Build the metrics
+
+builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi);
+
+var app = builder.Build();
+
+app.UseMetrics(); // Add the metrics middleware
+
+app.MapGet("/powertools", (IMetrics metrics) =>
+ {
+ // add custom metrics
+ metrics.AddMetric("MyCustomMetric", 1, MetricUnit.Count);
+ // flush metrics - this is required to ensure metrics are sent to CloudWatch
+ metrics.Flush();
+ });
+
+app.Run();
+
+```
+
+Here is the highlighted `UseMetrics` method:
+
+```csharp
+///
+/// Adds a metrics middleware to the specified application builder.
+/// This will capture cold start (if CaptureColdStart is enabled) metrics and flush metrics on function exit.
+///
+/// The application builder to add the metrics middleware to.
+/// The application builder with the metrics middleware added.
+public static IApplicationBuilder UseMetrics(this IApplicationBuilder app)
+{
+ app.UseMiddleware();
+ return app;
+}
+```
+
+Explanation:
+
+- The method is defined as an extension method for the `IApplicationBuilder` interface.
+- It adds a `MetricsMiddleware` to the application builder using the `UseMiddleware` method.
+- The `MetricsMiddleware` captures and records metrics for HTTP requests, including cold start metrics if the `CaptureColdStart` option is enabled.
+
+### WithMetrics() filter
+
+The `WithMetrics` method is an extension method for the `RouteHandlerBuilder` class.
+
+It adds a metrics filter to the specified route handler builder, which captures cold start metrics (if enabled) and flushes metrics on function exit.
+
+#### Example
+
+```csharp hl_lines="31"
+
+using AWS.Lambda.Powertools.Metrics;
+using AWS.Lambda.Powertools.Metrics.AspNetCore.Http;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// Configure metrics
+builder.Services.AddSingleton(_ => new MetricsBuilder()
+ .WithNamespace("MyApi") // Namespace for the metrics
+ .WithService("WeatherService") // Service name for the metrics
+ .WithCaptureColdStart(true) // Capture cold start metrics
+ .WithDefaultDimensions(new Dictionary // Default dimensions for the metrics
+ {
+ {"Environment", "Prod"},
+ {"Another", "One"}
+ })
+ .Build()); // Build the metrics
+
+// Add AWS Lambda support. When the application is run in Lambda, Kestrel is swapped out as the web server with Amazon.Lambda.AspNetCoreServer. This
+// package will act as the web server translating requests and responses between the Lambda event source and ASP.NET Core.
+builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi);
+
+var app = builder.Build();
+
+app.MapGet("/powertools", (IMetrics metrics) =>
+ {
+ // add custom metrics
+ metrics.AddMetric("MyCustomMetric", 1, MetricUnit.Count);
+ // flush metrics - this is required to ensure metrics are sent to CloudWatch
+ metrics.Flush();
+ })
+ .WithMetrics();
+
+app.Run();
+
+```
+
+Here is the highlighted `WithMetrics` method:
+
+```csharp
+///
+/// Adds a metrics filter to the specified route handler builder.
+/// This will capture cold start (if CaptureColdStart is enabled) metrics and flush metrics on function exit.
+///
+/// The route handler builder to add the metrics filter to.
+/// The route handler builder with the metrics filter added.
+public static RouteHandlerBuilder WithMetrics(this RouteHandlerBuilder builder)
+{
+ builder.AddEndpointFilter();
+ return builder;
+}
+```
+
+Explanation:
+
+- The method is defined as an extension method for the `RouteHandlerBuilder` class.
+- It adds a `MetricsFilter` to the route handler builder using the `AddEndpointFilter` method.
+- The `MetricsFilter` captures and records metrics for HTTP endpoints, including cold start metrics if the `CaptureColdStart` option is enabled.
+- The method returns the modified `RouteHandlerBuilder` instance with the metrics filter added.
+
+
+## Testing your code
+
+### Unit testing
+
+To test your code that uses the Metrics utility, you can use the `TestLambdaContext` class from the `Amazon.Lambda.TestUtilities` package.
+
+You can also use the `IMetrics` interface to mock the Metrics utility in your tests.
+
+Here is an example of how you can test a Lambda function that uses the Metrics utility:
+
+#### Lambda Function
+
+```csharp
+using System.Collections.Generic;
+using Amazon.Lambda.Core;
+
+public class MetricsnBuilderHandler
+{
+ private readonly IMetrics _metrics;
+
+ // Allow injection of IMetrics for testing
+ public MetricsnBuilderHandler(IMetrics metrics = null)
+ {
+ _metrics = metrics ?? new MetricsBuilder()
+ .WithCaptureColdStart(true)
+ .WithService("testService")
+ .WithNamespace("dotnet-powertools-test")
+ .WithDefaultDimensions(new Dictionary
+ {
+ { "Environment", "Prod1" },
+ { "Another", "One" }
+ }).Build();
+ }
+
+ [Metrics]
+ public void Handler(ILambdaContext context)
+ {
+ _metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
+ }
+}
+
+```
+#### Unit Tests
+
+
+```csharp
+[Fact]
+ public void Handler_With_Builder_Should_Configure_In_Constructor()
+ {
+ // Arrange
+ var handler = new MetricsnBuilderHandler();
+
+ // Act
+ handler.Handler(new TestLambdaContext
+ {
+ FunctionName = "My_Function_Name"
+ });
+
+ // Get the output and parse it
+ var metricsOutput = _consoleOut.ToString();
+
+ // Assert cold start
+ Assert.Contains(
+ "\"CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"ColdStart\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Service\",\"Environment\",\"Another\",\"FunctionName\"]]}]},\"Service\":\"testService\",\"Environment\":\"Prod1\",\"Another\":\"One\",\"FunctionName\":\"My_Function_Name\",\"ColdStart\":1}",
+ metricsOutput);
+ // Assert successful Memory metrics
+ Assert.Contains(
+ "\"CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"SuccessfulBooking\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Service\",\"Environment\",\"Another\",\"FunctionName\"]]}]},\"Service\":\"testService\",\"Environment\":\"Prod1\",\"Another\":\"One\",\"FunctionName\":\"My_Function_Name\",\"SuccessfulBooking\":1}",
+ metricsOutput);
+ }
+
+ [Fact]
+ public void Handler_With_Builder_Should_Configure_In_Constructor_Mock()
+ {
+ var metricsMock = Substitute.For();
+
+ metricsMock.Options.Returns(new MetricsOptions
+ {
+ CaptureColdStart = true,
+ Namespace = "dotnet-powertools-test",
+ Service = "testService",
+ DefaultDimensions = new Dictionary
+ {
+ { "Environment", "Prod" },
+ { "Another", "One" }
+ }
+ });
+
+ Metrics.UseMetricsForTests(metricsMock);
+
+ var sut = new MetricsnBuilderHandler(metricsMock);
+
+ // Act
+ sut.Handler(new TestLambdaContext
+ {
+ FunctionName = "My_Function_Name"
+ });
+
+ metricsMock.Received(1).PushSingleMetric("ColdStart", 1, MetricUnit.Count, "dotnet-powertools-test",
+ service: "testService", Arg.Any>());
+ metricsMock.Received(1).AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
+ }
+```
+
+### Environment variables
+
+???+ tip
+ Ignore this section, if:
+
+ * You are explicitly setting namespace/default dimension via `namespace` and `service` parameters
+ * You're not instantiating `Metrics` in the global namespace
+
+ For example, `Metrics(namespace="ExampleApplication", service="booking")`
+
+Make sure to set `POWERTOOLS_METRICS_NAMESPACE` and `POWERTOOLS_SERVICE_NAME` before running your tests to prevent failing on `SchemaValidation` exception. You can set it before you run tests by adding the environment variable.
+
+```csharp title="Injecting Metric Namespace before running tests"
+Environment.SetEnvironmentVariable("POWERTOOLS_METRICS_NAMESPACE","AWSLambdaPowertools");
+```
diff --git a/docs/core/metrics.md b/docs/core/metrics.md
index 65fb5f50..0a766414 100644
--- a/docs/core/metrics.md
+++ b/docs/core/metrics.md
@@ -109,7 +109,7 @@ You can create metrics using **`AddMetric`**, and you can create dimensions for
=== "Metrics"
- ```csharp hl_lines="8"
+ ```csharp hl_lines="5 8"
using AWS.Lambda.Powertools.Metrics;
public class Function {
diff --git a/libraries/AWS.Lambda.Powertools.sln b/libraries/AWS.Lambda.Powertools.sln
index 72aea967..bcc1a2c9 100644
--- a/libraries/AWS.Lambda.Powertools.sln
+++ b/libraries/AWS.Lambda.Powertools.sln
@@ -97,6 +97,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-FunctionHandlerTest", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-FunctionMethodAttributeTest", "tests\e2e\functions\idempotency\AOT-Function\src\AOT-FunctionMethodAttributeTest\AOT-FunctionMethodAttributeTest.csproj", "{CC8CFF43-DC72-464C-A42D-55E023DE8500}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Metrics.AspNetCore", "src\AWS.Lambda.Powertools.Metrics.AspNetCore\AWS.Lambda.Powertools.Metrics.AspNetCore.csproj", "{A2AD98B1-2BED-4864-B573-77BE7B52FED2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Metrics.AspNetCore.Tests", "tests\AWS.Lambda.Powertools.Metrics.AspNetCore.Tests\AWS.Lambda.Powertools.Metrics.AspNetCore.Tests.csproj", "{F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -518,6 +522,30 @@ Global
{CC8CFF43-DC72-464C-A42D-55E023DE8500}.Release|x64.Build.0 = Release|Any CPU
{CC8CFF43-DC72-464C-A42D-55E023DE8500}.Release|x86.ActiveCfg = Release|Any CPU
{CC8CFF43-DC72-464C-A42D-55E023DE8500}.Release|x86.Build.0 = Release|Any CPU
+ {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|x64.Build.0 = Debug|Any CPU
+ {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Debug|x86.Build.0 = Debug|Any CPU
+ {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|x64.ActiveCfg = Release|Any CPU
+ {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|x64.Build.0 = Release|Any CPU
+ {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|x86.ActiveCfg = Release|Any CPU
+ {A2AD98B1-2BED-4864-B573-77BE7B52FED2}.Release|x86.Build.0 = Release|Any CPU
+ {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|x64.Build.0 = Debug|Any CPU
+ {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Debug|x86.Build.0 = Debug|Any CPU
+ {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|x64.ActiveCfg = Release|Any CPU
+ {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|x64.Build.0 = Release|Any CPU
+ {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|x86.ActiveCfg = Release|Any CPU
+ {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
@@ -563,5 +591,7 @@ Global
{ACA789EA-BD38-490B-A7F8-6A3A86985025} = {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6}
{E71C48D2-AD56-4177-BBD7-6BB859A40C92} = {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6}
{CC8CFF43-DC72-464C-A42D-55E023DE8500} = {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6}
+ {A2AD98B1-2BED-4864-B573-77BE7B52FED2} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5}
+ {F8F80477-1EAD-4C5C-A329-CBC0A60C7CAB} = {1CFF5568-8486-475F-81F6-06105C437528}
EndGlobalSection
EndGlobal
diff --git a/libraries/src/AWS.Lambda.Powertools.Common/Core/ConsoleWrapper.cs b/libraries/src/AWS.Lambda.Powertools.Common/Core/ConsoleWrapper.cs
new file mode 100644
index 00000000..87321140
--- /dev/null
+++ b/libraries/src/AWS.Lambda.Powertools.Common/Core/ConsoleWrapper.cs
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+namespace AWS.Lambda.Powertools.Common;
+
+///
+public class ConsoleWrapper : IConsoleWrapper
+{
+ ///
+ public void WriteLine(string message) => Console.WriteLine(message);
+ ///
+ public void Debug(string message) => System.Diagnostics.Debug.WriteLine(message);
+ ///
+ public void Error(string message) => Console.Error.WriteLine(message);
+ ///
+ public string ReadLine() => Console.ReadLine();
+}
\ No newline at end of file
diff --git a/libraries/src/AWS.Lambda.Powertools.Common/Core/IConsoleWrapper.cs b/libraries/src/AWS.Lambda.Powertools.Common/Core/IConsoleWrapper.cs
new file mode 100644
index 00000000..de75020e
--- /dev/null
+++ b/libraries/src/AWS.Lambda.Powertools.Common/Core/IConsoleWrapper.cs
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+namespace AWS.Lambda.Powertools.Common;
+
+///
+/// Wrapper for console operations to facilitate testing by abstracting system console interactions.
+///
+public interface IConsoleWrapper
+{
+ ///
+ /// Writes the specified message followed by a line terminator to the standard output stream.
+ ///
+ /// The message to write.
+ void WriteLine(string message);
+
+ ///
+ /// Writes a debug message to the trace listeners in the Debug.Listeners collection.
+ ///
+ /// The debug message to write.
+ void Debug(string message);
+
+ ///
+ /// Writes the specified error message followed by a line terminator to the standard error stream.
+ ///
+ /// The error message to write.
+ void Error(string message);
+
+ ///
+ /// Reads the next line of characters from the standard input stream.
+ ///
+ /// The next line of characters from the input stream, or null if no more lines are available.
+ string ReadLine();
+}
\ No newline at end of file
diff --git a/libraries/src/AWS.Lambda.Powertools.Metrics.AspNetCore/AWS.Lambda.Powertools.Metrics.AspNetCore.csproj b/libraries/src/AWS.Lambda.Powertools.Metrics.AspNetCore/AWS.Lambda.Powertools.Metrics.AspNetCore.csproj
new file mode 100644
index 00000000..976fe8b0
--- /dev/null
+++ b/libraries/src/AWS.Lambda.Powertools.Metrics.AspNetCore/AWS.Lambda.Powertools.Metrics.AspNetCore.csproj
@@ -0,0 +1,25 @@
+
+
+
+
+ AWS.Lambda.Powertools.Metrics.AspNetCore
+ Powertools for AWS Lambda (.NET) - Metrics AspNetCore package.
+ AWS.Lambda.Powertools.Metrics.AspNetCore
+ AWS.Lambda.Powertools.Metrics.AspNetCore
+ net8.0
+ false
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libraries/src/AWS.Lambda.Powertools.Metrics.AspNetCore/Http/ColdStartTracker.cs b/libraries/src/AWS.Lambda.Powertools.Metrics.AspNetCore/Http/ColdStartTracker.cs
new file mode 100644
index 00000000..aafaad26
--- /dev/null
+++ b/libraries/src/AWS.Lambda.Powertools.Metrics.AspNetCore/Http/ColdStartTracker.cs
@@ -0,0 +1,76 @@
+/*
+ * 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 Amazon.Lambda.Core;
+using Microsoft.AspNetCore.Http;
+
+namespace AWS.Lambda.Powertools.Metrics.AspNetCore.Http;
+
+
+///
+/// Tracks and manages cold start metrics for Lambda functions in ASP.NET Core applications.
+///
+///
+/// This class is responsible for detecting and recording the first invocation (cold start) of a Lambda function.
+/// It ensures thread-safe tracking of cold starts and proper metric capture using the provided IMetrics implementation.
+///
+internal class ColdStartTracker : IDisposable
+{
+ private readonly IMetrics _metrics;
+ private static bool _coldStart = true;
+ private static readonly object _lock = new();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The metrics implementation to use for capturing cold start metrics.
+ public ColdStartTracker(IMetrics metrics)
+ {
+ _metrics = metrics;
+ }
+
+ ///
+ /// Tracks the cold start of the Lambda function.
+ ///
+ /// The current HTTP context.
+ internal void TrackColdStart(HttpContext context)
+ {
+ if (!_coldStart) return;
+
+ lock (_lock)
+ {
+ if (!_coldStart) return;
+ _metrics.CaptureColdStartMetric(context.Items["LambdaContext"] as ILambdaContext);
+ _coldStart = false;
+ }
+ }
+
+ ///
+ /// Resets the cold start tracking state.
+ ///
+ internal static void ResetColdStart()
+ {
+ lock (_lock)
+ {
+ _coldStart = true;
+ }
+ }
+
+ ///
+ public void Dispose()
+ {
+ ResetColdStart();
+ }
+}
\ No newline at end of file
diff --git a/libraries/src/AWS.Lambda.Powertools.Metrics.AspNetCore/Http/MetricsEndpointExtensions.cs b/libraries/src/AWS.Lambda.Powertools.Metrics.AspNetCore/Http/MetricsEndpointExtensions.cs
new file mode 100644
index 00000000..a2101229
--- /dev/null
+++ b/libraries/src/AWS.Lambda.Powertools.Metrics.AspNetCore/Http/MetricsEndpointExtensions.cs
@@ -0,0 +1,37 @@
+/*
+ * 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 Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+
+namespace AWS.Lambda.Powertools.Metrics.AspNetCore.Http;
+
+///
+/// Provides extension methods for adding metrics to route handlers.
+///
+public static class MetricsEndpointExtensions
+{
+ ///
+ /// Adds a metrics filter to the specified route handler builder.
+ /// This will capture cold start (if CaptureColdStart is enabled) metrics and flush metrics on function exit.
+ ///
+ /// The route handler builder to add the metrics filter to.
+ /// The route handler builder with the metrics filter added.
+ public static RouteHandlerBuilder WithMetrics(this RouteHandlerBuilder builder)
+ {
+ builder.AddEndpointFilter();
+ return builder;
+ }
+}
diff --git a/libraries/src/AWS.Lambda.Powertools.Metrics.AspNetCore/Http/MetricsFilter.cs b/libraries/src/AWS.Lambda.Powertools.Metrics.AspNetCore/Http/MetricsFilter.cs
new file mode 100644
index 00000000..f89fd94b
--- /dev/null
+++ b/libraries/src/AWS.Lambda.Powertools.Metrics.AspNetCore/Http/MetricsFilter.cs
@@ -0,0 +1,68 @@
+/*
+ * 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 Microsoft.AspNetCore.Http;
+
+namespace AWS.Lambda.Powertools.Metrics.AspNetCore.Http;
+
+///
+/// Represents a filter that captures and records metrics for HTTP endpoints.
+///
+///
+/// This filter is responsible for tracking cold starts and capturing metrics during HTTP request processing.
+/// It integrates with the ASP.NET Core endpoint routing system to inject metrics collection at the endpoint level.
+///
+///
+///
+public class MetricsFilter : IEndpointFilter, IDisposable
+{
+ private readonly ColdStartTracker _coldStartTracker;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MetricsFilter(IMetrics metrics)
+ {
+ _coldStartTracker = new ColdStartTracker(metrics);
+ }
+
+ ///
+ /// Invokes the filter asynchronously.
+ ///
+ /// The context for the endpoint filter invocation.
+ /// The delegate to invoke the next filter or endpoint.
+ /// A task that represents the asynchronous operation, containing the result of the endpoint invocation.
+ public async ValueTask