Skip to content

Commit 271f560

Browse files
authored
refactor(metrics): optimize validation and serialization (#307)
* feat: allow metric validation to be disabled * refactor: use native python validation over JSON Schema * test: adjust perf bar to flaky/CI machines * chore: flaky CI * chore: update lock to test flaky CI build * chore: ignore 3.6 to test flaky CI * chore: re-add 3.6 to test flaky CI
1 parent b4d0baa commit 271f560

File tree

8 files changed

+373
-353
lines changed

8 files changed

+373
-353
lines changed

aws_lambda_powertools/metrics/base.py

+12-11
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@
99

1010
from ..shared import constants
1111
from ..shared.functions import resolve_env_var_choice
12-
from ..shared.lazy_import import LazyLoader
1312
from .exceptions import MetricUnitError, MetricValueError, SchemaValidationError
14-
from .schema import CLOUDWATCH_EMF_SCHEMA
1513

16-
fastjsonschema = LazyLoader("fastjsonschema", globals(), "fastjsonschema")
1714
logger = logging.getLogger(__name__)
1815

1916
MAX_METRICS = 100
17+
MAX_DIMENSIONS = 9
2018

2119

2220
class MetricUnit(Enum):
@@ -180,6 +178,12 @@ def serialize_metric_set(self, metrics: Dict = None, dimensions: Dict = None, me
180178
if self.service and not self.dimension_set.get("service"):
181179
self.dimension_set["service"] = self.service
182180

181+
if len(metrics) == 0:
182+
raise SchemaValidationError("Must contain at least one metric.")
183+
184+
if self.namespace is None:
185+
raise SchemaValidationError("Must contain a metric namespace.")
186+
183187
logger.debug({"details": "Serializing metrics", "metrics": metrics, "dimensions": dimensions})
184188

185189
metric_names_and_units: List[Dict[str, str]] = [] # [ { "Name": "metric_name", "Unit": "Count" } ]
@@ -209,12 +213,6 @@ def serialize_metric_set(self, metrics: Dict = None, dimensions: Dict = None, me
209213
**metric_names_and_values, # "single_metric": 1.0
210214
}
211215

212-
try:
213-
logger.debug("Validating serialized metrics against CloudWatch EMF schema")
214-
fastjsonschema.validate(definition=CLOUDWATCH_EMF_SCHEMA, data=embedded_metrics_object)
215-
except fastjsonschema.JsonSchemaException as e:
216-
message = f"Invalid format. Error: {e.message}, Invalid item: {e.name}" # noqa: B306, E501
217-
raise SchemaValidationError(message)
218216
return embedded_metrics_object
219217

220218
def add_dimension(self, name: str, value: str):
@@ -234,7 +232,10 @@ def add_dimension(self, name: str, value: str):
234232
Dimension value
235233
"""
236234
logger.debug(f"Adding dimension: {name}:{value}")
237-
235+
if len(self.dimension_set) == 9:
236+
raise SchemaValidationError(
237+
f"Maximum number of dimensions exceeded ({MAX_DIMENSIONS}): Unable to add dimension {name}."
238+
)
238239
# Cast value to str according to EMF spec
239240
# Majority of values are expected to be string already, so
240241
# checking before casting improves performance in most cases
@@ -295,7 +296,7 @@ def __extract_metric_unit_value(self, unit: Union[str, MetricUnit]) -> str:
295296
if unit in self._metric_unit_options:
296297
unit = MetricUnit[unit].value
297298

298-
if unit not in self._metric_units: # str correta
299+
if unit not in self._metric_units:
299300
raise MetricUnitError(
300301
f"Invalid metric unit '{unit}', expected either option: {self._metric_unit_options}"
301302
)

aws_lambda_powertools/metrics/metric.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,12 @@ def single_metric(name: str, unit: MetricUnit, value: float, namespace: str = No
102102
103103
Raises
104104
------
105-
e
106-
Propagate error received
105+
MetricUnitError
106+
When metric metric isn't supported by CloudWatch
107+
MetricValueError
108+
When metric value isn't a number
109+
SchemaValidationError
110+
When metric object fails EMF schema validation
107111
"""
108112
metric_set = None
109113
try:
@@ -112,4 +116,4 @@ def single_metric(name: str, unit: MetricUnit, value: float, namespace: str = No
112116
yield metric
113117
metric_set: Dict = metric.serialize_metric_set()
114118
finally:
115-
print(json.dumps(metric_set))
119+
print(json.dumps(metric_set, separators=(",", ":")))

aws_lambda_powertools/metrics/metrics.py

+11-5
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,19 @@ def do_something():
5757
5858
Parameters
5959
----------
60-
MetricManager : MetricManager
61-
Inherits from `aws_lambda_powertools.metrics.base.MetricManager`
60+
service : str, optional
61+
service name to be used as metric dimension, by default "service_undefined"
62+
namespace : str
63+
Namespace for metrics
6264
6365
Raises
6466
------
65-
e
66-
Propagate error received
67+
MetricUnitError
68+
When metric metric isn't supported by CloudWatch
69+
MetricValueError
70+
When metric value isn't a number
71+
SchemaValidationError
72+
When metric object fails EMF schema validation
6773
"""
6874

6975
_metrics = {}
@@ -150,7 +156,7 @@ def decorate(event, context):
150156
else:
151157
metrics = self.serialize_metric_set()
152158
self.clear_metrics()
153-
print(json.dumps(metrics))
159+
print(json.dumps(metrics, separators=(",", ":")))
154160

155161
return response
156162

aws_lambda_powertools/metrics/schema.py

-94
This file was deleted.

0 commit comments

Comments
 (0)