Skip to content

Commit 31dad3b

Browse files
committed
Prevent default service to be added to PushSingleMetric
1 parent 2b6a336 commit 31dad3b

File tree

4 files changed

+110
-45
lines changed

4 files changed

+110
-45
lines changed

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

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/*
22
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3-
*
3+
*
44
* Licensed under the Apache License, Version 2.0 (the "License").
55
* You may not use this file except in compliance with the License.
66
* A copy of the License is located at
7-
*
7+
*
88
* http://aws.amazon.com/apache2.0
9-
*
9+
*
1010
* or in the "license" file accompanying this file. This file is distributed
1111
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
1212
* express or implied. See the License for the specific language governing
@@ -31,7 +31,7 @@ public class Metrics : IMetrics, IDisposable
3131
/// The instance
3232
/// </summary>
3333
private static IMetrics _instance;
34-
34+
3535
/// <summary>
3636
/// The context
3737
/// </summary>
@@ -46,7 +46,7 @@ public class Metrics : IMetrics, IDisposable
4646
/// If true, Powertools for AWS Lambda (.NET) will throw an exception on empty metrics when trying to flush
4747
/// </summary>
4848
private readonly bool _raiseOnEmptyMetrics;
49-
49+
5050
/// <summary>
5151
/// The capture cold start enabled
5252
/// </summary>
@@ -76,9 +76,8 @@ internal Metrics(IPowertoolsConfigurations powertoolsConfigurations, string name
7676
_raiseOnEmptyMetrics = raiseOnEmptyMetrics;
7777
_captureColdStartEnabled = captureColdStartEnabled;
7878
_context = InitializeContext(nameSpace, service, null);
79-
79+
8080
_powertoolsConfigurations.SetExecutionEnvironment(this);
81-
8281
}
8382

8483
/// <summary>
@@ -96,18 +95,20 @@ void IMetrics.AddMetric(string key, double value, MetricUnit unit, MetricResolut
9695
{
9796
if (string.IsNullOrWhiteSpace(key))
9897
throw new ArgumentNullException(
99-
nameof(key), "'AddMetric' method requires a valid metrics key. 'Null' or empty values are not allowed.");
100-
101-
if (value < 0) {
98+
nameof(key),
99+
"'AddMetric' method requires a valid metrics key. 'Null' or empty values are not allowed.");
100+
101+
if (value < 0)
102+
{
102103
throw new ArgumentException(
103104
"'AddMetric' method requires a valid metrics value. Value must be >= 0.", nameof(value));
104105
}
105106

106107
lock (_lockObj)
107108
{
108109
var metrics = _context.GetMetrics();
109-
110-
if (metrics.Count > 0 &&
110+
111+
if (metrics.Count > 0 &&
111112
(metrics.Count == PowertoolsConfigurations.MaxMetrics ||
112113
metrics.FirstOrDefault(x => x.Name == key)
113114
?.Values.Count == PowertoolsConfigurations.MaxMetrics))
@@ -134,7 +135,14 @@ void IMetrics.SetNamespace(string nameSpace)
134135
/// <returns>Namespace identifier</returns>
135136
string IMetrics.GetNamespace()
136137
{
137-
return _context.GetNamespace();
138+
try
139+
{
140+
return _context.GetNamespace();
141+
}
142+
catch
143+
{
144+
return null;
145+
}
138146
}
139147

140148
/// <summary>
@@ -143,7 +151,14 @@ string IMetrics.GetNamespace()
143151
/// <returns>System.String.</returns>
144152
string IMetrics.GetService()
145153
{
146-
return _context.GetService();
154+
try
155+
{
156+
return _context.GetService();
157+
}
158+
catch
159+
{
160+
return null;
161+
}
147162
}
148163

149164
/// <summary>
@@ -226,7 +241,7 @@ void IMetrics.Flush(bool metricsOverflow)
226241
{
227242
if (!_captureColdStartEnabled)
228243
Console.WriteLine(
229-
"##WARNING## Metrics and Metadata have not been specified. No data will be sent to Cloudwatch Metrics.");
244+
"##User-WARNING## No application metrics to publish. The cold-start metric may be published if enabled. If application metrics should never be empty, consider using 'RaiseOnEmptyMetrics = true'");
230245
}
231246
}
232247

@@ -283,7 +298,7 @@ public void Dispose()
283298
Dispose(true);
284299
GC.SuppressFinalize(this);
285300
}
286-
301+
287302
/// <summary>
288303
///
289304
/// </summary>
@@ -356,7 +371,7 @@ public static void SetDefaultDimensions(Dictionary<string, string> defaultDimens
356371
{
357372
_instance.SetDefaultDimensions(defaultDimensions);
358373
}
359-
374+
360375
/// <summary>
361376
/// Clears both default dimensions and dimensions lists
362377
/// </summary>
@@ -389,14 +404,14 @@ private void Flush(MetricsContext context)
389404
/// <param name="defaultDimensions">Default dimensions list</param>
390405
/// <param name="metricResolution">Metrics resolution</param>
391406
public static void PushSingleMetric(string metricName, double value, MetricUnit unit, string nameSpace = null,
392-
string service = null, Dictionary<string, string> defaultDimensions = null, MetricResolution metricResolution = MetricResolution.Default)
407+
string service = null, Dictionary<string, string> defaultDimensions = null,
408+
MetricResolution metricResolution = MetricResolution.Default)
393409
{
394410
_instance.PushSingleMetric(metricName, value, unit, nameSpace, service, defaultDimensions, metricResolution);
395411
}
396412

397413
/// <summary>
398-
/// Sets global namespace, service name and default dimensions list. Service name is automatically added as a default
399-
/// dimension
414+
/// Sets global namespace, service name and default dimensions list.
400415
/// </summary>
401416
/// <param name="nameSpace">Metrics namespace</param>
402417
/// <param name="service">Service Name</param>
@@ -406,19 +421,26 @@ private MetricsContext InitializeContext(string nameSpace, string service,
406421
Dictionary<string, string> defaultDimensions)
407422
{
408423
var context = new MetricsContext();
424+
var defaultDimensionsList = DictionaryToList(defaultDimensions);
409425

410426
context.SetNamespace(!string.IsNullOrWhiteSpace(nameSpace)
411427
? nameSpace
412-
: _powertoolsConfigurations.MetricsNamespace);
428+
: _instance.GetNamespace() ?? _powertoolsConfigurations.MetricsNamespace);
413429

414-
context.SetService(!string.IsNullOrWhiteSpace(service)
430+
// this needs to check if service is set through code or env variables
431+
// the default value service_undefined has to be ignored and return null so it is not added as default
432+
// TODO: Check if there is a way to get the default dimensions and if it makes sense
433+
var parsedService = !string.IsNullOrWhiteSpace(service)
415434
? service
416-
: _powertoolsConfigurations.Service);
417-
418-
var defaultDimensionsList = DictionaryToList(defaultDimensions);
435+
: _powertoolsConfigurations.Service == "service_undefined"
436+
? null
437+
: _powertoolsConfigurations.Service;
419438

420-
// Add service as a default dimension
421-
defaultDimensionsList.Add(new DimensionSet("Service", context.GetService()));
439+
if (parsedService != null)
440+
{
441+
context.SetService(parsedService);
442+
defaultDimensionsList.Add(new DimensionSet("Service", context.GetService()));
443+
}
422444

423445
context.SetDefaultDimensions(defaultDimensionsList);
424446

@@ -447,4 +469,4 @@ internal static void ResetForTest()
447469
{
448470
_instance = null;
449471
}
450-
}
472+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ namespace AWS.Lambda.Powertools.Metrics;
6666
/// </listheader>
6767
/// <item>
6868
/// <term>Service</term>
69-
/// <description>string, service name is used for metric dimension across all metrics, by default service_undefined</description>
69+
/// <description>string, service name is used for metric dimension</description>
7070
/// </item>
7171
/// <item>
7272
/// <term>Namespace</term>

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

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,39 @@ public void When_Multiple_DimensionsAreAdded_MustExistAsMembers()
157157
var metricsOutput = _consoleOut.ToString();
158158

159159
// Assert
160-
Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"ns1\",\"Metrics\":[{\"Name\":\"Lambda Execute\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[[\"Type\",\"Service\"]]}]},\"Type\":\"Start\",\"Service\":\"service_undefined\",\"Lambda Execute\":1}", metricsOutput);
161-
162-
Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"ns2\",\"Metrics\":[{\"Name\":\"Lambda Execute\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[[\"Type\",\"SessionId\",\"Service\"]]}]},\"Type\":\"Start\",\"SessionId\":\"Unset\",\"Service\":\"service_undefined\",\"Lambda Execute\":1}", metricsOutput);
160+
Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"ColdStart\",\"Unit\":\"Count\"}],\"Dimensions\":[[\"Service\"]]}]},\"Service\":\"ServiceName\",\"ColdStart\":1}", metricsOutput);
161+
Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"SingleMetric1\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[[\"Default1\"]]}]},\"Default1\":\"SingleMetric1\",\"SingleMetric1\":1}", metricsOutput);
162+
Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"ns2\",\"Metrics\":[{\"Name\":\"SingleMetric2\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[[\"Default1\",\"Default2\"]]}]},\"Default1\":\"SingleMetric2\",\"Default2\":\"SingleMetric2\",\"SingleMetric2\":1}", metricsOutput);
163+
Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"AddMetric\",\"Unit\":\"Count\",\"StorageResolution\":1},{\"Name\":\"AddMetric2\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[[\"Service\"]]}]},\"Service\":\"ServiceName\",\"AddMetric\":1,\"AddMetric2\":1}", metricsOutput);
164+
}
165+
166+
[Trait("Category", "SchemaValidation")]
167+
[Fact]
168+
public void When_PushSingleMetric_With_Namespace()
169+
{
170+
// Act
171+
_handler.PushSingleMetricWithNamespace();
172+
173+
var metricsOutput = _consoleOut.ToString();
174+
175+
// Assert
176+
Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"ExampleApplication\",\"Metrics\":[{\"Name\":\"SingleMetric\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[[\"Default\"]]}]},\"Default\":\"SingleMetric\",\"SingleMetric\":1}", metricsOutput);
177+
}
178+
179+
[Trait("Category", "SchemaValidation")]
180+
[Fact]
181+
public void When_PushSingleMetric_With_Env_Namespace()
182+
{
183+
// Arrange
184+
Environment.SetEnvironmentVariable("POWERTOOLS_METRICS_NAMESPACE", "EnvNamespace");
163185

164-
Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"dotnet-powertools-test\",\"Metrics\":[{\"Name\":\"Lambda Execute\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[[\"Service\",\"Default\",\"SessionId\",\"Type\"]]}]},\"Service\":\"testService\",\"Default\":\"Initial\",\"SessionId\":\"MySessionId\",\"Type\":\"Start\",\"Lambda Execute\":1}", metricsOutput);
186+
// Act
187+
_handler.PushSingleMetricWithEnvNamespace();
188+
189+
var metricsOutput = _consoleOut.ToString();
190+
191+
// Assert
192+
Assert.Contains("\"CloudWatchMetrics\":[{\"Namespace\":\"EnvNamespace\",\"Metrics\":[{\"Name\":\"SingleMetric\",\"Unit\":\"Count\",\"StorageResolution\":1}],\"Dimensions\":[[\"Default\"]]}]},\"Default\":\"SingleMetric\",\"SingleMetric\":1}", metricsOutput);
165193
}
166194

167195
[Trait("Category", "MetricsImplementation")]
@@ -366,6 +394,7 @@ public void Dispose()
366394
{
367395
// need to reset instance after each test
368396
MetricsAspect.ResetForTest();
397+
Environment.SetEnvironmentVariable("POWERTOOLS_METRICS_NAMESPACE", null);
369398
}
370399
}
371400
}

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

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,25 +39,39 @@ public void AddDimensions()
3939
Metrics.AddMetric("TestMetric", 1, MetricUnit.Count);
4040
}
4141

42-
[Metrics(Namespace = "dotnet-powertools-test", Service = "testService")]
42+
[Metrics(Namespace = "dotnet-powertools-test", Service = "ServiceName", CaptureColdStart = true)]
4343
public void AddMultipleDimensions()
4444
{
45-
Metrics.SetDefaultDimensions(new Dictionary<string, string> {
46-
{ "Default", "Initial" }
47-
});
48-
Metrics.PushSingleMetric("Lambda Execute", 1, MetricUnit.Count, metricResolution: MetricResolution.High, nameSpace: "ns1",
45+
Metrics.PushSingleMetric("SingleMetric1", 1, MetricUnit.Count, metricResolution: MetricResolution.High,
4946
defaultDimensions: new Dictionary<string, string> {
50-
{ "Type", "Start" }
47+
{ "Default1", "SingleMetric1" }
5148
});
5249

53-
Metrics.PushSingleMetric("Lambda Execute", 1, MetricUnit.Count, metricResolution: MetricResolution.High, nameSpace: "ns2",
50+
Metrics.PushSingleMetric("SingleMetric2", 1, MetricUnit.Count, metricResolution: MetricResolution.High, nameSpace: "ns2",
51+
defaultDimensions: new Dictionary<string, string> {
52+
{ "Default1", "SingleMetric2" },
53+
{ "Default2", "SingleMetric2" }
54+
});
55+
Metrics.AddMetric("AddMetric", 1, MetricUnit.Count, MetricResolution.High);
56+
Metrics.AddMetric("AddMetric2", 1, MetricUnit.Count, MetricResolution.High);
57+
}
58+
59+
[Metrics(Namespace = "ExampleApplication")]
60+
public void PushSingleMetricWithNamespace()
61+
{
62+
Metrics.PushSingleMetric("SingleMetric", 1, MetricUnit.Count, metricResolution: MetricResolution.High,
63+
defaultDimensions: new Dictionary<string, string> {
64+
{ "Default", "SingleMetric" }
65+
});
66+
}
67+
68+
[Metrics]
69+
public void PushSingleMetricWithEnvNamespace()
70+
{
71+
Metrics.PushSingleMetric("SingleMetric", 1, MetricUnit.Count, metricResolution: MetricResolution.High,
5472
defaultDimensions: new Dictionary<string, string> {
55-
{ "Type", "Start" },
56-
{ "SessionId", "Unset" }
73+
{ "Default", "SingleMetric" }
5774
});
58-
Metrics.AddMetric("Lambda Execute", 1, MetricUnit.Count, MetricResolution.High);
59-
Metrics.AddDimension("SessionId", "MySessionId");
60-
Metrics.AddDimension("Type", "Start");
6175
}
6276

6377
[Metrics(Namespace = "dotnet-powertools-test", Service = "testService")]

0 commit comments

Comments
 (0)