We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
aiohttp is now a required dependency

Even if you don't use or require aiohttp, powertools now has it as a dependency. So now projects without this already declared will fail

```
/opt/python/3.7.6/lib/python3.7/site-packages/aws_lambda_powertools/tracing/__init__.py:3: in <module>
    from aws_xray_sdk.ext.aiohttp.client import aws_xray_trace_config as aiohttp_trace_config
/opt/python/3.7.6/lib/python3.7/site-packages/aws_xray_sdk/ext/aiohttp/client.py:4: in <module>
    import aiohttp
E   ModuleNotFoundError: No module named 'aiohttp'
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `aws_lambda_powertools/metrics/base.py`
Content:
```
1 import datetime
2 import json
3 import logging
4 import numbers
5 import os
6 import pathlib
7 from typing import Dict, List, Union
8 
9 import fastjsonschema
10 
11 from aws_lambda_powertools.helper.models import MetricUnit
12 
13 from .exceptions import MetricUnitError, MetricValueError, SchemaValidationError, UniqueNamespaceError
14 
15 logger = logging.getLogger(__name__)
16 
17 _schema_path = pathlib.Path(__file__).parent / "./schema.json"
18 with _schema_path.open() as f:
19     CLOUDWATCH_EMF_SCHEMA = json.load(f)
20 
21 MAX_METRICS = 100
22 
23 
24 class MetricManager:
25     """Base class for metric functionality (namespace, metric, dimension, serialization)
26 
27     MetricManager creates metrics asynchronously thanks to CloudWatch Embedded Metric Format (EMF).
28     CloudWatch EMF can create up to 100 metrics per EMF object
29     and metrics, dimensions, and namespace created via MetricManager
30     will adhere to the schema, will be serialized and validated against EMF Schema.
31 
32     **Use `aws_lambda_powertools.metrics.metrics.Metrics` or
33     `aws_lambda_powertools.metrics.metric.single_metric` to create EMF metrics.**
34 
35     Environment variables
36     ---------------------
37     POWERTOOLS_METRICS_NAMESPACE : str
38         metric namespace to be set for all metrics
39 
40     Raises
41     ------
42     MetricUnitError
43         When metric metric isn't supported by CloudWatch
44     MetricValueError
45         When metric value isn't a number
46     UniqueNamespaceError
47         When an additional namespace is set
48     SchemaValidationError
49         When metric object fails EMF schema validation
50     """
51 
52     def __init__(self, metric_set: Dict[str, str] = None, dimension_set: Dict = None, namespace: str = None):
53         self.metric_set = metric_set or {}
54         self.dimension_set = dimension_set or {}
55         self.namespace = os.getenv("POWERTOOLS_METRICS_NAMESPACE") or namespace
56         self._metric_units = [unit.value for unit in MetricUnit]
57         self._metric_unit_options = list(MetricUnit.__members__)
58 
59     def add_namespace(self, name: str):
60         """Adds given metric namespace
61 
62         Example
63         -------
64         **Add metric namespace**
65 
66             metric.add_namespace(name="ServerlessAirline")
67 
68         Parameters
69         ----------
70         name : str
71             Metric namespace
72         """
73         if self.namespace is not None:
74             raise UniqueNamespaceError(
75                 f"Namespace '{self.namespace}' already set - Only one namespace is allowed across metrics"
76             )
77         logger.debug(f"Adding metrics namespace: {name}")
78         self.namespace = name
79 
80     def add_metric(self, name: str, unit: MetricUnit, value: Union[float, int]):
81         """Adds given metric
82 
83         Example
84         -------
85         **Add given metric using MetricUnit enum**
86 
87             metric.add_metric(name="BookingConfirmation", unit=MetricUnit.Count, value=1)
88 
89         **Add given metric using plain string as value unit**
90 
91             metric.add_metric(name="BookingConfirmation", unit="Count", value=1)
92 
93         Parameters
94         ----------
95         name : str
96             Metric name
97         unit : MetricUnit
98             `aws_lambda_powertools.helper.models.MetricUnit`
99         value : float
100             Metric value
101 
102         Raises
103         ------
104         MetricUnitError
105             When metric unit is not supported by CloudWatch
106         """
107         if not isinstance(value, numbers.Number):
108             raise MetricValueError(f"{value} is not a valid number")
109 
110         unit = self.__extract_metric_unit_value(unit=unit)
111         metric = {"Unit": unit, "Value": float(value)}
112         logger.debug(f"Adding metric: {name} with {metric}")
113         self.metric_set[name] = metric
114 
115         if len(self.metric_set) == MAX_METRICS:
116             logger.debug(f"Exceeded maximum of {MAX_METRICS} metrics - Publishing existing metric set")
117             metrics = self.serialize_metric_set()
118             print(json.dumps(metrics))
119             self.metric_set = {}
120 
121     def serialize_metric_set(self, metrics: Dict = None, dimensions: Dict = None) -> Dict:
122         """Serializes metric and dimensions set
123 
124         Parameters
125         ----------
126         metrics : Dict, optional
127             Dictionary of metrics to serialize, by default None
128         dimensions : Dict, optional
129             Dictionary of dimensions to serialize, by default None
130 
131         Example
132         -------
133         **Serialize metrics into EMF format**
134 
135             metrics = MetricManager()
136             # ...add metrics, dimensions, namespace
137             ret = metrics.serialize_metric_set()
138 
139         Returns
140         -------
141         Dict
142             Serialized metrics following EMF specification
143 
144         Raises
145         ------
146         SchemaValidationError
147             Raised when serialization fail schema validation
148         """
149         if metrics is None:  # pragma: no cover
150             metrics = self.metric_set
151 
152         if dimensions is None:  # pragma: no cover
153             dimensions = self.dimension_set
154 
155         logger.debug("Serializing...", {"metrics": metrics, "dimensions": dimensions})
156 
157         dimension_keys: List[str] = list(dimensions.keys())
158         metric_names_unit: List[Dict[str, str]] = []
159         metric_set: Dict[str, str] = {}
160 
161         for metric_name in metrics:
162             metric: str = metrics[metric_name]
163             metric_value: int = metric.get("Value", 0)
164             metric_unit: str = metric.get("Unit", "")
165 
166             metric_names_unit.append({"Name": metric_name, "Unit": metric_unit})
167             metric_set.update({metric_name: metric_value})
168 
169         metrics_definition = {
170             "CloudWatchMetrics": [
171                 {"Namespace": self.namespace, "Dimensions": [dimension_keys], "Metrics": metric_names_unit}
172             ]
173         }
174         metrics_timestamp = {"Timestamp": int(datetime.datetime.now().timestamp() * 1000)}
175         metric_set["_aws"] = {**metrics_timestamp, **metrics_definition}
176         metric_set.update(**dimensions)
177 
178         try:
179             logger.debug("Validating serialized metrics against CloudWatch EMF schema", metric_set)
180             fastjsonschema.validate(definition=CLOUDWATCH_EMF_SCHEMA, data=metric_set)
181         except fastjsonschema.JsonSchemaException as e:
182             message = f"Invalid format. Error: {e.message}, Invalid item: {e.name}"  # noqa: B306, E501
183             raise SchemaValidationError(message)
184         return metric_set
185 
186     def add_dimension(self, name: str, value: str):
187         """Adds given dimension to all metrics
188 
189         Example
190         -------
191         **Add a metric dimensions**
192 
193             metric.add_dimension(name="operation", value="confirm_booking")
194 
195         Parameters
196         ----------
197         name : str
198             Dimension name
199         value : str
200             Dimension value
201         """
202         logger.debug(f"Adding dimension: {name}:{value}")
203         self.dimension_set[name] = value
204 
205     def __extract_metric_unit_value(self, unit: Union[str, MetricUnit]) -> str:
206         """Return metric value from metric unit whether that's str or MetricUnit enum
207 
208         Parameters
209         ----------
210         unit : Union[str, MetricUnit]
211             Metric unit
212 
213         Returns
214         -------
215         str
216             Metric unit value (e.g. "Seconds", "Count/Second")
217 
218         Raises
219         ------
220         MetricUnitError
221             When metric unit is not supported by CloudWatch
222         """
223 
224         if isinstance(unit, str):
225             if unit in self._metric_unit_options:
226                 unit = MetricUnit[unit].value
227 
228             if unit not in self._metric_units:  # str correta
229                 raise MetricUnitError(
230                     f"Invalid metric unit '{unit}', expected either option: {self._metric_unit_options}"
231                 )
232 
233         if isinstance(unit, MetricUnit):
234             unit = unit.value
235 
236         return unit
237
```

Path: `aws_lambda_powertools/metrics/metrics.py`
Content:
```
1 import functools
2 import json
3 import logging
4 from typing import Any, Callable
5 
6 from aws_lambda_powertools.metrics.base import MetricManager
7 
8 logger = logging.getLogger(__name__)
9 
10 
11 class Metrics(MetricManager):
12     """Metrics create an EMF object with up to 100 metrics
13 
14     Use Metrics when you need to create multiple metrics that have
15     dimensions in common (e.g. service_name="payment").
16 
17     Metrics up to 100 metrics in memory and are shared across
18     all its instances. That means it can be safely instantiated outside
19     of a Lambda function, or anywhere else.
20 
21     A decorator (log_metrics) is provided so metrics are published at the end of its execution.
22     If more than 100 metrics are added at a given function execution,
23     these metrics are serialized and published before adding a given metric
24     to prevent metric truncation.
25 
26     Example
27     -------
28     **Creates a few metrics and publish at the end of a function execution**
29 
30         from aws_lambda_powertools.metrics import Metrics
31 
32         metrics = Metrics()
33         metrics.add_namespace(name="ServerlessAirline")
34         metrics.add_metric(name="ColdStart", unit=MetricUnit.Count, value=1)
35         metrics.add_metric(name="BookingConfirmation", unit="Count", value=1)
36         metrics.add_dimension(name="service", value="booking")
37         metrics.add_dimension(name="function_version", value="$LATEST")
38         ...
39 
40         @tracer.capture_lambda_handler
41         @metrics.log_metrics()
42         def lambda_handler():
43                 do_something()
44                 return True
45 
46         def do_something():
47                 metrics.add_metric(name="Something", unit="Count", value=1)
48 
49     Environment variables
50     ---------------------
51     POWERTOOLS_METRICS_NAMESPACE : str
52         metric namespace
53 
54     Parameters
55     ----------
56     MetricManager : MetricManager
57         Inherits from `aws_lambda_powertools.metrics.base.MetricManager`
58 
59     Raises
60     ------
61     e
62         Propagate error received
63     """
64 
65     _metrics = {}
66     _dimensions = {}
67 
68     def __init__(self, metric_set=None, dimension_set=None, namespace=None):
69         super().__init__(metric_set=self._metrics, dimension_set=self._dimensions, namespace=namespace)
70 
71     def log_metrics(self, lambda_handler: Callable[[Any, Any], Any] = None):
72         """Decorator to serialize and publish metrics at the end of a function execution.
73 
74         Be aware that the log_metrics **does call* the decorated function (e.g. lambda_handler).
75 
76         Example
77         -------
78         **Lambda function using tracer and metrics decorators**
79 
80             metrics = Metrics()
81             tracer = Tracer(service="payment")
82 
83             @tracer.capture_lambda_handler
84             @metrics.log_metrics
85                 def handler(event, context)
86 
87         Parameters
88         ----------
89         lambda_handler : Callable[[Any, Any], Any], optional
90             Lambda function handler, by default None
91 
92         Raises
93         ------
94         e
95             Propagate error received
96         """
97 
98         @functools.wraps(lambda_handler)
99         def decorate(*args, **kwargs):
100             try:
101                 response = lambda_handler(*args, **kwargs)
102             finally:
103                 metrics = self.serialize_metric_set()
104                 logger.debug("Publishing metrics", {"metrics": metrics})
105                 print(json.dumps(metrics))
106 
107             return response
108 
109         return decorate
110
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/aws_lambda_powertools/metrics/base.py b/aws_lambda_powertools/metrics/base.py
--- a/aws_lambda_powertools/metrics/base.py
+++ b/aws_lambda_powertools/metrics/base.py
@@ -50,8 +50,8 @@
     """
 
     def __init__(self, metric_set: Dict[str, str] = None, dimension_set: Dict = None, namespace: str = None):
-        self.metric_set = metric_set or {}
-        self.dimension_set = dimension_set or {}
+        self.metric_set = metric_set if metric_set is not None else {}
+        self.dimension_set = dimension_set if dimension_set is not None else {}
         self.namespace = os.getenv("POWERTOOLS_METRICS_NAMESPACE") or namespace
         self._metric_units = [unit.value for unit in MetricUnit]
         self._metric_unit_options = list(MetricUnit.__members__)
@@ -116,7 +116,10 @@
             logger.debug(f"Exceeded maximum of {MAX_METRICS} metrics - Publishing existing metric set")
             metrics = self.serialize_metric_set()
             print(json.dumps(metrics))
-            self.metric_set = {}
+
+            # clear metric set only as opposed to metrics and dimensions set
+            # since we could have more than 100 metrics
+            self.metric_set.clear()
 
     def serialize_metric_set(self, metrics: Dict = None, dimensions: Dict = None) -> Dict:
         """Serializes metric and dimensions set
diff --git a/aws_lambda_powertools/metrics/metrics.py b/aws_lambda_powertools/metrics/metrics.py
--- a/aws_lambda_powertools/metrics/metrics.py
+++ b/aws_lambda_powertools/metrics/metrics.py
@@ -65,8 +65,15 @@
     _metrics = {}
     _dimensions = {}
 
-    def __init__(self, metric_set=None, dimension_set=None, namespace=None):
-        super().__init__(metric_set=self._metrics, dimension_set=self._dimensions, namespace=namespace)
+    def __init__(self):
+        self.metric_set = self._metrics
+        self.dimension_set = self._dimensions
+        super().__init__(metric_set=self.metric_set, dimension_set=self.dimension_set)
+
+    def clear_metrics(self):
+        logger.debug("Clearing out existing metric set from memory")
+        self.metric_set.clear()
+        self.dimension_set.clear()
 
     def log_metrics(self, lambda_handler: Callable[[Any, Any], Any] = None):
         """Decorator to serialize and publish metrics at the end of a function execution.
@@ -101,6 +108,7 @@
                 response = lambda_handler(*args, **kwargs)
             finally:
                 metrics = self.serialize_metric_set()
+                self.clear_metrics()
                 logger.debug("Publishing metrics", {"metrics": metrics})
                 print(json.dumps(metrics))

{"golden_diff": "diff --git a/aws_lambda_powertools/metrics/base.py b/aws_lambda_powertools/metrics/base.py\n--- a/aws_lambda_powertools/metrics/base.py\n+++ b/aws_lambda_powertools/metrics/base.py\n@@ -50,8 +50,8 @@\n     \"\"\"\n \n     def __init__(self, metric_set: Dict[str, str] = None, dimension_set: Dict = None, namespace: str = None):\n-        self.metric_set = metric_set or {}\n-        self.dimension_set = dimension_set or {}\n+        self.metric_set = metric_set if metric_set is not None else {}\n+        self.dimension_set = dimension_set if dimension_set is not None else {}\n         self.namespace = os.getenv(\"POWERTOOLS_METRICS_NAMESPACE\") or namespace\n         self._metric_units = [unit.value for unit in MetricUnit]\n         self._metric_unit_options = list(MetricUnit.__members__)\n@@ -116,7 +116,10 @@\n             logger.debug(f\"Exceeded maximum of {MAX_METRICS} metrics - Publishing existing metric set\")\n             metrics = self.serialize_metric_set()\n             print(json.dumps(metrics))\n-            self.metric_set = {}\n+\n+            # clear metric set only as opposed to metrics and dimensions set\n+            # since we could have more than 100 metrics\n+            self.metric_set.clear()\n \n     def serialize_metric_set(self, metrics: Dict = None, dimensions: Dict = None) -> Dict:\n         \"\"\"Serializes metric and dimensions set\ndiff --git a/aws_lambda_powertools/metrics/metrics.py b/aws_lambda_powertools/metrics/metrics.py\n--- a/aws_lambda_powertools/metrics/metrics.py\n+++ b/aws_lambda_powertools/metrics/metrics.py\n@@ -65,8 +65,15 @@\n     _metrics = {}\n     _dimensions = {}\n \n-    def __init__(self, metric_set=None, dimension_set=None, namespace=None):\n-        super().__init__(metric_set=self._metrics, dimension_set=self._dimensions, namespace=namespace)\n+    def __init__(self):\n+        self.metric_set = self._metrics\n+        self.dimension_set = self._dimensions\n+        super().__init__(metric_set=self.metric_set, dimension_set=self.dimension_set)\n+\n+    def clear_metrics(self):\n+        logger.debug(\"Clearing out existing metric set from memory\")\n+        self.metric_set.clear()\n+        self.dimension_set.clear()\n \n     def log_metrics(self, lambda_handler: Callable[[Any, Any], Any] = None):\n         \"\"\"Decorator to serialize and publish metrics at the end of a function execution.\n@@ -101,6 +108,7 @@\n                 response = lambda_handler(*args, **kwargs)\n             finally:\n                 metrics = self.serialize_metric_set()\n+                self.clear_metrics()\n                 logger.debug(\"Publishing metrics\", {\"metrics\": metrics})\n                 print(json.dumps(metrics))\n", "issue": "aiohttp is now a required dependency\n\r\nEven if you don't use or require aiohttp, powertools now has it as a dependency. So now projects without this already declared will fail\r\n\r\n```\r\n/opt/python/3.7.6/lib/python3.7/site-packages/aws_lambda_powertools/tracing/__init__.py:3: in <module>\r\n    from aws_xray_sdk.ext.aiohttp.client import aws_xray_trace_config as aiohttp_trace_config\r\n/opt/python/3.7.6/lib/python3.7/site-packages/aws_xray_sdk/ext/aiohttp/client.py:4: in <module>\r\n    import aiohttp\r\nE   ModuleNotFoundError: No module named 'aiohttp'\r\n```\n", "before_files": [{"content": "import datetime\nimport json\nimport logging\nimport numbers\nimport os\nimport pathlib\nfrom typing import Dict, List, Union\n\nimport fastjsonschema\n\nfrom aws_lambda_powertools.helper.models import MetricUnit\n\nfrom .exceptions import MetricUnitError, MetricValueError, SchemaValidationError, UniqueNamespaceError\n\nlogger = logging.getLogger(__name__)\n\n_schema_path = pathlib.Path(__file__).parent / \"./schema.json\"\nwith _schema_path.open() as f:\n    CLOUDWATCH_EMF_SCHEMA = json.load(f)\n\nMAX_METRICS = 100\n\n\nclass MetricManager:\n    \"\"\"Base class for metric functionality (namespace, metric, dimension, serialization)\n\n    MetricManager creates metrics asynchronously thanks to CloudWatch Embedded Metric Format (EMF).\n    CloudWatch EMF can create up to 100 metrics per EMF object\n    and metrics, dimensions, and namespace created via MetricManager\n    will adhere to the schema, will be serialized and validated against EMF Schema.\n\n    **Use `aws_lambda_powertools.metrics.metrics.Metrics` or\n    `aws_lambda_powertools.metrics.metric.single_metric` to create EMF metrics.**\n\n    Environment variables\n    ---------------------\n    POWERTOOLS_METRICS_NAMESPACE : str\n        metric namespace to be set for all metrics\n\n    Raises\n    ------\n    MetricUnitError\n        When metric metric isn't supported by CloudWatch\n    MetricValueError\n        When metric value isn't a number\n    UniqueNamespaceError\n        When an additional namespace is set\n    SchemaValidationError\n        When metric object fails EMF schema validation\n    \"\"\"\n\n    def __init__(self, metric_set: Dict[str, str] = None, dimension_set: Dict = None, namespace: str = None):\n        self.metric_set = metric_set or {}\n        self.dimension_set = dimension_set or {}\n        self.namespace = os.getenv(\"POWERTOOLS_METRICS_NAMESPACE\") or namespace\n        self._metric_units = [unit.value for unit in MetricUnit]\n        self._metric_unit_options = list(MetricUnit.__members__)\n\n    def add_namespace(self, name: str):\n        \"\"\"Adds given metric namespace\n\n        Example\n        -------\n        **Add metric namespace**\n\n            metric.add_namespace(name=\"ServerlessAirline\")\n\n        Parameters\n        ----------\n        name : str\n            Metric namespace\n        \"\"\"\n        if self.namespace is not None:\n            raise UniqueNamespaceError(\n                f\"Namespace '{self.namespace}' already set - Only one namespace is allowed across metrics\"\n            )\n        logger.debug(f\"Adding metrics namespace: {name}\")\n        self.namespace = name\n\n    def add_metric(self, name: str, unit: MetricUnit, value: Union[float, int]):\n        \"\"\"Adds given metric\n\n        Example\n        -------\n        **Add given metric using MetricUnit enum**\n\n            metric.add_metric(name=\"BookingConfirmation\", unit=MetricUnit.Count, value=1)\n\n        **Add given metric using plain string as value unit**\n\n            metric.add_metric(name=\"BookingConfirmation\", unit=\"Count\", value=1)\n\n        Parameters\n        ----------\n        name : str\n            Metric name\n        unit : MetricUnit\n            `aws_lambda_powertools.helper.models.MetricUnit`\n        value : float\n            Metric value\n\n        Raises\n        ------\n        MetricUnitError\n            When metric unit is not supported by CloudWatch\n        \"\"\"\n        if not isinstance(value, numbers.Number):\n            raise MetricValueError(f\"{value} is not a valid number\")\n\n        unit = self.__extract_metric_unit_value(unit=unit)\n        metric = {\"Unit\": unit, \"Value\": float(value)}\n        logger.debug(f\"Adding metric: {name} with {metric}\")\n        self.metric_set[name] = metric\n\n        if len(self.metric_set) == MAX_METRICS:\n            logger.debug(f\"Exceeded maximum of {MAX_METRICS} metrics - Publishing existing metric set\")\n            metrics = self.serialize_metric_set()\n            print(json.dumps(metrics))\n            self.metric_set = {}\n\n    def serialize_metric_set(self, metrics: Dict = None, dimensions: Dict = None) -> Dict:\n        \"\"\"Serializes metric and dimensions set\n\n        Parameters\n        ----------\n        metrics : Dict, optional\n            Dictionary of metrics to serialize, by default None\n        dimensions : Dict, optional\n            Dictionary of dimensions to serialize, by default None\n\n        Example\n        -------\n        **Serialize metrics into EMF format**\n\n            metrics = MetricManager()\n            # ...add metrics, dimensions, namespace\n            ret = metrics.serialize_metric_set()\n\n        Returns\n        -------\n        Dict\n            Serialized metrics following EMF specification\n\n        Raises\n        ------\n        SchemaValidationError\n            Raised when serialization fail schema validation\n        \"\"\"\n        if metrics is None:  # pragma: no cover\n            metrics = self.metric_set\n\n        if dimensions is None:  # pragma: no cover\n            dimensions = self.dimension_set\n\n        logger.debug(\"Serializing...\", {\"metrics\": metrics, \"dimensions\": dimensions})\n\n        dimension_keys: List[str] = list(dimensions.keys())\n        metric_names_unit: List[Dict[str, str]] = []\n        metric_set: Dict[str, str] = {}\n\n        for metric_name in metrics:\n            metric: str = metrics[metric_name]\n            metric_value: int = metric.get(\"Value\", 0)\n            metric_unit: str = metric.get(\"Unit\", \"\")\n\n            metric_names_unit.append({\"Name\": metric_name, \"Unit\": metric_unit})\n            metric_set.update({metric_name: metric_value})\n\n        metrics_definition = {\n            \"CloudWatchMetrics\": [\n                {\"Namespace\": self.namespace, \"Dimensions\": [dimension_keys], \"Metrics\": metric_names_unit}\n            ]\n        }\n        metrics_timestamp = {\"Timestamp\": int(datetime.datetime.now().timestamp() * 1000)}\n        metric_set[\"_aws\"] = {**metrics_timestamp, **metrics_definition}\n        metric_set.update(**dimensions)\n\n        try:\n            logger.debug(\"Validating serialized metrics against CloudWatch EMF schema\", metric_set)\n            fastjsonschema.validate(definition=CLOUDWATCH_EMF_SCHEMA, data=metric_set)\n        except fastjsonschema.JsonSchemaException as e:\n            message = f\"Invalid format. Error: {e.message}, Invalid item: {e.name}\"  # noqa: B306, E501\n            raise SchemaValidationError(message)\n        return metric_set\n\n    def add_dimension(self, name: str, value: str):\n        \"\"\"Adds given dimension to all metrics\n\n        Example\n        -------\n        **Add a metric dimensions**\n\n            metric.add_dimension(name=\"operation\", value=\"confirm_booking\")\n\n        Parameters\n        ----------\n        name : str\n            Dimension name\n        value : str\n            Dimension value\n        \"\"\"\n        logger.debug(f\"Adding dimension: {name}:{value}\")\n        self.dimension_set[name] = value\n\n    def __extract_metric_unit_value(self, unit: Union[str, MetricUnit]) -> str:\n        \"\"\"Return metric value from metric unit whether that's str or MetricUnit enum\n\n        Parameters\n        ----------\n        unit : Union[str, MetricUnit]\n            Metric unit\n\n        Returns\n        -------\n        str\n            Metric unit value (e.g. \"Seconds\", \"Count/Second\")\n\n        Raises\n        ------\n        MetricUnitError\n            When metric unit is not supported by CloudWatch\n        \"\"\"\n\n        if isinstance(unit, str):\n            if unit in self._metric_unit_options:\n                unit = MetricUnit[unit].value\n\n            if unit not in self._metric_units:  # str correta\n                raise MetricUnitError(\n                    f\"Invalid metric unit '{unit}', expected either option: {self._metric_unit_options}\"\n                )\n\n        if isinstance(unit, MetricUnit):\n            unit = unit.value\n\n        return unit\n", "path": "aws_lambda_powertools/metrics/base.py"}, {"content": "import functools\nimport json\nimport logging\nfrom typing import Any, Callable\n\nfrom aws_lambda_powertools.metrics.base import MetricManager\n\nlogger = logging.getLogger(__name__)\n\n\nclass Metrics(MetricManager):\n    \"\"\"Metrics create an EMF object with up to 100 metrics\n\n    Use Metrics when you need to create multiple metrics that have\n    dimensions in common (e.g. service_name=\"payment\").\n\n    Metrics up to 100 metrics in memory and are shared across\n    all its instances. That means it can be safely instantiated outside\n    of a Lambda function, or anywhere else.\n\n    A decorator (log_metrics) is provided so metrics are published at the end of its execution.\n    If more than 100 metrics are added at a given function execution,\n    these metrics are serialized and published before adding a given metric\n    to prevent metric truncation.\n\n    Example\n    -------\n    **Creates a few metrics and publish at the end of a function execution**\n\n        from aws_lambda_powertools.metrics import Metrics\n\n        metrics = Metrics()\n        metrics.add_namespace(name=\"ServerlessAirline\")\n        metrics.add_metric(name=\"ColdStart\", unit=MetricUnit.Count, value=1)\n        metrics.add_metric(name=\"BookingConfirmation\", unit=\"Count\", value=1)\n        metrics.add_dimension(name=\"service\", value=\"booking\")\n        metrics.add_dimension(name=\"function_version\", value=\"$LATEST\")\n        ...\n\n        @tracer.capture_lambda_handler\n        @metrics.log_metrics()\n        def lambda_handler():\n                do_something()\n                return True\n\n        def do_something():\n                metrics.add_metric(name=\"Something\", unit=\"Count\", value=1)\n\n    Environment variables\n    ---------------------\n    POWERTOOLS_METRICS_NAMESPACE : str\n        metric namespace\n\n    Parameters\n    ----------\n    MetricManager : MetricManager\n        Inherits from `aws_lambda_powertools.metrics.base.MetricManager`\n\n    Raises\n    ------\n    e\n        Propagate error received\n    \"\"\"\n\n    _metrics = {}\n    _dimensions = {}\n\n    def __init__(self, metric_set=None, dimension_set=None, namespace=None):\n        super().__init__(metric_set=self._metrics, dimension_set=self._dimensions, namespace=namespace)\n\n    def log_metrics(self, lambda_handler: Callable[[Any, Any], Any] = None):\n        \"\"\"Decorator to serialize and publish metrics at the end of a function execution.\n\n        Be aware that the log_metrics **does call* the decorated function (e.g. lambda_handler).\n\n        Example\n        -------\n        **Lambda function using tracer and metrics decorators**\n\n            metrics = Metrics()\n            tracer = Tracer(service=\"payment\")\n\n            @tracer.capture_lambda_handler\n            @metrics.log_metrics\n                def handler(event, context)\n\n        Parameters\n        ----------\n        lambda_handler : Callable[[Any, Any], Any], optional\n            Lambda function handler, by default None\n\n        Raises\n        ------\n        e\n            Propagate error received\n        \"\"\"\n\n        @functools.wraps(lambda_handler)\n        def decorate(*args, **kwargs):\n            try:\n                response = lambda_handler(*args, **kwargs)\n            finally:\n                metrics = self.serialize_metric_set()\n                logger.debug(\"Publishing metrics\", {\"metrics\": metrics})\n                print(json.dumps(metrics))\n\n            return response\n\n        return decorate\n", "path": "aws_lambda_powertools/metrics/metrics.py"}], "after_files": [{"content": "import datetime\nimport json\nimport logging\nimport numbers\nimport os\nimport pathlib\nfrom typing import Dict, List, Union\n\nimport fastjsonschema\n\nfrom aws_lambda_powertools.helper.models import MetricUnit\n\nfrom .exceptions import MetricUnitError, MetricValueError, SchemaValidationError, UniqueNamespaceError\n\nlogger = logging.getLogger(__name__)\n\n_schema_path = pathlib.Path(__file__).parent / \"./schema.json\"\nwith _schema_path.open() as f:\n    CLOUDWATCH_EMF_SCHEMA = json.load(f)\n\nMAX_METRICS = 100\n\n\nclass MetricManager:\n    \"\"\"Base class for metric functionality (namespace, metric, dimension, serialization)\n\n    MetricManager creates metrics asynchronously thanks to CloudWatch Embedded Metric Format (EMF).\n    CloudWatch EMF can create up to 100 metrics per EMF object\n    and metrics, dimensions, and namespace created via MetricManager\n    will adhere to the schema, will be serialized and validated against EMF Schema.\n\n    **Use `aws_lambda_powertools.metrics.metrics.Metrics` or\n    `aws_lambda_powertools.metrics.metric.single_metric` to create EMF metrics.**\n\n    Environment variables\n    ---------------------\n    POWERTOOLS_METRICS_NAMESPACE : str\n        metric namespace to be set for all metrics\n\n    Raises\n    ------\n    MetricUnitError\n        When metric metric isn't supported by CloudWatch\n    MetricValueError\n        When metric value isn't a number\n    UniqueNamespaceError\n        When an additional namespace is set\n    SchemaValidationError\n        When metric object fails EMF schema validation\n    \"\"\"\n\n    def __init__(self, metric_set: Dict[str, str] = None, dimension_set: Dict = None, namespace: str = None):\n        self.metric_set = metric_set if metric_set is not None else {}\n        self.dimension_set = dimension_set if dimension_set is not None else {}\n        self.namespace = os.getenv(\"POWERTOOLS_METRICS_NAMESPACE\") or namespace\n        self._metric_units = [unit.value for unit in MetricUnit]\n        self._metric_unit_options = list(MetricUnit.__members__)\n\n    def add_namespace(self, name: str):\n        \"\"\"Adds given metric namespace\n\n        Example\n        -------\n        **Add metric namespace**\n\n            metric.add_namespace(name=\"ServerlessAirline\")\n\n        Parameters\n        ----------\n        name : str\n            Metric namespace\n        \"\"\"\n        if self.namespace is not None:\n            raise UniqueNamespaceError(\n                f\"Namespace '{self.namespace}' already set - Only one namespace is allowed across metrics\"\n            )\n        logger.debug(f\"Adding metrics namespace: {name}\")\n        self.namespace = name\n\n    def add_metric(self, name: str, unit: MetricUnit, value: Union[float, int]):\n        \"\"\"Adds given metric\n\n        Example\n        -------\n        **Add given metric using MetricUnit enum**\n\n            metric.add_metric(name=\"BookingConfirmation\", unit=MetricUnit.Count, value=1)\n\n        **Add given metric using plain string as value unit**\n\n            metric.add_metric(name=\"BookingConfirmation\", unit=\"Count\", value=1)\n\n        Parameters\n        ----------\n        name : str\n            Metric name\n        unit : MetricUnit\n            `aws_lambda_powertools.helper.models.MetricUnit`\n        value : float\n            Metric value\n\n        Raises\n        ------\n        MetricUnitError\n            When metric unit is not supported by CloudWatch\n        \"\"\"\n        if not isinstance(value, numbers.Number):\n            raise MetricValueError(f\"{value} is not a valid number\")\n\n        unit = self.__extract_metric_unit_value(unit=unit)\n        metric = {\"Unit\": unit, \"Value\": float(value)}\n        logger.debug(f\"Adding metric: {name} with {metric}\")\n        self.metric_set[name] = metric\n\n        if len(self.metric_set) == MAX_METRICS:\n            logger.debug(f\"Exceeded maximum of {MAX_METRICS} metrics - Publishing existing metric set\")\n            metrics = self.serialize_metric_set()\n            print(json.dumps(metrics))\n\n            # clear metric set only as opposed to metrics and dimensions set\n            # since we could have more than 100 metrics\n            self.metric_set.clear()\n\n    def serialize_metric_set(self, metrics: Dict = None, dimensions: Dict = None) -> Dict:\n        \"\"\"Serializes metric and dimensions set\n\n        Parameters\n        ----------\n        metrics : Dict, optional\n            Dictionary of metrics to serialize, by default None\n        dimensions : Dict, optional\n            Dictionary of dimensions to serialize, by default None\n\n        Example\n        -------\n        **Serialize metrics into EMF format**\n\n            metrics = MetricManager()\n            # ...add metrics, dimensions, namespace\n            ret = metrics.serialize_metric_set()\n\n        Returns\n        -------\n        Dict\n            Serialized metrics following EMF specification\n\n        Raises\n        ------\n        SchemaValidationError\n            Raised when serialization fail schema validation\n        \"\"\"\n        if metrics is None:  # pragma: no cover\n            metrics = self.metric_set\n\n        if dimensions is None:  # pragma: no cover\n            dimensions = self.dimension_set\n\n        logger.debug(\"Serializing...\", {\"metrics\": metrics, \"dimensions\": dimensions})\n\n        dimension_keys: List[str] = list(dimensions.keys())\n        metric_names_unit: List[Dict[str, str]] = []\n        metric_set: Dict[str, str] = {}\n\n        for metric_name in metrics:\n            metric: str = metrics[metric_name]\n            metric_value: int = metric.get(\"Value\", 0)\n            metric_unit: str = metric.get(\"Unit\", \"\")\n\n            metric_names_unit.append({\"Name\": metric_name, \"Unit\": metric_unit})\n            metric_set.update({metric_name: metric_value})\n\n        metrics_definition = {\n            \"CloudWatchMetrics\": [\n                {\"Namespace\": self.namespace, \"Dimensions\": [dimension_keys], \"Metrics\": metric_names_unit}\n            ]\n        }\n        metrics_timestamp = {\"Timestamp\": int(datetime.datetime.now().timestamp() * 1000)}\n        metric_set[\"_aws\"] = {**metrics_timestamp, **metrics_definition}\n        metric_set.update(**dimensions)\n\n        try:\n            logger.debug(\"Validating serialized metrics against CloudWatch EMF schema\", metric_set)\n            fastjsonschema.validate(definition=CLOUDWATCH_EMF_SCHEMA, data=metric_set)\n        except fastjsonschema.JsonSchemaException as e:\n            message = f\"Invalid format. Error: {e.message}, Invalid item: {e.name}\"  # noqa: B306, E501\n            raise SchemaValidationError(message)\n        return metric_set\n\n    def add_dimension(self, name: str, value: str):\n        \"\"\"Adds given dimension to all metrics\n\n        Example\n        -------\n        **Add a metric dimensions**\n\n            metric.add_dimension(name=\"operation\", value=\"confirm_booking\")\n\n        Parameters\n        ----------\n        name : str\n            Dimension name\n        value : str\n            Dimension value\n        \"\"\"\n        logger.debug(f\"Adding dimension: {name}:{value}\")\n        self.dimension_set[name] = value\n\n    def __extract_metric_unit_value(self, unit: Union[str, MetricUnit]) -> str:\n        \"\"\"Return metric value from metric unit whether that's str or MetricUnit enum\n\n        Parameters\n        ----------\n        unit : Union[str, MetricUnit]\n            Metric unit\n\n        Returns\n        -------\n        str\n            Metric unit value (e.g. \"Seconds\", \"Count/Second\")\n\n        Raises\n        ------\n        MetricUnitError\n            When metric unit is not supported by CloudWatch\n        \"\"\"\n\n        if isinstance(unit, str):\n            if unit in self._metric_unit_options:\n                unit = MetricUnit[unit].value\n\n            if unit not in self._metric_units:  # str correta\n                raise MetricUnitError(\n                    f\"Invalid metric unit '{unit}', expected either option: {self._metric_unit_options}\"\n                )\n\n        if isinstance(unit, MetricUnit):\n            unit = unit.value\n\n        return unit\n", "path": "aws_lambda_powertools/metrics/base.py"}, {"content": "import functools\nimport json\nimport logging\nfrom typing import Any, Callable\n\nfrom aws_lambda_powertools.metrics.base import MetricManager\n\nlogger = logging.getLogger(__name__)\n\n\nclass Metrics(MetricManager):\n    \"\"\"Metrics create an EMF object with up to 100 metrics\n\n    Use Metrics when you need to create multiple metrics that have\n    dimensions in common (e.g. service_name=\"payment\").\n\n    Metrics up to 100 metrics in memory and are shared across\n    all its instances. That means it can be safely instantiated outside\n    of a Lambda function, or anywhere else.\n\n    A decorator (log_metrics) is provided so metrics are published at the end of its execution.\n    If more than 100 metrics are added at a given function execution,\n    these metrics are serialized and published before adding a given metric\n    to prevent metric truncation.\n\n    Example\n    -------\n    **Creates a few metrics and publish at the end of a function execution**\n\n        from aws_lambda_powertools.metrics import Metrics\n\n        metrics = Metrics()\n        metrics.add_namespace(name=\"ServerlessAirline\")\n        metrics.add_metric(name=\"ColdStart\", unit=MetricUnit.Count, value=1)\n        metrics.add_metric(name=\"BookingConfirmation\", unit=\"Count\", value=1)\n        metrics.add_dimension(name=\"service\", value=\"booking\")\n        metrics.add_dimension(name=\"function_version\", value=\"$LATEST\")\n        ...\n\n        @tracer.capture_lambda_handler\n        @metrics.log_metrics()\n        def lambda_handler():\n                do_something()\n                return True\n\n        def do_something():\n                metrics.add_metric(name=\"Something\", unit=\"Count\", value=1)\n\n    Environment variables\n    ---------------------\n    POWERTOOLS_METRICS_NAMESPACE : str\n        metric namespace\n\n    Parameters\n    ----------\n    MetricManager : MetricManager\n        Inherits from `aws_lambda_powertools.metrics.base.MetricManager`\n\n    Raises\n    ------\n    e\n        Propagate error received\n    \"\"\"\n\n    _metrics = {}\n    _dimensions = {}\n\n    def __init__(self):\n        self.metric_set = self._metrics\n        self.dimension_set = self._dimensions\n        super().__init__(metric_set=self.metric_set, dimension_set=self.dimension_set)\n\n    def clear_metrics(self):\n        logger.debug(\"Clearing out existing metric set from memory\")\n        self.metric_set.clear()\n        self.dimension_set.clear()\n\n    def log_metrics(self, lambda_handler: Callable[[Any, Any], Any] = None):\n        \"\"\"Decorator to serialize and publish metrics at the end of a function execution.\n\n        Be aware that the log_metrics **does call* the decorated function (e.g. lambda_handler).\n\n        Example\n        -------\n        **Lambda function using tracer and metrics decorators**\n\n            metrics = Metrics()\n            tracer = Tracer(service=\"payment\")\n\n            @tracer.capture_lambda_handler\n            @metrics.log_metrics\n                def handler(event, context)\n\n        Parameters\n        ----------\n        lambda_handler : Callable[[Any, Any], Any], optional\n            Lambda function handler, by default None\n\n        Raises\n        ------\n        e\n            Propagate error received\n        \"\"\"\n\n        @functools.wraps(lambda_handler)\n        def decorate(*args, **kwargs):\n            try:\n                response = lambda_handler(*args, **kwargs)\n            finally:\n                metrics = self.serialize_metric_set()\n                self.clear_metrics()\n                logger.debug(\"Publishing metrics\", {\"metrics\": metrics})\n                print(json.dumps(metrics))\n\n            return response\n\n        return decorate\n", "path": "aws_lambda_powertools/metrics/metrics.py"}]}