Skip to content

Commit 807bead

Browse files
authored
Merge pull request #780 from hjgraca/feature/metrics-single-default-dimensions
chore: Metrics PushSingleMetric default dimensions
2 parents 475ade3 + ce9bf85 commit 807bead

File tree

7 files changed

+277
-1
lines changed

7 files changed

+277
-1
lines changed

docs/core/metrics-v2.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,30 @@ CloudWatch EMF uses the same dimensions across all your metrics. Use **`PushSing
602602

603603
=== "Function.cs"
604604

605+
```csharp hl_lines="8-13"
606+
using AWS.Lambda.Powertools.Metrics;
607+
608+
public class Function {
609+
610+
[Metrics(Namespace = ExampleApplication, Service = "Booking")]
611+
public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
612+
{
613+
Metrics.PushSingleMetric(
614+
metricName: "ColdStart",
615+
value: 1,
616+
unit: MetricUnit.Count,
617+
nameSpace: "ExampleApplication",
618+
service: "Booking");
619+
...
620+
```
621+
622+
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.
623+
624+
- `Metrics.DefaultDimensions`: Reuse default dimensions when using static Metrics
625+
- `Options.DefaultDimensions`: Reuse default dimensions when using Builder or Configure patterns
626+
627+
=== "New Default Dimensions.cs"
628+
605629
```csharp hl_lines="8-17"
606630
using AWS.Lambda.Powertools.Metrics;
607631

@@ -622,6 +646,47 @@ CloudWatch EMF uses the same dimensions across all your metrics. Use **`PushSing
622646
});
623647
...
624648
```
649+
=== "Default Dimensions static.cs"
650+
651+
```csharp hl_lines="8-12"
652+
using AWS.Lambda.Powertools.Metrics;
653+
654+
public class Function {
655+
656+
[Metrics(Namespace = ExampleApplication, Service = "Booking")]
657+
public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
658+
{
659+
Metrics.SetDefaultDimensions(new Dictionary<string, string>
660+
{
661+
{ "Default", "SingleMetric" }
662+
});
663+
Metrics.PushSingleMetric("SingleMetric", 1, MetricUnit.Count, dimensions: Metrics.DefaultDimensions );
664+
...
665+
```
666+
=== "Default Dimensions Options / Builder patterns .cs"
667+
668+
```csharp hl_lines="9-13 18"
669+
using AWS.Lambda.Powertools.Metrics;
670+
671+
public MetricsnBuilderHandler(IMetrics metrics = null)
672+
{
673+
_metrics = metrics ?? new MetricsBuilder()
674+
.WithCaptureColdStart(true)
675+
.WithService("testService")
676+
.WithNamespace("dotnet-powertools-test")
677+
.WithDefaultDimensions(new Dictionary<string, string>
678+
{
679+
{ "Environment", "Prod1" },
680+
{ "Another", "One" }
681+
}).Build();
682+
}
683+
684+
public void HandlerSingleMetricDimensions()
685+
{
686+
_metrics.PushSingleMetric("SuccessfulBooking", 1, MetricUnit.Count, dimensions: _metrics.Options.DefaultDimensions);
687+
}
688+
...
689+
```
625690

626691
## AspNetCore
627692

libraries/src/AWS.Lambda.Powertools.Metrics/Metrics.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,21 @@ public static IMetrics Instance
3636
get => _instance ?? new Metrics(PowertoolsConfigurations.Instance, consoleWrapper: new ConsoleWrapper());
3737
private set => _instance = value;
3838
}
39+
40+
/// <summary>
41+
/// Gets DefaultDimensions
42+
/// </summary>
43+
public static Dictionary<string, string> DefaultDimensions => Instance.Options.DefaultDimensions;
44+
45+
/// <summary>
46+
/// Gets Namespace
47+
/// </summary>
48+
public static string Namespace => Instance.Options.Namespace;
49+
50+
/// <summary>
51+
/// Gets Service
52+
/// </summary>
53+
public static string Service => Instance.Options.Service;
3954

4055
/// <inheritdoc />
4156
public MetricsOptions Options => _options ??

libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/EMFValidationTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,32 @@ public void When_PushSingleMetric_With_Namespace()
176176
Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"ExampleApplication\",\"Metrics\":[{\"Name\":\"SingleMetric\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[[\"Default\"]]}]},\"Default\":\"SingleMetric\",\"SingleMetric\":1}", metricsOutput);
177177
}
178178

179+
[Trait("Category", "SchemaValidation")]
180+
[Fact]
181+
public void When_PushSingleMetric_With_No_DefaultDimensions()
182+
{
183+
// Act
184+
_handler.PushSingleMetricNoDefaultDimensions();
185+
186+
var metricsOutput = _consoleOut.ToString();
187+
188+
// Assert
189+
Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"ExampleApplication\",\"Metrics\":[{\"Name\":\"SingleMetric\",\"Unit\":\"Count\"}],\"Dimensions\":[[]]}]},\"SingleMetric\":1}", metricsOutput);
190+
}
191+
192+
[Trait("Category", "SchemaValidation")]
193+
[Fact]
194+
public void When_PushSingleMetric_With_DefaultDimensions()
195+
{
196+
// Act
197+
_handler.PushSingleMetricDefaultDimensions();
198+
199+
var metricsOutput = _consoleOut.ToString();
200+
201+
// Assert
202+
Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"ExampleApplication\",\"Metrics\":[{\"Name\":\"SingleMetric\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Default\"]]}]},\"Default\":\"SingleMetric\",\"SingleMetric\":1}", metricsOutput);
203+
}
204+
179205
[Trait("Category", "SchemaValidation")]
180206
[Fact]
181207
public void When_PushSingleMetric_With_Env_Namespace()

libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/FunctionHandler.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,22 @@ public void PushSingleMetricWithNamespace()
6363
});
6464
}
6565

66+
[Metrics(Namespace = "ExampleApplication")]
67+
public void PushSingleMetricNoDefaultDimensions()
68+
{
69+
Metrics.PushSingleMetric("SingleMetric", 1, MetricUnit.Count);
70+
}
71+
72+
[Metrics(Namespace = "ExampleApplication")]
73+
public void PushSingleMetricDefaultDimensions()
74+
{
75+
Metrics.SetDefaultDimensions(new Dictionary<string, string>
76+
{
77+
{ "Default", "SingleMetric" }
78+
});
79+
Metrics.PushSingleMetric("SingleMetric", 1, MetricUnit.Count, dimensions: Metrics.DefaultDimensions );
80+
}
81+
6682
[Metrics]
6783
public void PushSingleMetricWithEnvNamespace()
6884
{
@@ -218,4 +234,10 @@ public void HandlerRaiseOnEmptyMetrics()
218234
{
219235

220236
}
237+
238+
[Metrics(Namespace = "ns", Service = "svc", CaptureColdStart = true)]
239+
public void HandleOnlyDimensionsInColdStart(ILambdaContext context)
240+
{
241+
Metrics.AddMetric("MyMetric", 1);
242+
}
221243
}

libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/FunctionHandlerTests.cs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ public void When_RaiseOnEmptyMetrics_And_NoMetrics_Should_ThrowException()
270270
var exception = Assert.Throws<SchemaValidationException>(() => _handler.HandlerRaiseOnEmptyMetrics());
271271
Assert.Equal("No metrics have been provided.", exception.Message);
272272
}
273-
273+
274274
[Fact]
275275
public void Handler_With_Builder_Should_Raise_Empty_Metrics()
276276
{
@@ -282,6 +282,66 @@ public void Handler_With_Builder_Should_Raise_Empty_Metrics()
282282
Assert.Equal("No metrics have been provided.", exception.Message);
283283
}
284284

285+
[Fact]
286+
public void Handler_With_Builder_Push_Single_Metric_No_Dimensions()
287+
{
288+
// Arrange
289+
var handler = new MetricsnBuilderHandler();
290+
291+
// Act
292+
handler.HandlerSingleMetric();
293+
294+
// Get the output and parse it
295+
var metricsOutput = _consoleOut.ToString();
296+
297+
Assert.Contains(
298+
"\"CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"SuccessfulBooking\",\"Unit\":\"Count\"}],\"Dimensions\":[[]]}]},\"SuccessfulBooking\":1}",
299+
metricsOutput);
300+
}
301+
302+
[Fact]
303+
public void Handler_With_Builder_Push_Single_Metric_Dimensions()
304+
{
305+
// Arrange
306+
var handler = new MetricsnBuilderHandler();
307+
308+
// Act
309+
handler.HandlerSingleMetricDimensions();
310+
311+
// Get the output and parse it
312+
var metricsOutput = _consoleOut.ToString();
313+
314+
Assert.Contains(
315+
"\"CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"SuccessfulBooking\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Service\",\"Environment\",\"Another\"]]}]},\"Service\":\"testService\",\"Environment\":\"Prod1\",\"Another\":\"One\",\"SuccessfulBooking\":1}",
316+
metricsOutput);
317+
}
318+
319+
[Fact]
320+
public void Dimension_Only_Set_In_Cold_Start()
321+
{
322+
// Arrange
323+
var handler = new FunctionHandler();
324+
325+
// Act
326+
handler.HandleOnlyDimensionsInColdStart(new TestLambdaContext
327+
{
328+
FunctionName = "My_Function_Name"
329+
});
330+
331+
// Get the output and parse it
332+
var metricsOutput = _consoleOut.ToString();
333+
334+
// Assert cold start
335+
Assert.Contains(
336+
"\"CloudWatchMetrics\":[{\"Namespace\":\"ns\",\"Metrics\":[{\"Name\":\"ColdStart\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Service\",\"FunctionName\"]]}]},\"Service\":\"svc\",\"FunctionName\":\"My_Function_Name\",\"ColdStart\":1}",
337+
metricsOutput);
338+
339+
// Assert successful add metric without dimensions
340+
Assert.Contains(
341+
"\"CloudWatchMetrics\":[{\"Namespace\":\"ns\",\"Metrics\":[{\"Name\":\"MyMetric\",\"Unit\":\"None\"}],\"Dimensions\":[[\"Service\"]]}]},\"Service\":\"svc\",\"MyMetric\":1}",
342+
metricsOutput);
343+
}
344+
285345
public void Dispose()
286346
{
287347
Metrics.ResetForTest();

libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/Handlers/MetricsnBuilderHandler.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,14 @@ public void Handler(ILambdaContext context)
3232
public void HandlerEmpty()
3333
{
3434
}
35+
36+
public void HandlerSingleMetric()
37+
{
38+
_metrics.PushSingleMetric("SuccessfulBooking", 1, MetricUnit.Count);
39+
}
40+
41+
public void HandlerSingleMetricDimensions()
42+
{
43+
_metrics.PushSingleMetric("SuccessfulBooking", 1, MetricUnit.Count, dimensions: _metrics.Options.DefaultDimensions);
44+
}
3545
}

libraries/tests/AWS.Lambda.Powertools.Metrics.Tests/MetricsTests.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,4 +243,82 @@ public void When_ColdStart_And_DefaultDimensions_Is_Null_Should_Only_Add_Service
243243
Arg.Is<string>(s => s.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"ColdStart\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"FunctionName\"]]}]},\"FunctionName\":\"TestFunction\",\"ColdStart\":1}"))
244244
);
245245
}
246+
247+
[Fact]
248+
public void Namespace_Should_Return_OptionsNamespace()
249+
{
250+
// Arrange
251+
Metrics.ResetForTest();
252+
var metricsMock = Substitute.For<IMetrics>();
253+
var optionsMock = new MetricsOptions
254+
{
255+
Namespace = "TestNamespace"
256+
};
257+
258+
metricsMock.Options.Returns(optionsMock);
259+
Metrics.UseMetricsForTests(metricsMock);
260+
261+
// Act
262+
var result = Metrics.Namespace;
263+
264+
// Assert
265+
Assert.Equal("TestNamespace", result);
266+
}
267+
268+
[Fact]
269+
public void Service_Should_Return_OptionsService()
270+
{
271+
// Arrange
272+
Metrics.ResetForTest();
273+
var metricsMock = Substitute.For<IMetrics>();
274+
var optionsMock = new MetricsOptions
275+
{
276+
Service = "TestService"
277+
};
278+
279+
metricsMock.Options.Returns(optionsMock);
280+
Metrics.UseMetricsForTests(metricsMock);
281+
282+
// Act
283+
var result = Metrics.Service;
284+
285+
// Assert
286+
Assert.Equal("TestService", result);
287+
}
288+
289+
[Fact]
290+
public void Namespace_Should_Return_Null_When_Not_Set()
291+
{
292+
// Arrange
293+
Metrics.ResetForTest();
294+
var metricsMock = Substitute.For<IMetrics>();
295+
var optionsMock = new MetricsOptions();
296+
297+
metricsMock.Options.Returns(optionsMock);
298+
Metrics.UseMetricsForTests(metricsMock);
299+
300+
// Act
301+
var result = Metrics.Namespace;
302+
303+
// Assert
304+
Assert.Null(result);
305+
}
306+
307+
[Fact]
308+
public void Service_Should_Return_Null_When_Not_Set()
309+
{
310+
// Arrange
311+
Metrics.ResetForTest();
312+
var metricsMock = Substitute.For<IMetrics>();
313+
var optionsMock = new MetricsOptions();
314+
315+
metricsMock.Options.Returns(optionsMock);
316+
Metrics.UseMetricsForTests(metricsMock);
317+
318+
// Act
319+
var result = Metrics.Service;
320+
321+
// Assert
322+
Assert.Null(result);
323+
}
246324
}

0 commit comments

Comments
 (0)