Skip to content
65 changes: 65 additions & 0 deletions docs/core/metrics-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,30 @@ CloudWatch EMF uses the same dimensions across all your metrics. Use **`PushSing

=== "Function.cs"

```csharp hl_lines="8-13"
using AWS.Lambda.Powertools.Metrics;

public class Function {

[Metrics(Namespace = ExampleApplication, Service = "Booking")]
public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
{
Metrics.PushSingleMetric(
metricName: "ColdStart",
value: 1,
unit: MetricUnit.Count,
nameSpace: "ExampleApplication",
service: "Booking");
...
```

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.

- `Metrics.DefaultDimensions`: Reuse default dimensions when using static Metrics
- `Options.DefaultDimensions`: Reuse default dimensions when using Builder or Configure patterns

=== "New Default Dimensions.cs"

```csharp hl_lines="8-17"
using AWS.Lambda.Powertools.Metrics;

Expand All @@ -622,6 +646,47 @@ CloudWatch EMF uses the same dimensions across all your metrics. Use **`PushSing
});
...
```
=== "Default Dimensions static.cs"

```csharp hl_lines="8-12"
using AWS.Lambda.Powertools.Metrics;

public class Function {

[Metrics(Namespace = ExampleApplication, Service = "Booking")]
public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
{
Metrics.SetDefaultDimensions(new Dictionary<string, string>
{
{ "Default", "SingleMetric" }
});
Metrics.PushSingleMetric("SingleMetric", 1, MetricUnit.Count, dimensions: Metrics.DefaultDimensions );
...
```
=== "Default Dimensions Options / Builder patterns .cs"

```csharp hl_lines="9-13 18"
using AWS.Lambda.Powertools.Metrics;

public MetricsnBuilderHandler(IMetrics metrics = null)
{
_metrics = metrics ?? new MetricsBuilder()
.WithCaptureColdStart(true)
.WithService("testService")
.WithNamespace("dotnet-powertools-test")
.WithDefaultDimensions(new Dictionary<string, string>
{
{ "Environment", "Prod1" },
{ "Another", "One" }
}).Build();
}

public void HandlerSingleMetricDimensions()
{
_metrics.PushSingleMetric("SuccessfulBooking", 1, MetricUnit.Count, dimensions: _metrics.Options.DefaultDimensions);
}
...
```

## AspNetCore

Expand Down
15 changes: 15 additions & 0 deletions libraries/src/AWS.Lambda.Powertools.Metrics/Metrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,21 @@ public static IMetrics Instance
get => _instance ?? new Metrics(PowertoolsConfigurations.Instance, consoleWrapper: new ConsoleWrapper());
private set => _instance = value;
}

/// <summary>
/// Gets DefaultDimensions
/// </summary>
public static Dictionary<string, string> DefaultDimensions => Instance.Options.DefaultDimensions;

/// <summary>
/// Gets Namespace
/// </summary>
public static string Namespace => Instance.Options.Namespace;

/// <summary>
/// Gets Service
/// </summary>
public static string Service => Instance.Options.Service;

/// <inheritdoc />
public MetricsOptions Options => _options ??
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,32 @@ public void When_PushSingleMetric_With_Namespace()
Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"ExampleApplication\",\"Metrics\":[{\"Name\":\"SingleMetric\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[[\"Default\"]]}]},\"Default\":\"SingleMetric\",\"SingleMetric\":1}", metricsOutput);
}

[Trait("Category", "SchemaValidation")]
[Fact]
public void When_PushSingleMetric_With_No_DefaultDimensions()
{
// Act
_handler.PushSingleMetricNoDefaultDimensions();

var metricsOutput = _consoleOut.ToString();

// Assert
Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"ExampleApplication\",\"Metrics\":[{\"Name\":\"SingleMetric\",\"Unit\":\"Count\"}],\"Dimensions\":[[]]}]},\"SingleMetric\":1}", metricsOutput);
}

[Trait("Category", "SchemaValidation")]
[Fact]
public void When_PushSingleMetric_With_DefaultDimensions()
{
// Act
_handler.PushSingleMetricDefaultDimensions();

var metricsOutput = _consoleOut.ToString();

// Assert
Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"ExampleApplication\",\"Metrics\":[{\"Name\":\"SingleMetric\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Default\"]]}]},\"Default\":\"SingleMetric\",\"SingleMetric\":1}", metricsOutput);
}

[Trait("Category", "SchemaValidation")]
[Fact]
public void When_PushSingleMetric_With_Env_Namespace()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,22 @@ public void PushSingleMetricWithNamespace()
});
}

[Metrics(Namespace = "ExampleApplication")]
public void PushSingleMetricNoDefaultDimensions()
{
Metrics.PushSingleMetric("SingleMetric", 1, MetricUnit.Count);
}

[Metrics(Namespace = "ExampleApplication")]
public void PushSingleMetricDefaultDimensions()
{
Metrics.SetDefaultDimensions(new Dictionary<string, string>
{
{ "Default", "SingleMetric" }
});
Metrics.PushSingleMetric("SingleMetric", 1, MetricUnit.Count, dimensions: Metrics.DefaultDimensions );
}

[Metrics]
public void PushSingleMetricWithEnvNamespace()
{
Expand Down Expand Up @@ -218,4 +234,10 @@ public void HandlerRaiseOnEmptyMetrics()
{

}

[Metrics(Namespace = "ns", Service = "svc", CaptureColdStart = true)]
public void HandleOnlyDimensionsInColdStart(ILambdaContext context)
{
Metrics.AddMetric("MyMetric", 1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ public void When_RaiseOnEmptyMetrics_And_NoMetrics_Should_ThrowException()
var exception = Assert.Throws<SchemaValidationException>(() => _handler.HandlerRaiseOnEmptyMetrics());
Assert.Equal("No metrics have been provided.", exception.Message);
}

[Fact]
public void Handler_With_Builder_Should_Raise_Empty_Metrics()
{
Expand All @@ -282,6 +282,66 @@ public void Handler_With_Builder_Should_Raise_Empty_Metrics()
Assert.Equal("No metrics have been provided.", exception.Message);
}

[Fact]
public void Handler_With_Builder_Push_Single_Metric_No_Dimensions()
{
// Arrange
var handler = new MetricsnBuilderHandler();

// Act
handler.HandlerSingleMetric();

// Get the output and parse it
var metricsOutput = _consoleOut.ToString();

Assert.Contains(
"\"CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"SuccessfulBooking\",\"Unit\":\"Count\"}],\"Dimensions\":[[]]}]},\"SuccessfulBooking\":1}",
metricsOutput);
}

[Fact]
public void Handler_With_Builder_Push_Single_Metric_Dimensions()
{
// Arrange
var handler = new MetricsnBuilderHandler();

// Act
handler.HandlerSingleMetricDimensions();

// Get the output and parse it
var metricsOutput = _consoleOut.ToString();

Assert.Contains(
"\"CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"SuccessfulBooking\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Service\",\"Environment\",\"Another\"]]}]},\"Service\":\"testService\",\"Environment\":\"Prod1\",\"Another\":\"One\",\"SuccessfulBooking\":1}",
metricsOutput);
}

[Fact]
public void Dimension_Only_Set_In_Cold_Start()
{
// Arrange
var handler = new FunctionHandler();

// Act
handler.HandleOnlyDimensionsInColdStart(new TestLambdaContext
{
FunctionName = "My_Function_Name"
});

// Get the output and parse it
var metricsOutput = _consoleOut.ToString();

// Assert cold start
Assert.Contains(
"\"CloudWatchMetrics\":[{\"Namespace\":\"ns\",\"Metrics\":[{\"Name\":\"ColdStart\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Service\",\"FunctionName\"]]}]},\"Service\":\"svc\",\"FunctionName\":\"My_Function_Name\",\"ColdStart\":1}",
metricsOutput);

// Assert successful add metric without dimensions
Assert.Contains(
"\"CloudWatchMetrics\":[{\"Namespace\":\"ns\",\"Metrics\":[{\"Name\":\"MyMetric\",\"Unit\":\"None\"}],\"Dimensions\":[[\"Service\"]]}]},\"Service\":\"svc\",\"MyMetric\":1}",
metricsOutput);
}

public void Dispose()
{
Metrics.ResetForTest();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,14 @@ public void Handler(ILambdaContext context)
public void HandlerEmpty()
{
}

public void HandlerSingleMetric()
{
_metrics.PushSingleMetric("SuccessfulBooking", 1, MetricUnit.Count);
}

public void HandlerSingleMetricDimensions()
{
_metrics.PushSingleMetric("SuccessfulBooking", 1, MetricUnit.Count, dimensions: _metrics.Options.DefaultDimensions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,82 @@ public void When_ColdStart_And_DefaultDimensions_Is_Null_Should_Only_Add_Service
Arg.Is<string>(s => s.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"ColdStart\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"FunctionName\"]]}]},\"FunctionName\":\"TestFunction\",\"ColdStart\":1}"))
);
}

[Fact]
public void Namespace_Should_Return_OptionsNamespace()
{
// Arrange
Metrics.ResetForTest();
var metricsMock = Substitute.For<IMetrics>();
var optionsMock = new MetricsOptions
{
Namespace = "TestNamespace"
};

metricsMock.Options.Returns(optionsMock);
Metrics.UseMetricsForTests(metricsMock);

// Act
var result = Metrics.Namespace;

// Assert
Assert.Equal("TestNamespace", result);
}

[Fact]
public void Service_Should_Return_OptionsService()
{
// Arrange
Metrics.ResetForTest();
var metricsMock = Substitute.For<IMetrics>();
var optionsMock = new MetricsOptions
{
Service = "TestService"
};

metricsMock.Options.Returns(optionsMock);
Metrics.UseMetricsForTests(metricsMock);

// Act
var result = Metrics.Service;

// Assert
Assert.Equal("TestService", result);
}

[Fact]
public void Namespace_Should_Return_Null_When_Not_Set()
{
// Arrange
Metrics.ResetForTest();
var metricsMock = Substitute.For<IMetrics>();
var optionsMock = new MetricsOptions();

metricsMock.Options.Returns(optionsMock);
Metrics.UseMetricsForTests(metricsMock);

// Act
var result = Metrics.Namespace;

// Assert
Assert.Null(result);
}

[Fact]
public void Service_Should_Return_Null_When_Not_Set()
{
// Arrange
Metrics.ResetForTest();
var metricsMock = Substitute.For<IMetrics>();
var optionsMock = new MetricsOptions();

metricsMock.Options.Returns(optionsMock);
Metrics.UseMetricsForTests(metricsMock);

// Act
var result = Metrics.Service;

// Assert
Assert.Null(result);
}
}
Loading