Skip to content

Commit 4444802

Browse files
authored
Add support to clear custom dimensions (#38)
1 parent 40e03ef commit 4444802

File tree

6 files changed

+312
-6
lines changed

6 files changed

+312
-6
lines changed

README.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,122 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
150150
"Time": 189
151151
}
152152
```
153+
154+
## API
155+
156+
### MetricsLogger
157+
158+
The `MetricsLogger` is the interface you will use to publish embedded metrics.
159+
160+
- MetricsLogger **PutMetric**(string key, double value, Unit unit)
161+
- MetricsLogger **PutMetric**(string key, double value)
162+
163+
Adds a new metric to the current logger context. Multiple metrics using the same key will be appended to an array of values. The Embedded Metric Format supports a maxumum of 100 metrics per key.
164+
165+
Metrics must meet CloudWatch Metrics requirements, otherwise a `InvalidMetricException` will be thrown. See [MetricDatum](https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_MetricDatum.html) for valid values.
166+
167+
Example:
168+
169+
```c#
170+
metrics.PutMetric("ProcessingLatency", 101, Unit.MILLISECONDS);
171+
```
172+
173+
- MetricsLogger **PutProperty**(string key, object value)
174+
175+
Adds or updates the value for a given property on this context. This value is not submitted to CloudWatch Metrics but is searchable by CloudWatch Logs Insights. This is useful for contextual and potentially high-cardinality data that is not appropriate for CloudWatch Metrics dimensions.
176+
177+
Example:
178+
```c#
179+
metrics.PutProperty("AccountId", "123456789");
180+
metrics.PutProperty("RequestId", "422b1569-16f6-4a03-b8f0-fe3fd9b100f8");
181+
182+
Dictionary<string, object> payLoad = new Dictionary<string, object>
183+
{
184+
{ "sampleTime", 123456789 },
185+
{ "temperature", 273.0 },
186+
{ "pressure", 101.3 }
187+
};
188+
metrics.PutProperty("Payload", payLoad);
189+
```
190+
191+
- MetricsLogger **PutDimensions**(DimensionSet dimensions)
192+
193+
Adds a new set of dimensions that will be associated with all metric values.
194+
195+
**WARNING**: Each dimension set will result in a new CloudWatch metric (even dimension sets with the same values).
196+
If the cardinality of a particular value is expected to be high, you should consider
197+
using `setProperty` instead.
198+
199+
Dimensions must meet CloudWatch Dimensions requirements, otherwise a `InvalidDimensionException` will be thrown. See [Dimensions](https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_Dimension.html) for valid values.
200+
201+
Example:
202+
203+
```c#
204+
DimensionSet dimensionSet = new DimensionSet();
205+
dimensionSet.AddDimension("Service", "Aggregator");
206+
dimensionSet.AddDimension("Region", "us-west-2");
207+
metrics.PutDimensions(dimensionSet);
208+
```
209+
210+
- MetricsLogger **SetDimensions**(params DimensionSet[] dimensionSets)
211+
- MetricsLogger **SetDimensions**(bool useDefault, params DimensionSet[] dimensionSets)
212+
213+
Explicitly override all dimensions. This will remove the default dimensions unless `useDefault` is set to true.
214+
215+
**WARNING**:Each dimension set will result in a new CloudWatch metric (even dimension sets with the same values).
216+
If the cardinality of a particular value is expected to be high, you should consider
217+
using `setProperty` instead.
218+
219+
Dimensions must meet CloudWatch Dimensions requirements, otherwise a `InvalidDimensionException` will be thrown. See [Dimensions](https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_Dimension.html) for valid values.
220+
221+
Examples:
222+
223+
```c#
224+
DimensionSet dimensionSet = new DimensionSet();
225+
dimensionSet.AddDimension("Service", "Aggregator");
226+
dimensionSet.AddDimension("Region", "us-west-2");
227+
metrics.SetDimensions(true, dimensionSet); // Will preserve default dimensions
228+
```
229+
230+
```c#
231+
DimensionSet dimensionSet = new DimensionSet();
232+
dimensionSet.AddDimension("Service", "Aggregator");
233+
dimensionSet.AddDimension("Region", "us-west-2");
234+
metrics.SetDimensions(dimensionSet); // Will remove default dimensions
235+
```
236+
237+
- MetricsLogger **ResetDimensions**(bool useDefault)
238+
239+
Explicitly clear all custom dimensions. Set `useDefault` to `true` to keep using the default dimensions.
240+
241+
- MetricsLogger **SetNamespace**(string logNamespace)
242+
243+
Sets the CloudWatch [namespace](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html#Namespace) that extracted metrics should be published to. If not set, a default value of aws-embedded-metrics will be used.
244+
Namespaces must meet CloudWatch Namespace requirements, otherwise a `InvalidNamespaceException` will be thrown. See [Namespace](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html#Namespace) for valid values.
245+
246+
Example:
247+
248+
```c#
249+
SetNamespace("MyApplication")
250+
```
251+
252+
- **Flush**()
253+
254+
Flushes the current MetricsContext to the configured sink and resets all properties and metric values. The namespace and default dimensions will be preserved across flushes. Custom dimensions are preserved by default, but this behavior can be changed by setting `flushPreserveDimensions = false` on the metrics logger.
255+
256+
Examples:
257+
258+
```c#
259+
flush(); // default dimensions and custom dimensions will be preserved after each flush()
260+
```
261+
262+
```c#
263+
logger.setFlushPreserveDimensions = false;
264+
flush(); // only default dimensions will be preserved after each flush()
265+
```
266+
267+
```c#
268+
setFlushPreserveDimensions(false);
269+
resetDimensions(false); // default dimensions are disabled; no dimensions will be preserved after each flush()
270+
flush();
271+
```

src/Amazon.CloudWatch.EMF/Logger/MetricsLogger.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace Amazon.CloudWatch.EMF.Logger
1111
{
1212
public class MetricsLogger : IMetricsLogger, IDisposable
1313
{
14+
public bool FlushPreserveDimensions = true;
1415
private readonly ILogger _logger;
1516
private readonly IEnvironment _environment;
1617
private readonly IEnvironmentProvider _environmentProvider;
@@ -86,7 +87,7 @@ public void Flush()
8687
_logger.LogDebug("Sending data to sink. {}", _environment.Sink.GetType().Name);
8788

8889
_environment.Sink.Accept(_context);
89-
_context = _context.CreateCopyWithContext();
90+
_context = _context.CreateCopyWithContext(this.FlushPreserveDimensions);
9091
}
9192

9293
/// <summary>
@@ -133,6 +134,30 @@ public MetricsLogger SetDimensions(params DimensionSet[] dimensionSets)
133134
return this;
134135
}
135136

137+
/// <summary>
138+
/// Overwrites all dimensions on this MetricsLogger instance; also overriding default dimensions unless useDefault is set to true
139+
/// </summary>
140+
/// <param name="useDefault">whether to use the default dimensions</param>
141+
/// <param name="dimensionSets">the dimensionSets to set</param>
142+
/// <returns>the current logger</returns>
143+
/// <seealso cref="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html#Dimension"/>
144+
public MetricsLogger SetDimensions(bool useDefault, params DimensionSet[] dimensionSets)
145+
{
146+
_context.SetDimensions(useDefault, dimensionSets);
147+
return this;
148+
}
149+
150+
/// <summary>
151+
/// Resets all dimensions on this MetricsLogger instance; also resetting default dimensions unless useDefault is set to true
152+
/// </summary>
153+
/// <param name="useDefault">whether to use the default dimensions</param>
154+
/// <returns>the current logger</returns>
155+
public MetricsLogger ResetDimensions(bool useDefault)
156+
{
157+
_context.ResetDimensions(useDefault);
158+
return this;
159+
}
160+
136161
/// <summary>
137162
/// Puts a metric value.
138163
/// This value will be emitted to CloudWatch Metrics asynchronously and does

src/Amazon.CloudWatch.EMF/Model/MetricDirective.cs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,17 +109,40 @@ internal void SetDimensions(List<DimensionSet> dimensionSets)
109109
CustomDimensionSets = dimensionSets;
110110
}
111111

112+
/// <summary>
113+
/// Overrides all existing dimensions, optionally suppressing any default dimensions.
114+
/// </summary>
115+
/// <param name="useDefault">whether to use the default dimensions</param>
116+
/// <param name="dimensionSets">the dimension sets to use in lieu of all existing custom and default dimensions</param>
117+
internal void SetDimensions(bool useDefault, List<DimensionSet> dimensionSets)
118+
{
119+
_shouldUseDefaultDimensionSet = useDefault;
120+
CustomDimensionSets = dimensionSets;
121+
}
122+
123+
/// <summary>
124+
/// Resets all dimensions to the default dimensions.
125+
/// </summary>
126+
/// <param name="useDefault">whether to keep the default dimensions</param>
127+
internal void ResetDimensions(bool useDefault)
128+
{
129+
_shouldUseDefaultDimensionSet = useDefault;
130+
CustomDimensionSets = new List<DimensionSet>();
131+
}
132+
112133
internal List<DimensionSet> GetAllDimensionSets()
113134
{
114135
if (!_shouldUseDefaultDimensionSet)
115136
{
116137
return CustomDimensionSets;
117138
}
118139

119-
CustomDimensionSets.ForEach(ds => DefaultDimensionSet.AddRange(ds));
140+
var dimensions = new DimensionSet();
141+
142+
dimensions.AddRange(this.DefaultDimensionSet);
143+
CustomDimensionSets.ForEach(ds => dimensions.AddRange(ds));
120144

121-
var dimensions = new List<DimensionSet>() { DefaultDimensionSet };
122-
return dimensions;
145+
return new List<DimensionSet>() { dimensions };
123146
}
124147

125148
internal MetricDirective DeepCloneWithNewMetrics(List<MetricDefinition> metrics)

src/Amazon.CloudWatch.EMF/Model/MetricsContext.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,15 @@ public MetricsContext(
5252
/// <summary>
5353
/// Creates a new MetricsContext with the same namespace, properties,
5454
/// and dimensions as this one but empty metrics-directive collection.
55+
/// Custom dimensions are preserved by default unless preservedDimension is set to false
5556
/// </summary>
5657
/// <returns></returns>
57-
public MetricsContext CreateCopyWithContext()
58+
public MetricsContext CreateCopyWithContext(bool preserveDimensions = true)
5859
{
5960
return new MetricsContext(
6061
_metricDirective.Namespace,
6162
_rootNode.GetProperties(),
62-
_metricDirective.CustomDimensionSets,
63+
preserveDimensions ? _metricDirective.CustomDimensionSets : new List<DimensionSet>(),
6364
_metricDirective.DefaultDimensionSet);
6465
}
6566

@@ -189,6 +190,25 @@ public void SetDimensions(params DimensionSet[] dimensionSets)
189190
_metricDirective.SetDimensions(dimensionSets.ToList());
190191
}
191192

193+
/// <summary>
194+
/// Update the dimensions to the specified list; optionally overriding default dimensions
195+
/// </summary>
196+
/// <param name="useDefault">whether to use default dimensions or not.</param>
197+
/// <param name="dimensionSets">the dimensionSets to use instead of all existing dimensions and default dimensions.</param>
198+
public void SetDimensions(bool useDefault, params DimensionSet[] dimensionSets)
199+
{
200+
_metricDirective.SetDimensions(useDefault, dimensionSets.ToList());
201+
}
202+
203+
/// <summary>
204+
/// Reset all dimensions
205+
/// </summary>
206+
/// <param name="useDefault">whether to keep default dimensions or not.</param>
207+
public void ResetDimensions(bool useDefault)
208+
{
209+
_metricDirective.ResetDimensions(useDefault);
210+
}
211+
192212
/// <summary>
193213
/// Adds the specified key-value pair to the metadata.
194214
/// </summary>

src/Amazon.CloudWatch.EMF/Utils/Validator.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ namespace Amazon.CloudWatch.EMF.Utils
55
{
66
public class Validator
77
{
8+
/// <summary>
9+
/// Validates dimension set.
10+
/// </summary>
11+
/// <param name="dimensionName">Dimension name</param>
12+
/// <param name="dimensionValue">Dimension value</param>
13+
/// <exception cref="InvalidDimensionException">Thrown when dimension name or value is invalid</exception>
814
internal static void ValidateDimensionSet(in string dimensionName, in string dimensionValue)
915
{
1016
if (dimensionName == null || dimensionName.Trim().Length == 0)
@@ -43,6 +49,12 @@ internal static void ValidateDimensionSet(in string dimensionName, in string dim
4349
}
4450
}
4551

52+
/// <summary>
53+
/// Validates metric name.
54+
/// </summary>
55+
/// <param name="name">Metric name</param>
56+
/// <param name="value">Metric value</param>
57+
/// <exception cref="InvalidMetricException">Thrown when metric name or value is invalid</exception>
4658
internal static void ValidateMetric(in string name, in double value)
4759
{
4860
if (name == null || name.Trim().Length == 0)
@@ -61,6 +73,11 @@ internal static void ValidateMetric(in string name, in double value)
6173
}
6274
}
6375

76+
/// <summary>
77+
/// Validates namespace.
78+
/// </summary>
79+
/// <param name="@namespace">Namespace</param>
80+
/// <exception cref="InvalidNamespaceException">Thrown when namespace is invalid</exception>
6481
internal static void ValidateNamespace(in string @namespace)
6582
{
6683
if (@namespace == null || @namespace.Trim().Length == 0)
@@ -79,6 +96,11 @@ internal static void ValidateNamespace(in string @namespace)
7996
}
8097
}
8198

99+
/// <summary>
100+
/// Checks if given string is only ASCII.
101+
/// </summary>
102+
/// <param name="str">String to check</param>
103+
/// <returns>True if string is only ASCII, false otherwise</returns>
82104
private static bool IsAscii(in string str)
83105
{
84106
return Encoding.UTF8.GetByteCount(str) == str.Length;

0 commit comments

Comments
 (0)