Skip to content

Commit 40690e6

Browse files
authored
Merge pull request #183 from getappmap/sw/dont_show_warning_when_appmap_is_enabled
feat: downgrade logging level when AppMap recording is enabled
2 parents 031cabc + f763eb6 commit 40690e6

File tree

11 files changed

+102
-30
lines changed

11 files changed

+102
-30
lines changed

appmap/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
"""AppMap recorder for Python"""
22
from ._implementation import generation # noqa: F401
33
from ._implementation.env import Env # noqa: F401
4-
from ._implementation.labels import labels # noqa: F401
54
from ._implementation.importer import instrument_module # noqa: F401
5+
from ._implementation.labels import labels # noqa: F401
66
from ._implementation.recording import Recording # noqa: F401
77

88
try:

appmap/_implementation/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
from . import configuration
22
from . import env as appmapenv
33
from . import event, importer, metadata, recorder
4-
from .detect_enabled import DetectEnabled
54
from .py_version_check import check_py_version
65

76

87
def initialize(**kwargs):
98
check_py_version()
109
appmapenv.initialize(**kwargs)
11-
DetectEnabled.initialize()
1210
event.initialize()
1311
importer.initialize()
1412
recorder.initialize()

appmap/_implementation/configuration.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ def __init__(self, *args, **kwargs):
383383

384384

385385
def initialize():
386+
DetectEnabled.initialize()
386387
Config().initialize()
387388
Importer.use_filter(BuiltinFilter)
388389
Importer.use_filter(ConfigFilter)

appmap/_implementation/detect_enabled.py

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
# environment variables.
2020
class DetectEnabled:
2121
_instance = None
22+
_initialized = False
23+
_logged_at_least_once = False
2224

2325
def __new__(cls):
2426
if cls._instance is None:
@@ -58,60 +60,88 @@ def should_enable(cls, recording_method):
5860
if recording_method in cls._detected_for_method:
5961
return cls._detected_for_method[recording_method]
6062
else:
61-
message, enabled = cls._detect_should_enable(recording_method)
63+
enabled, log_level, message = cls._detect_should_enable(recording_method)
6264
cls._detected_for_method[recording_method] = enabled
63-
if enabled:
64-
logger.warning(dedent(f"AppMap recording is enabled because {message}"))
65+
# don't log enabled messages more than once
66+
if (not cls._logged_at_least_once and logger.isEnabledFor(log_level)):
67+
cls._logged_at_least_once = True
68+
logger.log(log_level, message)
6569
return enabled
6670

6771
@classmethod
6872
def any_enabled(cls):
6973
for m in RECORDING_METHODS:
70-
_, enabled = cls._detect_should_enable(m)
71-
if enabled:
74+
if cls.should_enable(m):
7275
return True
7376
return False
7477

78+
@classmethod
79+
def _log_prefix(cls, should_enable, log_message):
80+
enabled_prefix = ""
81+
if not should_enable:
82+
enabled_prefix = "not "
83+
84+
return dedent(
85+
f"AppMap recording is {enabled_prefix}enabled because {log_message}."
86+
)
87+
7588
@classmethod
7689
def _detect_should_enable(cls, recording_method):
7790
if not recording_method:
78-
return ["no recording method is set", False]
91+
return False, logging.WARNING, cls._log_prefix(False, "no recording method is set")
7992

8093
if recording_method not in RECORDING_METHODS:
81-
return ["invalid recording method", False]
94+
return False, logging.WARNING, cls._log_prefix(
95+
False, f"{recording_method} is an invalid recording method"
96+
)
8297

8398
# explicitly disabled or enabled
8499
if "APPMAP" in os.environ:
85-
if os.environ["APPMAP"] == "false":
86-
return ["APPMAP=false", False]
87-
elif os.environ["APPMAP"] == "true":
88-
return ["APPMAP=true", True]
100+
if os.environ["APPMAP"].lower() == "false":
101+
return False, logging.INFO, cls._log_prefix(False, f"APPMAP=false")
102+
elif os.environ["APPMAP"].lower() == "true":
103+
return True, logging.INFO, cls._log_prefix(True, f"APPMAP=true")
104+
else:
105+
return False, logging.WARNING, cls._log_prefix(False, f"APPMAP={os.environ['APPMAP']} is an invalid option")
89106

90107
# recording method explicitly disabled or enabled
91108
if recording_method:
92109
for one_recording_method in RECORDING_METHODS:
93110
if one_recording_method == recording_method.lower():
94111
env_var = "_".join(["APPMAP", "RECORD", recording_method.upper()])
95112
if env_var in os.environ:
96-
if os.environ[env_var] == "false":
97-
return [f"{env_var}=false", False]
98-
elif os.environ[env_var] == "true":
99-
return [f"{env_var}=true", True]
113+
if os.environ[env_var].lower() == "false":
114+
return False, logging.INFO, cls._log_prefix(False, f"{env_var}=false")
115+
elif os.environ[env_var].lower() == "true":
116+
return True, logging.INFO, cls._log_prefix(True, f"{env_var}=true")
117+
else:
118+
return False, logging.WARNING, cls._log_prefix(False, f"{env_var}={os.environ[env_var]} is an invalid option")
119+
120+
# check if name of APPMAP_RECORD_ env variable was defined incorrectly
121+
for env_var in os.environ:
122+
env_var_as_list = env_var.split("_")
123+
if (
124+
len(env_var_as_list) > 2
125+
and env_var_as_list[0] == "APPMAP"
126+
and env_var_as_list[1] == "RECORD"
127+
):
128+
if not (env_var_as_list[2].lower() in RECORDING_METHODS):
129+
return False, logging.WARNING, cls._log_prefix(False, f"{env_var} is an invalid recording method")
100130

101131
# it's flask
102132
message, should_enable = cls.is_flask_and_should_enable()
103-
if should_enable == True or should_enable == False:
104-
return [message, should_enable]
133+
if should_enable in [True, False]:
134+
return should_enable, logging.INFO, cls._log_prefix(should_enable, f"{message}")
105135

106136
# it's django
107137
message, should_enable = cls.is_django_and_should_enable()
108-
if should_enable == True or should_enable == False:
109-
return [message, should_enable]
138+
if should_enable in [True, False]:
139+
return should_enable, logging.INFO, cls._log_prefix(should_enable, f"{message}")
110140

111141
if recording_method in RECORDING_METHODS:
112-
return ["will record by default", True]
142+
return True, logging.INFO, cls._log_prefix(True, f"will record by default")
113143

114-
return ["it's not enabled by any configuration or framework", False]
144+
return False, logging.INFO, cls._log_prefix(False, f"it's not enabled by any configuration or framework")
115145

116146
@classmethod
117147
def is_flask_and_should_enable(cls):

appmap/_implementation/env.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ def __init__(self, env=None, cwd=None):
3636
self._env = _bootenv.copy()
3737
if env:
3838
self._env.update(env)
39+
40+
self._configure_logging()
3941
self._enabled = DetectEnabled.any_enabled()
4042

4143
self._root_dir = str(self._cwd) + "/"
@@ -44,8 +46,6 @@ def __init__(self, env=None, cwd=None):
4446
output_dir = Path(self.get("APPMAP_OUTPUT_DIR", "tmp/appmap"))
4547
self._output_dir = output_dir.resolve()
4648

47-
self._configure_logging()
48-
4949
def set(self, name, value):
5050
self._env[name] = value
5151

@@ -120,5 +120,6 @@ def _configure_logging(self):
120120

121121

122122
def initialize(**kwargs):
123+
DetectEnabled.initialize()
123124
Env.reset(**kwargs)
124125
logging.info("appmap enabled: %s", Env.current.enabled)

appmap/_implementation/testing_framework.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ def collect_result_metadata(metadata):
140140
metadata["exception"] = {"class": exn.__class__.__name__, "message": str(exn)}
141141
raise
142142

143+
143144
def file_delete(filename):
144145
try:
145146
os.remove(filename)

appmap/_implementation/web_framework.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ def __init__(self):
135135
self.record_url = "/_appmap/record"
136136

137137
def should_record(self):
138-
return DetectEnabled.should_enable("remote") or DetectEnabled.should_enable("requests")
138+
return DetectEnabled.should_enable("remote") or DetectEnabled.should_enable(
139+
"requests"
140+
)
139141

140142
def before_request_hook(self, request, request_path, recording_is_running):
141143
if request_path == self.record_url:

appmap/test/test_detect_enabled.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,23 @@ def setup_method():
2020
def test_none__appmap_disabled(self):
2121
assert DetectEnabled.should_enable(None) == False
2222

23+
@patch.dict(os.environ, {"APPMAP": "False"})
24+
def test_none__appmap_disabled_mixed_case(self):
25+
assert DetectEnabled.should_enable(None) == False
26+
2327
@patch.dict(os.environ, {"APPMAP": "true"})
2428
def test_none__appmap_enabled(self):
2529
# if there's no recording method then it's disabled
2630
assert DetectEnabled.should_enable(None) == False
2731

32+
@patch.dict(os.environ, {"APPMAP": "True"})
33+
def test_none__appmap_enabled_mixed_case(self):
34+
assert DetectEnabled.should_enable(None) == False
35+
36+
@patch.dict(os.environ, {"APPMAP": "invalid_value"})
37+
def test_none__appmap_invalid_value(self):
38+
assert DetectEnabled.should_enable(None) == False
39+
2840
def test_invalid__no_envvar(self):
2941
assert DetectEnabled.should_enable("invalid_recording_method") == False
3042

@@ -52,6 +64,22 @@ def test_some__recording_method_enabled(self):
5264
for recording_method in RECORDING_METHODS:
5365
assert DetectEnabled.should_enable(recording_method) == True
5466

67+
recording_methods_as_true_mixed_case = {
68+
key: "True" for key in recording_methods_as_true.keys()
69+
}
70+
71+
@patch.dict(os.environ, recording_methods_as_true_mixed_case)
72+
def test_some__recording_method_enabled_mixed_case(self):
73+
for recording_method in RECORDING_METHODS:
74+
assert DetectEnabled.should_enable(recording_method) == True
75+
76+
recording_methods_as_true_invalid = {"APPMAP_RECORD_INVALID": "true"}
77+
78+
@patch.dict(os.environ, recording_methods_as_true_invalid)
79+
def test_some__recording_method_enabled_invalid(self):
80+
for recording_method in RECORDING_METHODS:
81+
assert DetectEnabled.should_enable(recording_method) == False
82+
5583
recording_methods_as_false = {
5684
"_".join(["APPMAP", "RECORD", recording_method.upper()]): "false"
5785
for recording_method in RECORDING_METHODS
@@ -62,6 +90,15 @@ def test_some__recording_method_disabled(self):
6290
for recording_method in RECORDING_METHODS:
6391
assert DetectEnabled.should_enable(recording_method) == False
6492

93+
recording_methods_as_false_mixed_case = {
94+
key: "False" for key in recording_methods_as_false.keys()
95+
}
96+
97+
@patch.dict(os.environ, recording_methods_as_false_mixed_case)
98+
def test_some__recording_method_disabled_mixed_case(self):
99+
for recording_method in RECORDING_METHODS:
100+
assert DetectEnabled.should_enable(recording_method) == False
101+
65102

66103
class TestDetectEnabledFlask:
67104
@staticmethod

appmap/test/test_django.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ def test_framework_metadata(
6464

6565

6666
@pytest.mark.appmap_enabled
67-
def test_app_can_read_body(client, events, monkeypatch): # pylint: disable=unused-argument
67+
def test_app_can_read_body(
68+
client, events, monkeypatch
69+
): # pylint: disable=unused-argument
6870
monkeypatch.setenv("APPMAP_RECORD_REQUESTS", "false")
6971
response = client.post("/echo", json={"test": "json"})
7072
assert response.content == b'{"test": "json"}'

appmap/test/test_test_frameworks.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ def test_appmap_unittest_runner_disabled(testdir, monkeypatch):
4141
)
4242
assert result.ret == 0
4343
r = re.compile(r"AppMap disabled")
44-
assert [l for l in filter(r.search, result.errlines)], "Warning not found"
44+
# when APPMAP=false it no longer prints a warning
45+
assert [l for l in filter(r.search, result.errlines)] == []
4546

4647

4748
def test_pytest_runner_unittests(testdir):

0 commit comments

Comments
 (0)