Skip to content

Commit 56f5caf

Browse files
hallvictoriavrdmr
andauthored
python specific app setting logs added (Azure#1353)
* python specific app setting logs added * lint * refactor * codeowner * manual convert dict to string * moved to utils, more efficient * added to old tests, created new * addressing comments * fixing tests, efficiency * python worker ext * removed comma * FLAKE --------- Co-authored-by: Varad Meru <[email protected]>
1 parent d4200f8 commit 56f5caf

File tree

5 files changed

+172
-6
lines changed

5 files changed

+172
-6
lines changed

CODEOWNERS

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010
# For all file changes, github would automatically
1111
# include the following people in the PRs.
1212

13-
* @vrdmr @gavin-aguiar @YunchuWang @pdthummar
13+
* @vrdmr @gavin-aguiar @YunchuWang @pdthummar @hallvictoria

azure_functions_worker/dispatcher.py

+13-5
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,19 @@
2121

2222
from . import bindings, constants, functions, loader, protos
2323
from .bindings.shared_memory_data_transfer import SharedMemoryManager
24-
from .constants import (PYTHON_THREADPOOL_THREAD_COUNT,
24+
from .constants import (PYTHON_ROLLBACK_CWD_PATH,
25+
PYTHON_THREADPOOL_THREAD_COUNT,
2526
PYTHON_THREADPOOL_THREAD_COUNT_DEFAULT,
2627
PYTHON_THREADPOOL_THREAD_COUNT_MAX_37,
2728
PYTHON_THREADPOOL_THREAD_COUNT_MIN,
28-
PYTHON_ENABLE_DEBUG_LOGGING, SCRIPT_FILE_NAME,
29+
PYTHON_ENABLE_DEBUG_LOGGING,
30+
SCRIPT_FILE_NAME,
2931
PYTHON_LANGUAGE_RUNTIME, CUSTOMER_PACKAGES_PATH)
3032
from .extension import ExtensionManager
3133
from .logging import disable_console_logging, enable_console_logging
3234
from .logging import (logger, error_logger, is_system_log_category,
3335
CONSOLE_LOG_PREFIX, format_exception)
36+
from .utils.app_setting_manager import get_python_appsetting_state
3437
from .utils.common import get_app_setting, is_envvar_true
3538
from .utils.dependency import DependencyManager
3639
from .utils.tracing import marshall_exception_trace
@@ -265,11 +268,14 @@ async def _handle__worker_init_request(self, request):
265268
'python version %s, '
266269
'worker version %s, '
267270
'request ID %s.'
271+
'App Settings state: %s.'
268272
' To enable debug level logging, please refer to '
269273
'https://aka.ms/python-enable-debug-logging',
270274
sys.version,
271275
VERSION,
272-
self.request_id)
276+
self.request_id,
277+
get_python_appsetting_state()
278+
)
273279

274280
worker_init_request = request.worker_init_request
275281
host_capabilities = worker_init_request.capabilities
@@ -546,9 +552,11 @@ async def _handle__function_environment_reload_request(self, request):
546552
try:
547553
logger.info('Received FunctionEnvironmentReloadRequest, '
548554
'request ID: %s,'
555+
'App Settings state: %s.'
549556
' To enable debug level logging, please refer to '
550557
'https://aka.ms/python-enable-debug-logging',
551-
self.request_id)
558+
self.request_id,
559+
get_python_appsetting_state())
552560

553561
func_env_reload_request = \
554562
request.function_environment_reload_request
@@ -689,7 +697,7 @@ def _get_context(invoc_request: protos.InvocationRequest, name: str,
689697
name, directory, invoc_request.invocation_id,
690698
_invocation_id_local, trace_context, retry_context)
691699

692-
@disable_feature_by(constants.PYTHON_ROLLBACK_CWD_PATH)
700+
@disable_feature_by(PYTHON_ROLLBACK_CWD_PATH)
693701
def _change_cwd(self, new_cwd: str):
694702
if os.path.exists(new_cwd):
695703
os.chdir(new_cwd)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
import os
4+
import sys
5+
6+
from ..constants import (PYTHON_ROLLBACK_CWD_PATH,
7+
PYTHON_THREADPOOL_THREAD_COUNT,
8+
PYTHON_ISOLATE_WORKER_DEPENDENCIES,
9+
PYTHON_ENABLE_WORKER_EXTENSIONS,
10+
PYTHON_ENABLE_WORKER_EXTENSIONS_DEFAULT,
11+
PYTHON_ENABLE_WORKER_EXTENSIONS_DEFAULT_39,
12+
PYTHON_ENABLE_DEBUG_LOGGING,
13+
FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED)
14+
15+
16+
def get_python_appsetting_state():
17+
current_vars = os.environ.copy()
18+
python_specific_settings = \
19+
[PYTHON_ROLLBACK_CWD_PATH,
20+
PYTHON_THREADPOOL_THREAD_COUNT,
21+
PYTHON_ISOLATE_WORKER_DEPENDENCIES,
22+
PYTHON_ENABLE_DEBUG_LOGGING,
23+
PYTHON_ENABLE_WORKER_EXTENSIONS,
24+
FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED]
25+
26+
app_setting_states = "".join(
27+
f"{app_setting}: {current_vars[app_setting]} "
28+
for app_setting in python_specific_settings
29+
if app_setting in current_vars
30+
)
31+
32+
# Special case for extensions
33+
if 'PYTHON_ENABLE_WORKER_EXTENSIONS' not in current_vars:
34+
if sys.version_info.minor == 9:
35+
app_setting_states += \
36+
(f"{PYTHON_ENABLE_WORKER_EXTENSIONS}: "
37+
f"{str(PYTHON_ENABLE_WORKER_EXTENSIONS_DEFAULT_39)}")
38+
else:
39+
app_setting_states += \
40+
(f"{PYTHON_ENABLE_WORKER_EXTENSIONS}: "
41+
f"{str(PYTHON_ENABLE_WORKER_EXTENSIONS_DEFAULT)} ")
42+
43+
return app_setting_states
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
import collections as col
4+
import os
5+
6+
from unittest.mock import patch
7+
8+
from tests.utils import testutils
9+
from azure_functions_worker.utils.app_setting_manager import \
10+
get_python_appsetting_state
11+
from azure_functions_worker.constants import PYTHON_THREADPOOL_THREAD_COUNT, \
12+
PYTHON_ENABLE_DEBUG_LOGGING
13+
14+
SysVersionInfo = col.namedtuple("VersionInfo", ["major", "minor", "micro",
15+
"releaselevel", "serial"])
16+
DISPATCHER_FUNCTIONS_DIR = testutils.UNIT_TESTS_FOLDER / 'dispatcher_functions'
17+
DISPATCHER_STEIN_FUNCTIONS_DIR = testutils.UNIT_TESTS_FOLDER / \
18+
'dispatcher_functions' / \
19+
'dispatcher_functions_stein'
20+
DISPATCHER_STEIN_INVALID_FUNCTIONS_DIR = testutils.UNIT_TESTS_FOLDER / \
21+
'broken_functions' / \
22+
'invalid_stein'
23+
24+
25+
class TestDefaultAppSettingsLogs(testutils.AsyncTestCase):
26+
"""Tests for default app settings logs."""
27+
28+
@classmethod
29+
def setUpClass(cls):
30+
cls._ctrl = testutils.start_mockhost(
31+
script_root=DISPATCHER_FUNCTIONS_DIR)
32+
os_environ = os.environ.copy()
33+
cls._patch_environ = patch.dict('os.environ', os_environ)
34+
cls._patch_environ.start()
35+
super().setUpClass()
36+
37+
@classmethod
38+
def tearDownClass(cls):
39+
super().tearDownClass()
40+
cls._patch_environ.stop()
41+
42+
async def test_initialize_worker_logging(self):
43+
"""Test if the dispatcher's log can be flushed out during worker
44+
initialization
45+
"""
46+
async with self._ctrl as host:
47+
r = await host.init_worker('3.0.12345')
48+
self.assertTrue('App Settings state: ' in log for log in r.logs)
49+
self.assertTrue('PYTHON_ENABLE_WORKER_EXTENSIONS: '
50+
in log for log in r.logs)
51+
52+
def test_get_python_appsetting_state(self):
53+
app_setting_state = get_python_appsetting_state()
54+
expected_string = "PYTHON_ENABLE_WORKER_EXTENSIONS: "
55+
self.assertIn(expected_string, app_setting_state)
56+
57+
58+
class TestNonDefaultAppSettingsLogs(testutils.AsyncTestCase):
59+
"""Tests for non-default app settings logs."""
60+
61+
@classmethod
62+
def setUpClass(cls):
63+
cls._ctrl = testutils.start_mockhost(
64+
script_root=DISPATCHER_FUNCTIONS_DIR)
65+
os_environ = os.environ.copy()
66+
os_environ[PYTHON_THREADPOOL_THREAD_COUNT] = '20'
67+
os_environ[PYTHON_ENABLE_DEBUG_LOGGING] = '1'
68+
cls._patch_environ = patch.dict('os.environ', os_environ)
69+
cls._patch_environ.start()
70+
super().setUpClass()
71+
72+
@classmethod
73+
def tearDownClass(cls):
74+
super().tearDownClass()
75+
cls._patch_environ.stop()
76+
77+
async def test_initialize_worker_logging(self):
78+
"""Test if the dispatcher's log can be flushed out during worker
79+
initialization
80+
"""
81+
async with self._ctrl as host:
82+
r = await host.init_worker('3.0.12345')
83+
self.assertTrue('App Settings state: ' in log for log in r.logs)
84+
self.assertTrue('PYTHON_THREADPOOL_THREAD_COUNT: '
85+
in log for log in r.logs)
86+
self.assertTrue('PYTHON_ENABLE_DEBUG_LOGGING: '
87+
in log for log in r.logs)
88+
89+
def test_get_python_appsetting_state(self):
90+
app_setting_state = get_python_appsetting_state()
91+
self.assertIn("PYTHON_THREADPOOL_THREAD_COUNT: 20 ", app_setting_state)
92+
self.assertIn("PYTHON_ENABLE_DEBUG_LOGGING: 1 ", app_setting_state)
93+
self.assertIn("PYTHON_ENABLE_WORKER_EXTENSIONS: ", app_setting_state)

tests/unittests/test_dispatcher.py

+22
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ async def test_dispatcher_initialize_worker_logging(self):
9898
1
9999
)
100100

101+
async def test_dispatcher_initialize_worker_settings_logs(self):
102+
"""Test if the dispatcher's log can be flushed out during worker
103+
initialization
104+
"""
105+
async with self._ctrl as host:
106+
r = await host.init_worker('3.0.12345')
107+
self.assertTrue('PYTHON_ENABLE_WORKER_EXTENSIONS: '
108+
in log for log in r.logs)
109+
101110
async def test_dispatcher_environment_reload_logging(self):
102111
"""Test if the sync threadpool will pick up app setting in placeholder
103112
mode (Linux Consumption)
@@ -115,6 +124,19 @@ async def test_dispatcher_environment_reload_logging(self):
115124
1
116125
)
117126

127+
async def test_dispatcher_environment_reload_settings_logs(self):
128+
"""Test if the sync threadpool will pick up app setting in placeholder
129+
mode (Linux Consumption)
130+
"""
131+
async with self._ctrl as host:
132+
await host.init_worker()
133+
await self._check_if_function_is_ok(host)
134+
135+
# Reload environment variable on specialization
136+
r = await host.reload_environment(environment={})
137+
self.assertTrue('PYTHON_ENABLE_WORKER_EXTENSIONS: '
138+
in log for log in r.logs)
139+
118140
async def test_dispatcher_send_worker_request(self):
119141
"""Test if the worker status response will be sent correctly when
120142
a worker status request is received

0 commit comments

Comments
 (0)