Skip to content

Commit dc09011

Browse files
committed
feat: store integration test metrics in metrics.json
For each test store metrics it emits into a metrics.json file. This makes it easier to look at the metrics. Signed-off-by: Egor Lazarchuk <yegorlz@amazon.co.uk>
1 parent 75ae954 commit dc09011

File tree

2 files changed

+50
-29
lines changed

2 files changed

+50
-29
lines changed

tests/conftest.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ def pytest_runtest_logreport(report):
172172

173173

174174
@pytest.fixture()
175-
def metrics(request):
175+
def metrics(results_dir, request):
176176
"""Fixture to pass the metrics scope
177177
178178
We use a fixture instead of the @metrics_scope decorator as that conflicts
@@ -188,6 +188,8 @@ def metrics(request):
188188
metrics_logger.set_property(prop_name, prop_val)
189189
yield metrics_logger
190190
metrics_logger.flush()
191+
if results_dir:
192+
metrics_logger.store_data(results_dir)
191193

192194

193195
@pytest.fixture
@@ -391,17 +393,29 @@ def results_dir(request):
391393
"""
392394
Fixture yielding the path to a directory into which the test can dump its results
393395
394-
Directories are unique per test, and named after the test name. Everything the tests puts
395-
into its directory will to be uploaded to S3. Directory will be placed inside defs.TEST_RESULTS_DIR.
396+
Directories are unique per test, and their names include test name and test parameters.
397+
Everything the tests puts into its directory will to be uploaded to S3.
398+
Directory will be placed inside defs.TEST_RESULTS_DIR.
396399
397400
For example
398401
```py
399-
def test_my_file(results_dir):
402+
@pytest.mark.parametrize("p", ["a", "b"])
403+
def test_my_file(p, results_dir):
400404
(results_dir / "output.txt").write_text("Hello World")
401405
```
402-
will result in `defs.TEST_RESULTS_DIR`/test_my_file/output.txt.
406+
will result in:
407+
- `defs.TEST_RESULTS_DIR`/test_my_file/test_my_file[a]/output.txt.
408+
- `defs.TEST_RESULTS_DIR`/test_my_file/test_my_file[b]/output.txt.
409+
410+
When this fixture is called with DoctestItem as a request.node
411+
during doc tests, it will return None.
403412
"""
404-
results_dir = defs.TEST_RESULTS_DIR / request.node.originalname
413+
try:
414+
results_dir = (
415+
defs.TEST_RESULTS_DIR / request.node.originalname / request.node.name
416+
)
417+
except AttributeError:
418+
return None
405419
results_dir.mkdir(parents=True, exist_ok=True)
406420
return results_dir
407421

tests/host_tools/metrics.py

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -46,52 +46,59 @@
4646
import json
4747
import os
4848
import socket
49+
from pathlib import Path
4950
from urllib.parse import urlparse
5051

5152
from aws_embedded_metrics.constants import DEFAULT_NAMESPACE
5253
from aws_embedded_metrics.logger.metrics_logger_factory import create_metrics_logger
5354

5455

55-
class MetricsWrapperDummy:
56-
"""Send metrics to /dev/null"""
56+
class MetricsWrapper:
57+
"""A convenient metrics logger"""
58+
59+
def __init__(self, logger):
60+
self.data = {}
61+
self.logger = logger
5762

5863
def set_dimensions(self, *args, **kwargs):
5964
"""Set dimensions"""
65+
if self.logger:
66+
self.logger.set_dimensions(*args, **kwargs)
6067

61-
def put_metric(self, *args, **kwargs):
68+
def put_metric(self, name, data, unit):
6269
"""Put a datapoint with given dimensions"""
70+
if name not in self.data:
71+
self.data[name] = []
72+
self.data[name].append(data)
73+
74+
if self.logger:
75+
self.logger.put_metric(name, data, unit)
6376

6477
def set_property(self, *args, **kwargs):
6578
"""Set a property"""
79+
if self.logger:
80+
self.logger.set_property(*args, **kwargs)
6681

6782
def flush(self):
6883
"""Flush any remaining metrics"""
84+
if self.logger:
85+
asyncio.run(self.logger.flush())
6986

70-
71-
class MetricsWrapper:
72-
"""A convenient metrics logger"""
73-
74-
def __init__(self, logger):
75-
self.logger = logger
76-
77-
def __getattr__(self, attr):
78-
"""Dispatch methods to logger instance"""
79-
if attr not in self.__dict__:
80-
return getattr(self.logger, attr)
81-
return getattr(self, attr)
82-
83-
def flush(self):
84-
"""Flush any remaining metrics"""
85-
asyncio.run(self.logger.flush())
87+
def store_data(self, dir_path):
88+
"""Store data into a file"""
89+
metrics_path = Path(dir_path / "metrics.json")
90+
with open(metrics_path, "w", encoding="utf-8") as f:
91+
json.dump(self.data, f)
8692

8793

8894
def get_metrics_logger():
8995
"""Get a new metrics logger object"""
9096
# if no metrics namespace, don't output metrics
91-
if "AWS_EMF_NAMESPACE" not in os.environ:
92-
return MetricsWrapperDummy()
93-
logger = create_metrics_logger()
94-
logger.reset_dimensions(False)
97+
if "AWS_EMF_NAMESPACE" in os.environ:
98+
logger = create_metrics_logger()
99+
logger.reset_dimensions(False)
100+
else:
101+
logger = None
95102
return MetricsWrapper(logger)
96103

97104

0 commit comments

Comments
 (0)