Skip to content

Commit dbdfb9e

Browse files
authored
Add LocalEnvironment (#61)
So far the DefaultEnvironment was hard coded to use the agent sink as that was the recommendation for workloads on-premise. While a fair recommendation, it isn't accurate for all workloads. This adds a similer local environment that uses the StdoutSink instead of the agent sink for such customers.
1 parent 5aa2d87 commit dbdfb9e

File tree

8 files changed

+175
-13
lines changed

8 files changed

+175
-13
lines changed

aws_embedded_metrics/environment/default_environment.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from aws_embedded_metrics.config import get_config
1515
from aws_embedded_metrics.environment import Environment
1616
from aws_embedded_metrics.logger.metrics_context import MetricsContext
17+
from aws_embedded_metrics.sinks import Sink
1718
from aws_embedded_metrics.sinks.agent_sink import AgentSink
1819
from typing import Optional
1920

@@ -22,7 +23,7 @@
2223

2324
class DefaultEnvironment(Environment):
2425
def __init__(self) -> None:
25-
self.sink: Optional[AgentSink] = None
26+
self.sink: Optional[Sink] = None
2627

2728
async def probe(self) -> bool:
2829
return True
@@ -39,7 +40,7 @@ def get_log_group_name(self) -> str:
3940
def configure_context(self, context: MetricsContext) -> None:
4041
pass
4142

42-
def get_sink(self) -> AgentSink:
43+
def get_sink(self) -> Sink:
4344
if self.sink is None:
4445
self.sink = AgentSink(self.get_log_group_name(), Config.log_stream_name)
4546
return self.sink

aws_embedded_metrics/environment/environment_detector.py

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from aws_embedded_metrics.environment import Environment
1717
from aws_embedded_metrics.environment.default_environment import DefaultEnvironment
1818
from aws_embedded_metrics.environment.lambda_environment import LambdaEnvironment
19+
from aws_embedded_metrics.environment.local_environment import LocalEnvironment
1920
from aws_embedded_metrics.environment.ec2_environment import EC2Environment
2021
from typing import Optional
2122

@@ -24,6 +25,7 @@
2425
lambda_environment = LambdaEnvironment()
2526
ec2_environment = EC2Environment()
2627
default_environment = DefaultEnvironment()
28+
local_environment = LocalEnvironment()
2729
environments = [lambda_environment, ec2_environment]
2830
Config = config.get_config()
2931

@@ -45,6 +47,8 @@ async def resolve_environment() -> Environment:
4547
EnvironmentCache.environment = ec2_environment
4648
elif lower_configured_enviroment == "default":
4749
EnvironmentCache.environment = default_environment
50+
elif lower_configured_enviroment == "local":
51+
EnvironmentCache.environment = local_environment
4852
else:
4953
log.info("Failed to understand environment override: %s", Config.environment)
5054
if EnvironmentCache.environment is not None:

aws_embedded_metrics/environment/lambda_environment.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from aws_embedded_metrics.environment import Environment
1515
from aws_embedded_metrics.logger.metrics_context import MetricsContext
1616
from aws_embedded_metrics.sinks import Sink
17-
from aws_embedded_metrics.sinks.lambda_sink import LambdaSink
17+
from aws_embedded_metrics.sinks.stdout_sink import StdoutSink
1818
import os
1919

2020

@@ -24,7 +24,7 @@ def get_env(key: str) -> str:
2424
return ""
2525

2626

27-
sink = LambdaSink()
27+
sink = StdoutSink()
2828

2929

3030
class LambdaEnvironment(Environment):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright 2019 Amazon.com, Inc. or its affiliates.
2+
# Licensed under the Apache License, Version 2.0 (the
3+
# "License"); you may not use this file except in compliance
4+
# with the License. You may obtain a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS,
10+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
# See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
14+
from aws_embedded_metrics.config import get_config
15+
from aws_embedded_metrics.environment import Environment
16+
from aws_embedded_metrics.logger.metrics_context import MetricsContext
17+
from aws_embedded_metrics.sinks import Sink
18+
from aws_embedded_metrics.sinks.stdout_sink import StdoutSink
19+
from typing import Optional
20+
21+
Config = get_config()
22+
23+
24+
class LocalEnvironment(Environment):
25+
def __init__(self) -> None:
26+
self.sink: Optional[Sink] = None
27+
28+
async def probe(self) -> bool:
29+
return False
30+
31+
def get_name(self) -> str:
32+
return Config.service_name or "Unknown"
33+
34+
def get_type(self) -> str:
35+
return Config.service_type or "Unknown"
36+
37+
def get_log_group_name(self) -> str:
38+
return Config.log_group_name or f"{self.get_name()}-metrics"
39+
40+
def configure_context(self, context: MetricsContext) -> None:
41+
pass
42+
43+
def get_sink(self) -> Sink:
44+
if self.sink is None:
45+
self.sink = StdoutSink()
46+
return self.sink

aws_embedded_metrics/sinks/lambda_sink.py renamed to aws_embedded_metrics/sinks/stdout_sink.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@
1717
from aws_embedded_metrics.serializers.log_serializer import LogSerializer
1818

1919

20-
class LambdaSink(Sink):
20+
class StdoutSink(Sink):
2121
def __init__(self, serializer: Serializer = LogSerializer()):
2222
self.serializer = serializer
2323

2424
def accept(self, context: MetricsContext) -> None:
2525
for serialized_content in self.serializer.serialize(context):
26-
print(serialized_content)
26+
if serialized_content:
27+
print(serialized_content)
2728

2829
@staticmethod
2930
def name() -> str:
30-
return "LambdaSink"
31+
return "StdoutSink"

tests/environment/test_lambda_environment.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import os
22
from aws_embedded_metrics.environment.lambda_environment import LambdaEnvironment
3-
from aws_embedded_metrics.sinks.lambda_sink import LambdaSink
3+
from aws_embedded_metrics.sinks.stdout_sink import StdoutSink
44
import pytest
55
from faker import Faker
66

@@ -65,4 +65,4 @@ def test_create_sink_creates_LambdaSink():
6565
result = env.get_sink()
6666

6767
# assert
68-
assert isinstance(result, LambdaSink)
68+
assert isinstance(result, StdoutSink)
+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
from aws_embedded_metrics import config
2+
from aws_embedded_metrics.environment.local_environment import LocalEnvironment
3+
from aws_embedded_metrics.sinks.stdout_sink import StdoutSink
4+
import pytest
5+
from faker import Faker
6+
7+
Config = config.get_config()
8+
fake = Faker()
9+
10+
11+
@pytest.mark.asyncio
12+
async def test_probe_always_returns_false():
13+
# arrange
14+
env = LocalEnvironment()
15+
16+
# act
17+
result = await env.probe()
18+
19+
# assert
20+
assert result is False
21+
22+
23+
def test_get_name_returns_unknown_if_not_specified():
24+
# arrange
25+
env = LocalEnvironment()
26+
Config.service_name = None
27+
28+
# act
29+
result = env.get_name()
30+
31+
# assert
32+
assert result == "Unknown"
33+
34+
35+
def test_get_type_returns_unknown_if_not_specified():
36+
# arrange
37+
env = LocalEnvironment()
38+
Config.service_type = None
39+
40+
# act
41+
result = env.get_type()
42+
43+
# assert
44+
assert result == "Unknown"
45+
46+
47+
def test_get_name_returns_name_if_configured():
48+
# arrange
49+
expected_name = fake.word()
50+
env = LocalEnvironment()
51+
Config.service_name = expected_name
52+
53+
# act
54+
result = env.get_name()
55+
56+
# assert
57+
assert result == expected_name
58+
59+
60+
def test__get_type__returns_type_if_configured():
61+
# arrange
62+
expected_type = fake.word()
63+
env = LocalEnvironment()
64+
Config.service_type = expected_type
65+
66+
# act
67+
result = env.get_type()
68+
69+
# assert
70+
assert result == expected_type
71+
72+
73+
def test__get_log_group_name__returns_log_group_if_configured():
74+
# arrange
75+
expected = fake.word()
76+
env = LocalEnvironment()
77+
Config.log_group_name = expected
78+
79+
# act
80+
result = env.get_log_group_name()
81+
82+
# assert
83+
assert result == expected
84+
85+
86+
def test__get_log_group_name__returns_service_name_metrics_if_not_configured():
87+
# arrange
88+
expected = fake.word()
89+
env = LocalEnvironment()
90+
Config.service_name = expected
91+
Config.log_group_name = None
92+
93+
# act
94+
result = env.get_log_group_name()
95+
96+
# assert
97+
assert result == f"{expected}-metrics"
98+
99+
100+
def test__get_sink__returns_stdout_sink():
101+
# arrange
102+
env = LocalEnvironment()
103+
104+
# act
105+
result = env.get_sink()
106+
107+
# assert
108+
assert isinstance(result, StdoutSink)

tests/sinks/test_lambda_sink.py renamed to tests/sinks/test_stdout_sink.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from importlib import reload
22

33
from aws_embedded_metrics import config
4-
from aws_embedded_metrics.sinks.lambda_sink import LambdaSink
4+
from aws_embedded_metrics.sinks.stdout_sink import StdoutSink
55
from aws_embedded_metrics.logger.metrics_context import MetricsContext
66
from faker import Faker
77
from unittest.mock import patch
@@ -14,9 +14,10 @@ def test_accept_writes_to_stdout(capfd):
1414
# arrange
1515
reload(config)
1616

17-
sink = LambdaSink()
17+
sink = StdoutSink()
1818
context = MetricsContext.empty()
1919
context.meta["Timestamp"] = 1
20+
context.put_metric("Dummy", 1)
2021

2122
# act
2223
sink.accept(context)
@@ -25,7 +26,8 @@ def test_accept_writes_to_stdout(capfd):
2526
out, err = capfd.readouterr()
2627
assert (
2728
out
28-
== '{"_aws": {"Timestamp": 1, "CloudWatchMetrics": [{"Dimensions": [], "Metrics": [], "Namespace": "aws-embedded-metrics"}]}}\n'
29+
== '{"_aws": {"Timestamp": 1, "CloudWatchMetrics": [{"Dimensions": [], "Metrics": [{"Name": "Dummy", "Unit": "None"}], '
30+
'"Namespace": "aws-embedded-metrics"}]}, "Dummy": 1}\n'
2931
)
3032

3133

@@ -34,7 +36,7 @@ def test_accept_writes_multiple_messages_to_stdout(mock_serializer, capfd):
3436
# arrange
3537
expected_messages = [fake.word() for _ in range(10)]
3638
mock_serializer.serialize.return_value = expected_messages
37-
sink = LambdaSink(serializer=mock_serializer)
39+
sink = StdoutSink(serializer=mock_serializer)
3840
context = MetricsContext.empty()
3941
context.meta["Timestamp"] = 1
4042

0 commit comments

Comments
 (0)