Skip to content

Commit 3da6b12

Browse files
committed
merge
2 parents d02ae25 + d5593b3 commit 3da6b12

34 files changed

+1049
-512
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ jobs:
3737
run: dotnet build --configuration Release --no-restore /tl
3838

3939
- name: Test & Code Coverage
40-
run: dotnet test --no-restore --filter "Category!=E2E" --collect:"XPlat Code Coverage" --results-directory ./codecov --verbosity quiet
40+
run: dotnet test --no-restore --filter "Category!=E2E" --collect:"XPlat Code Coverage" --results-directory ./codecov --verbosity normal
4141

4242
- name: Test Examples
43-
run: dotnet test ../examples/ --verbosity quiet
43+
run: dotnet test ../examples/ --verbosity normal
4444

4545
- name: Codecov
4646
uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # 5.3.1

.github/workflows/label_pr_on_title.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,12 @@ on:
66
types:
77
- completed
88

9-
permissions:
10-
contents: read
11-
129
jobs:
1310
get_pr_details:
1411
permissions:
15-
id-token: write
1612
contents: read
13+
id-token: write
14+
pull-requests: read
1715
# Guardrails to only ever run if PR recording workflow was indeed
1816
# run in a PR event and ran successfully
1917
if: ${{ github.event.workflow_run.conclusion == 'success' }}
@@ -27,6 +25,7 @@ jobs:
2725
permissions:
2826
contents: read
2927
id-token: write
28+
pull-requests: write
3029
needs: get_pr_details
3130
runs-on: ubuntu-latest
3231
steps:

.github/workflows/on_label_added.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ permissions:
1212
jobs:
1313
get_pr_details:
1414
permissions:
15+
contents: read
1516
id-token: write
1617
if: ${{ github.event.workflow_run.conclusion == 'success' }}
1718
uses: ./.github/workflows/reusable_export_pr_details.yml

.github/workflows/on_merged_pr.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ jobs:
2525
permissions:
2626
contents: read
2727
id-token: write
28+
issues: write
29+
pull-requests: write
2830
needs: get_pr_details
2931
runs-on: ubuntu-latest
3032
if: needs.get_pr_details.outputs.prIsMerged == 'true'

.github/workflows/on_opened_pr.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ jobs:
1313
get_pr_details:
1414
permissions:
1515
id-token: write
16+
contents: read
1617
if: ${{ github.event.workflow_run.conclusion == 'success' }}
1718
uses: ./.github/workflows/reusable_export_pr_details.yml
1819
with:

.github/workflows/reusable_export_pr_details.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ jobs:
4343
export_pr_details:
4444
permissions:
4545
id-token: write
46+
contents: read
4647
# see https://github.com/aws-powertools/powertools-lambda-python/issues/1349
4748
if: inputs.workflow_origin == 'aws-powertools/powertools-lambda-dotnet'
4849
runs-on: ubuntu-latest

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[![Build](https://github.com/aws-powertools/powertools-lambda-dotnet/actions/workflows/build.yml/badge.svg?branch=develop)](https://github.com/aws-powertools/powertools-lambda-dotnet/actions/workflows/build.yml)
55
[![codecov.io](https://codecov.io/github/aws-powertools/powertools-lambda-dotnet/branch/develop/graphs/badge.svg)](https://app.codecov.io/gh/aws-powertools/powertools-lambda-dotnet)
66
[![dotnet support](https://img.shields.io/static/v1?label=dotnet&message=%20NET6.0|NET8.0&color=blue?style=flat-square&logo=dotnet)](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)
7-
[![NuGet Downloads](https://img.shields.io/nuget/dt/AWS.Lambda.Powertools.Logging.svg)](https://www.nuget.org/packages?q=AWS.Lambda.Powertools)
7+
[![NuGet Downloads](https://img.shields.io/nuget/dt/AWS.Lambda.Powertools.Logging.svg)](https://www.nuget.org/packages?q=AWS.Lambda.Powertools) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/aws-powertools/powertools-lambda-dotnet/badge)](https://scorecard.dev/viewer/?uri=github.com/aws-powertools/powertools-lambda-dotnet)
88
[![Join our Discord](https://dcbadge.vercel.app/api/server/B8zZKbbyET?style=flat-square)](https://discord.gg/B8zZKbbyET)
99

1010
Powertools for AWS Lambda (.NET) is a developer toolkit to implement Serverless [best practices and increase developer velocity](https://docs.powertools.aws.dev/lambda-dotnet/#features).

docs/core/metrics-v2.md

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ These metrics can be visualized through [Amazon CloudWatch Console](https://aws.
1616
* 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
1717
* Support for AspNetCore middleware and filters to capture metrics for HTTP requests
1818

19+
## Breaking changes from V1
20+
21+
* **`Dimensions`** outputs as an array of arrays instead of an array of objects. Example: `Dimensions: [["service", "Environment"]]` instead of `Dimensions: ["service", "Environment"]`
22+
* **`FunctionName`** is not added as default dimension and only to cold start metric.
23+
* **`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.
24+
1925
<br />
2026

2127
<figure>
@@ -432,7 +438,7 @@ During metrics validation, if no metrics are provided then a warning will be log
432438
!!! tip "Metric validation"
433439
If metrics are provided, and any of the following criteria are not met, **`SchemaValidationException`** will be raised:
434440

435-
* Maximum of 9 dimensions
441+
* Maximum of 30 dimensions
436442
* Namespace is set
437443
* Metric units must be [supported by CloudWatch](https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_MetricDatum.html)
438444

@@ -610,7 +616,7 @@ CloudWatch EMF uses the same dimensions across all your metrics. Use **`PushSing
610616
...
611617
```
612618

613-
By default it will skip all previously defined dimensions including default dimensions. Use default_dimensions keyword argument if you want to reuse default dimensions or specify custom dimensions from a dictionary.
619+
By default it will skip all previously defined dimensions including default dimensions. Use `dimensions` argument if you want to reuse default dimensions or specify custom dimensions from a dictionary.
614620

615621
- `Metrics.DefaultDimensions`: Reuse default dimensions when using static Metrics
616622
- `Options.DefaultDimensions`: Reuse default dimensions when using Builder or Configure patterns
@@ -631,7 +637,7 @@ By default it will skip all previously defined dimensions including default dime
631637
unit: MetricUnit.Count,
632638
nameSpace: "ExampleApplication",
633639
service: "Booking",
634-
defaultDimensions: new Dictionary<string, string>
640+
dimensions: new Dictionary<string, string>
635641
{
636642
{"FunctionContext", "$LATEST"}
637643
});
@@ -651,10 +657,10 @@ By default it will skip all previously defined dimensions including default dime
651657
{
652658
{ "Default", "SingleMetric" }
653659
});
654-
Metrics.PushSingleMetric("SingleMetric", 1, MetricUnit.Count, defaultDimensions: Metrics.DefaultDimensions );
660+
Metrics.PushSingleMetric("SingleMetric", 1, MetricUnit.Count, dimensions: Metrics.DefaultDimensions );
655661
...
656662
```
657-
=== "Default Dimensions Options / Builder patterns .cs"
663+
=== "Default Dimensions Options / Builder patterns"
658664

659665
```csharp hl_lines="9-13 18"
660666
using AWS.Lambda.Powertools.Metrics;
@@ -674,11 +680,59 @@ By default it will skip all previously defined dimensions including default dime
674680

675681
public void HandlerSingleMetricDimensions()
676682
{
677-
_metrics.PushSingleMetric("SuccessfulBooking", 1, MetricUnit.Count, defaultDimensions: _metrics.Options.DefaultDimensions);
683+
_metrics.PushSingleMetric("SuccessfulBooking", 1, MetricUnit.Count, dimensions: _metrics.Options.DefaultDimensions);
678684
}
679685
...
680686
```
681687

688+
### Cold start Function Name dimension
689+
690+
In cases where you want to customize the `FunctionName` dimension in Cold Start metrics.
691+
692+
This is useful where you want to maintain the same name in case of auto generated handler names (cdk, top-level statement functions, etc.)
693+
694+
Example:
695+
696+
=== "In decorator"
697+
698+
```csharp hl_lines="5"
699+
using AWS.Lambda.Powertools.Metrics;
700+
701+
public class Function {
702+
703+
[Metrics(FunctionName = "MyFunctionName", Namespace = "ExampleApplication", Service = "Booking")]
704+
public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
705+
{
706+
Metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
707+
...
708+
}
709+
```
710+
=== "Configure / Builder patterns"
711+
712+
```csharp hl_lines="12"
713+
using AWS.Lambda.Powertools.Metrics;
714+
715+
public class Function {
716+
717+
public Function()
718+
{
719+
Metrics.Configure(options =>
720+
{
721+
options.Namespace = "dotnet-powertools-test";
722+
options.Service = "testService";
723+
options.CaptureColdStart = true;
724+
options.FunctionName = "MyFunctionName";
725+
});
726+
}
727+
728+
[Metrics]
729+
public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
730+
{
731+
Metrics.AddMetric("SuccessfulBooking", 1, MetricUnit.Count);
732+
...
733+
}
734+
```
735+
682736
## AspNetCore
683737

684738
### Installation
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
using System;
17+
18+
namespace AWS.Lambda.Powertools.Common;
19+
20+
/// <inheritdoc />
21+
public class ConsoleWrapper : IConsoleWrapper
22+
{
23+
/// <inheritdoc />
24+
public void WriteLine(string message) => Console.WriteLine(message);
25+
/// <inheritdoc />
26+
public void Debug(string message) => System.Diagnostics.Debug.WriteLine(message);
27+
/// <inheritdoc />
28+
public void Error(string message) => Console.Error.WriteLine(message);
29+
/// <inheritdoc />
30+
public string ReadLine() => Console.ReadLine();
31+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
namespace AWS.Lambda.Powertools.Common;
17+
18+
/// <summary>
19+
/// Wrapper for console operations to facilitate testing by abstracting system console interactions.
20+
/// </summary>
21+
public interface IConsoleWrapper
22+
{
23+
/// <summary>
24+
/// Writes the specified message followed by a line terminator to the standard output stream.
25+
/// </summary>
26+
/// <param name="message">The message to write.</param>
27+
void WriteLine(string message);
28+
29+
/// <summary>
30+
/// Writes a debug message to the trace listeners in the Debug.Listeners collection.
31+
/// </summary>
32+
/// <param name="message">The debug message to write.</param>
33+
void Debug(string message);
34+
35+
/// <summary>
36+
/// Writes the specified error message followed by a line terminator to the standard error stream.
37+
/// </summary>
38+
/// <param name="message">The error message to write.</param>
39+
void Error(string message);
40+
41+
/// <summary>
42+
/// Reads the next line of characters from the standard input stream.
43+
/// </summary>
44+
/// <returns>The next line of characters from the input stream, or null if no more lines are available.</returns>
45+
string ReadLine();
46+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
using Amazon.Lambda.Core;
17+
using Microsoft.AspNetCore.Http;
18+
19+
namespace AWS.Lambda.Powertools.Metrics.AspNetCore.Http;
20+
21+
22+
/// <summary>
23+
/// Tracks and manages cold start metrics for Lambda functions in ASP.NET Core applications.
24+
/// </summary>
25+
/// <remarks>
26+
/// This class is responsible for detecting and recording the first invocation (cold start) of a Lambda function.
27+
/// It ensures thread-safe tracking of cold starts and proper metric capture using the provided IMetrics implementation.
28+
/// </remarks>
29+
internal class ColdStartTracker : IDisposable
30+
{
31+
private readonly IMetrics _metrics;
32+
private static bool _coldStart = true;
33+
private static readonly object _lock = new();
34+
35+
/// <summary>
36+
/// Initializes a new instance of the <see cref="ColdStartTracker"/> class.
37+
/// </summary>
38+
/// <param name="metrics">The metrics implementation to use for capturing cold start metrics.</param>
39+
public ColdStartTracker(IMetrics metrics)
40+
{
41+
_metrics = metrics;
42+
}
43+
44+
/// <summary>
45+
/// Tracks the cold start of the Lambda function.
46+
/// </summary>
47+
/// <param name="context">The current HTTP context.</param>
48+
internal void TrackColdStart(HttpContext context)
49+
{
50+
if (!_coldStart) return;
51+
52+
lock (_lock)
53+
{
54+
if (!_coldStart) return;
55+
_metrics.CaptureColdStartMetric(context.Items["LambdaContext"] as ILambdaContext);
56+
_coldStart = false;
57+
}
58+
}
59+
60+
/// <summary>
61+
/// Resets the cold start tracking state.
62+
/// </summary>
63+
internal static void ResetColdStart()
64+
{
65+
lock (_lock)
66+
{
67+
_coldStart = true;
68+
}
69+
}
70+
71+
/// <inheritdoc />
72+
public void Dispose()
73+
{
74+
ResetColdStart();
75+
}
76+
}

0 commit comments

Comments
 (0)