Skip to content

Support 30 dimensions per dimension set #80

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion aws_embedded_metrics/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
# limitations under the License.

DEFAULT_NAMESPACE = "aws-embedded-metrics"
MAX_DIMENSIONS = 9
MAX_DIMENSION_SET_SIZE = 30
MAX_METRICS_PER_EVENT = 100
MAX_DATAPOINTS_PER_METRIC = 100
17 changes: 17 additions & 0 deletions aws_embedded_metrics/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2022 Amazon.com, Inc. or its affiliates.
# Licensed under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

class DimensionSetExceededError(Exception):
def __init__(self, message: str) -> None:
# Call the base class constructor with the parameters it needs
super().__init__(message)
17 changes: 17 additions & 0 deletions aws_embedded_metrics/logger/metrics_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

from aws_embedded_metrics import constants, utils
from aws_embedded_metrics.config import get_config
from aws_embedded_metrics.constants import MAX_DIMENSION_SET_SIZE
from aws_embedded_metrics.exceptions import DimensionSetExceededError
from aws_embedded_metrics.logger.metric import Metric
from typing import List, Dict, Any

Expand Down Expand Up @@ -55,6 +57,15 @@ def put_metric(self, key: str, value: float, unit: str = None) -> None:
else:
self.metrics[key] = Metric(value, unit)

@staticmethod
def validate_dimension_set(dimensions: Dict[str, str]) -> None:
"""
Validates dimension set length is not more than MAX_DIMENSION_SET_SIZE
"""
if len(dimensions) > MAX_DIMENSION_SET_SIZE:
raise DimensionSetExceededError(
f"Maximum number of dimensions per dimension set allowed are {MAX_DIMENSION_SET_SIZE}")

def put_dimensions(self, dimensions: Dict[str, str]) -> None:
"""
Adds dimensions to the context.
Expand All @@ -66,6 +77,8 @@ def put_dimensions(self, dimensions: Dict[str, str]) -> None:
# TODO add ability to define failure strategy
return

self.validate_dimension_set(dimensions)

self.dimensions.append(dimensions)

def set_dimensions(self, dimensionSets: List[Dict[str, str]]) -> None:
Expand All @@ -78,6 +91,10 @@ def set_dimensions(self, dimensionSets: List[Dict[str, str]]) -> None:
```
"""
self.should_use_default_dimensions = False

for dimensionSet in dimensionSets:
self.validate_dimension_set(dimensionSet)

self.dimensions = dimensionSets

def set_default_dimensions(self, default_dimensions: Dict) -> None:
Expand Down
9 changes: 7 additions & 2 deletions aws_embedded_metrics/serializers/log_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
from aws_embedded_metrics.logger.metrics_context import MetricsContext
from aws_embedded_metrics.serializers import Serializer
from aws_embedded_metrics.constants import (
MAX_DIMENSIONS, MAX_METRICS_PER_EVENT, MAX_DATAPOINTS_PER_METRIC
MAX_DIMENSION_SET_SIZE, MAX_METRICS_PER_EVENT, MAX_DATAPOINTS_PER_METRIC
)
from aws_embedded_metrics.exceptions import DimensionSetExceededError
import json
from typing import Any, Dict, List

Expand All @@ -31,7 +32,11 @@ def serialize(context: MetricsContext) -> List[str]:

for dimension_set in context.get_dimensions():
keys = list(dimension_set.keys())
dimension_keys.append(keys[0:MAX_DIMENSIONS])
if len(keys) > MAX_DIMENSION_SET_SIZE:
err_msg = (f"Maximum number of dimensions per dimension set allowed are {MAX_DIMENSION_SET_SIZE}. "
f"Account for default dimensions if not using set_dimensions.")
raise DimensionSetExceededError(err_msg)
dimension_keys.append(keys)
dimensions_properties = {**dimensions_properties, **dimension_set}

def create_body() -> Dict[str, Any]:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name="aws-embedded-metrics",
version="1.0.8",
version="2.0.0",
author="Amazon Web Services",
author_email="jarnance@amazon.com",
description="AWS Embedded Metrics Package",
Expand Down
39 changes: 35 additions & 4 deletions tests/logger/test_metrics_context.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from aws_embedded_metrics import config
from aws_embedded_metrics.logger.metrics_context import MetricsContext
from aws_embedded_metrics.constants import DEFAULT_NAMESPACE
from aws_embedded_metrics.exceptions import DimensionSetExceededError
from importlib import reload
from faker import Faker
import pytest

fake = Faker()

Expand Down Expand Up @@ -42,14 +44,14 @@ def test_put_dimension_adds_to_dimensions():
# arrange
context = MetricsContext()

dimension_key = fake.word()
dimension_value = fake.word()
dimensions_to_add = 30
dimension_set = generate_dimension_set(dimensions_to_add)

# act
context.put_dimensions({dimension_key: dimension_value})
context.put_dimensions(dimension_set)

# assert
assert context.dimensions == [{dimension_key: dimension_value}]
assert context.dimensions == [dimension_set]


def test_get_dimensions_returns_only_custom_dimensions_if_no_default_dimensions_not_set():
Expand Down Expand Up @@ -264,3 +266,32 @@ def test_create_copy_with_context_does_not_repeat_dimensions():

# assert
assert len(new_context.get_dimensions()) == 1


def test_cannot_set_more_than_30_dimensions():
context = MetricsContext()
dimensions_to_add = 32
dimension_set = generate_dimension_set(dimensions_to_add)

with pytest.raises(DimensionSetExceededError):
context.set_dimensions([dimension_set])


def test_cannot_put_more_than_30_dimensions():
context = MetricsContext()
dimensions_to_add = 32
dimension_set = generate_dimension_set(dimensions_to_add)

with pytest.raises(DimensionSetExceededError):
context.put_dimensions(dimension_set)


# Test utility method


def generate_dimension_set(dimensions_to_add):
dimension_set = {}
for i in range(0, dimensions_to_add):
dimension_set[f"{i}"] = fake.word()

return dimension_set
52 changes: 21 additions & 31 deletions tests/serializer/test_log_serializer.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from aws_embedded_metrics.config import get_config
from aws_embedded_metrics.exceptions import DimensionSetExceededError
from aws_embedded_metrics.logger.metrics_context import MetricsContext
from aws_embedded_metrics.serializers.log_serializer import LogSerializer
from collections import Counter
from faker import Faker
import json
import pytest

fake = Faker()

Expand All @@ -30,37 +32,6 @@ def test_serialize_dimensions():
assert_json_equality(result_json, expected)


def test_cannot_serialize_more_than_9_dimensions():
# arrange
dimensions = {}
dimension_pointers = []
allowed_dimensions = 9
dimensions_to_add = 15

for i in range(0, dimensions_to_add):
print(i)
expected_key = f"{i}"
expected_value = fake.word()
dimensions[expected_key] = expected_value
dimension_pointers.append(expected_key)

expected_dimensions_pointers = dimension_pointers[0:allowed_dimensions]

expected = {**get_empty_payload(), **dimensions}
expected["_aws"]["CloudWatchMetrics"][0]["Dimensions"].append(
expected_dimensions_pointers
)

context = get_context()
context.put_dimensions(dimensions)

# act
result_json = serializer.serialize(context)[0]

# assert
assert_json_equality(result_json, expected)


def test_serialize_properties():
# arrange
expected_key = fake.word()
Expand All @@ -79,6 +50,25 @@ def test_serialize_properties():
assert_json_equality(result_json, expected)


def test_default_and_custom_dimensions_combined_limit_exceeded():
# While serializing default dimensions are added to the custom dimension set,
# and the combined size of the dimension set should not be more than 30
dimensions = {}
default_dimension_key = fake.word()
default_dimension_value = fake.word()
custom_dimensions_to_add = 30

for i in range(0, custom_dimensions_to_add):
dimensions[f"{i}"] = fake.word()

context = get_context()
context.set_default_dimensions({default_dimension_key: default_dimension_value})
context.put_dimensions(dimensions)

with pytest.raises(DimensionSetExceededError):
serializer.serialize(context)


def test_serialize_metrics():
# arrange
expected_key = fake.word()
Expand Down