Skip to content

Commit 191c9a0

Browse files
peterstone2017wangbill1993
andauthored
Enable debug logging using the PYTHON_ENABLE_DEBUG_LOGGING flag (#939)
* Supporting customer enable debug logging flag from app settings * Removing unused import * Fixed flake8 styling issues * Fix flaky enable debug log tests * Fix enable debug logging test * Address PR feedbacks(Add test case, Copyright, remove comments) * Remove duplicate test app for enable debug logging testing Co-authored-by: wangbill <[email protected]>
1 parent edc21f8 commit 191c9a0

File tree

8 files changed

+191
-25
lines changed

8 files changed

+191
-25
lines changed

CODEOWNERS

Lines changed: 1 addition & 1 deletion
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
13+
* @vrdmr @gavin-aguiar @YunchuWang

azure_functions_worker/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
PYTHON_THREADPOOL_THREAD_COUNT = "PYTHON_THREADPOOL_THREAD_COUNT"
2929
PYTHON_ISOLATE_WORKER_DEPENDENCIES = "PYTHON_ISOLATE_WORKER_DEPENDENCIES"
3030
PYTHON_ENABLE_WORKER_EXTENSIONS = "PYTHON_ENABLE_WORKER_EXTENSIONS"
31+
PYTHON_ENABLE_DEBUG_LOGGING = "PYTHON_ENABLE_DEBUG_LOGGING"
3132
FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED = \
3233
"FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED"
3334
"""

azure_functions_worker/dispatcher.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@
2727
from .constants import (PYTHON_THREADPOOL_THREAD_COUNT,
2828
PYTHON_THREADPOOL_THREAD_COUNT_DEFAULT,
2929
PYTHON_THREADPOOL_THREAD_COUNT_MAX_37,
30-
PYTHON_THREADPOOL_THREAD_COUNT_MIN)
30+
PYTHON_THREADPOOL_THREAD_COUNT_MIN,
31+
PYTHON_ENABLE_DEBUG_LOGGING)
3132
from .logging import disable_console_logging, enable_console_logging
3233
from .logging import (logger, error_logger, is_system_log_category,
3334
CONSOLE_LOG_PREFIX)
3435
from .extension import ExtensionManager
35-
from .utils.common import get_app_setting
36+
from .utils.common import get_app_setting, is_envvar_true
3637
from .utils.tracing import marshall_exception_trace
3738
from .utils.dependency import DependencyManager
3839
from .utils.wrappers import disable_feature_by
@@ -145,8 +146,9 @@ async def dispatch_forever(self):
145146
logging_handler = AsyncLoggingHandler()
146147
root_logger = logging.getLogger()
147148

148-
# Don't change this unless you read #780 and #745
149-
root_logger.setLevel(logging.INFO)
149+
log_level = logging.INFO if not is_envvar_true(
150+
PYTHON_ENABLE_DEBUG_LOGGING) else logging.DEBUG
151+
root_logger.setLevel(log_level)
150152
root_logger.addHandler(logging_handler)
151153
logger.info('Switched to gRPC logging.')
152154
logging_handler.flush()
@@ -484,6 +486,10 @@ async def _handle__function_environment_reload_request(self, req):
484486
self._create_sync_call_tp(self._get_sync_tp_max_workers())
485487
)
486488

489+
if is_envvar_true(PYTHON_ENABLE_DEBUG_LOGGING):
490+
root_logger = logging.getLogger()
491+
root_logger.setLevel(logging.DEBUG)
492+
487493
# Reload azure google namespaces
488494
DependencyManager.reload_customer_libraries(
489495
func_env_reload_request.function_app_directory

azure_functions_worker/main.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@ def parse_args():
1212
parser.add_argument('--host',
1313
help="host address")
1414
parser.add_argument('--port', type=int,
15-
help='id for the requests')
15+
help='port number')
1616
parser.add_argument('--workerId', dest='worker_id',
1717
help='id for the worker')
1818
parser.add_argument('--requestId', dest='request_id',
19-
help='log destination: stdout, stderr, '
20-
'syslog, or a file path')
19+
help='id of the request')
2120
parser.add_argument('--log-level', type=str, default='INFO',
2221
choices=['TRACE', 'INFO', 'WARNING', 'ERROR'],
2322
help="log level: 'TRACE', 'INFO', 'WARNING', "

tests/endtoend/test_linux_consumption.py

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
from unittest import TestCase, skip
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
from unittest import TestCase
24

35
import os
46
import sys
@@ -9,7 +11,6 @@
911
)
1012

1113

12-
@skip('Flaky test and needs stabilization')
1314
class TestLinuxConsumption(TestCase):
1415
"""Test worker behaviors on specific scenarios.
1516
@@ -134,6 +135,59 @@ def test_old_protobuf(self):
134135
self.assertIn('grpc', content)
135136
self.assertEqual(resp.status_code, 200)
136137

138+
def test_debug_logging_disabled(self):
139+
"""An HttpTrigger function app with 'azure-functions' library
140+
should return 200 and by default customer debug logging should be
141+
disabled.
142+
"""
143+
with LinuxConsumptionWebHostController("3", self._py_version) as ctrl:
144+
ctrl.assign_container(env={
145+
"AzureWebJobsStorage": self._storage,
146+
"SCM_RUN_FROM_PACKAGE": self._get_blob_url("EnableDebugLogging")
147+
})
148+
req = Request('GET', f'{ctrl.url}/api/HttpTrigger1')
149+
resp = ctrl.send_request(req)
150+
151+
self.assertEqual(resp.status_code, 200)
152+
container_log = ctrl.get_container_logs()
153+
func_start_idx = container_log.find(
154+
"Executing 'Functions.HttpTrigger1'")
155+
self.assertTrue(func_start_idx > -1,
156+
"HttpTrigger function is not executed.")
157+
func_log = container_log[func_start_idx:]
158+
159+
self.assertIn('logging info', func_log)
160+
self.assertIn('logging warning', func_log)
161+
self.assertIn('logging error', func_log)
162+
self.assertNotIn('logging debug', func_log)
163+
164+
def test_debug_logging_enabled(self):
165+
"""An HttpTrigger function app with 'azure-functions' library
166+
should return 200 and with customer debug logging enabled, debug logs
167+
should be written to container logs.
168+
"""
169+
with LinuxConsumptionWebHostController("3", self._py_version) as ctrl:
170+
ctrl.assign_container(env={
171+
"AzureWebJobsStorage": self._storage,
172+
"SCM_RUN_FROM_PACKAGE": self._get_blob_url(
173+
"EnableDebugLogging"),
174+
"PYTHON_ENABLE_DEBUG_LOGGING": "1"
175+
})
176+
req = Request('GET', f'{ctrl.url}/api/HttpTrigger1')
177+
resp = ctrl.send_request(req)
178+
179+
self.assertEqual(resp.status_code, 200)
180+
container_log = ctrl.get_container_logs()
181+
func_start_idx = container_log.find(
182+
"Executing 'Functions.HttpTrigger1'")
183+
self.assertTrue(func_start_idx > -1)
184+
func_log = container_log[func_start_idx:]
185+
186+
self.assertIn('logging info', func_log)
187+
self.assertIn('logging warning', func_log)
188+
self.assertIn('logging error', func_log)
189+
self.assertIn('logging debug', func_log)
190+
137191
def _get_blob_url(self, scenario_name: str) -> str:
138192
return (
139193
f'https://pythonworker{self._py_shortform}sa.blob.core.windows.net/'

tests/unittests/http_functions/debug_user_logging/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
import azure.functions
66

7-
87
logger = logging.getLogger('my function')
8+
logger.setLevel(logging.DEBUG)
99

1010

1111
def main(req: azure.functions.HttpRequest):
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
import typing
4+
import os
5+
from unittest.mock import patch
6+
7+
from azure_functions_worker import testutils
8+
from azure_functions_worker.constants import PYTHON_ENABLE_DEBUG_LOGGING
9+
from azure_functions_worker.testutils import TESTS_ROOT, remove_path
10+
11+
HOST_JSON_TEMPLATE_WITH_LOGLEVEL_INFO = """\
12+
{
13+
"version": "2.0",
14+
"logging": {
15+
"logLevel": {
16+
"default": "Information"
17+
}
18+
},
19+
"functionTimeout": "00:05:00"
20+
}
21+
"""
22+
23+
24+
class TestDebugLoggingEnabledFunctions(testutils.WebHostTestCase):
25+
@classmethod
26+
def setUpClass(cls):
27+
os_environ = os.environ.copy()
28+
os_environ[PYTHON_ENABLE_DEBUG_LOGGING] = '1'
29+
cls._patch_environ = patch.dict('os.environ', os_environ)
30+
cls._patch_environ.start()
31+
super().setUpClass()
32+
33+
@classmethod
34+
def tearDownClass(self):
35+
super().tearDownClass()
36+
self._patch_environ.stop()
37+
38+
@classmethod
39+
def get_script_dir(cls):
40+
return testutils.UNIT_TESTS_FOLDER / 'log_filtering_functions'
41+
42+
def test_debug_logging_enabled(self):
43+
r = self.webhost.request('GET', 'debug_logging')
44+
self.assertEqual(r.status_code, 200)
45+
self.assertEqual(r.text, 'OK-debug')
46+
47+
def check_log_debug_logging_enabled(self, host_out: typing.List[str]):
48+
self.assertIn('logging info', host_out)
49+
self.assertIn('logging warning', host_out)
50+
self.assertIn('logging debug', host_out)
51+
self.assertIn('logging error', host_out)
52+
53+
54+
class TestDebugLoggingDisabledFunctions(testutils.WebHostTestCase):
55+
@classmethod
56+
def setUpClass(cls):
57+
os_environ = os.environ.copy()
58+
os_environ[PYTHON_ENABLE_DEBUG_LOGGING] = '0'
59+
cls._patch_environ = patch.dict('os.environ', os_environ)
60+
cls._patch_environ.start()
61+
super().setUpClass()
62+
63+
@classmethod
64+
def tearDownClass(self):
65+
super().tearDownClass()
66+
self._patch_environ.stop()
67+
68+
@classmethod
69+
def get_script_dir(cls):
70+
return testutils.UNIT_TESTS_FOLDER / 'log_filtering_functions'
71+
72+
def test_debug_logging_disabled(self):
73+
r = self.webhost.request('GET', 'debug_logging')
74+
self.assertEqual(r.status_code, 200)
75+
self.assertEqual(r.text, 'OK-debug')
76+
77+
def check_log_debug_logging_disabled(self, host_out: typing.List[str]):
78+
self.assertIn('logging info', host_out)
79+
self.assertIn('logging warning', host_out)
80+
self.assertIn('logging error', host_out)
81+
self.assertNotIn('logging debug', host_out)
82+
83+
84+
class TestLogFilteringFunctions(testutils.WebHostTestCase):
85+
@classmethod
86+
def setUpClass(cls):
87+
host_json = TESTS_ROOT / cls.get_script_dir() / 'host.json'
88+
89+
with open(host_json, 'w+') as f:
90+
f.write(HOST_JSON_TEMPLATE_WITH_LOGLEVEL_INFO)
91+
92+
os_environ = os.environ.copy()
93+
os_environ[PYTHON_ENABLE_DEBUG_LOGGING] = '1'
94+
cls._patch_environ = patch.dict('os.environ', os_environ)
95+
cls._patch_environ.start()
96+
super().setUpClass()
97+
98+
@classmethod
99+
def tearDownClass(self):
100+
host_json = TESTS_ROOT / self.get_script_dir() / 'host.json'
101+
remove_path(host_json)
102+
103+
super().tearDownClass()
104+
self._patch_environ.stop()
105+
106+
@classmethod
107+
def get_script_dir(cls):
108+
return testutils.UNIT_TESTS_FOLDER / 'log_filtering_functions'
109+
110+
def test_debug_logging_filtered(self):
111+
r = self.webhost.request('GET', 'debug_logging')
112+
self.assertEqual(r.status_code, 200)
113+
self.assertEqual(r.text, 'OK-debug')
114+
115+
def check_log_debug_logging_filtered(self, host_out: typing.List[str]):
116+
self.assertIn('logging info', host_out)
117+
self.assertIn('logging warning', host_out)
118+
self.assertNotIn('logging debug', host_out)
119+
self.assertIn('logging error', host_out)

tests/unittests/test_http_functions.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import filecmp
66
import typing
77
import os
8-
import unittest
98
import pytest
109

1110
from azure_functions_worker import testutils
@@ -98,34 +97,22 @@ def check_log_async_logging(self, host_out: typing.List[str]):
9897
self.assertIn('hello info', host_out)
9998
self.assertIn('and another error', host_out)
10099

101-
@unittest.skip("Reverting the debug logs PR as host currently cannot handle"
102-
"apps with lot of debug statements. Reverting PR: "
103-
"azure-functions-python-worker/pull/745")
104100
def test_debug_logging(self):
105101
r = self.webhost.request('GET', 'debug_logging')
106102
self.assertEqual(r.status_code, 200)
107103
self.assertEqual(r.text, 'OK-debug')
108104

109-
@unittest.skip("Reverting the debug logs PR as host currently cannot handle"
110-
"apps with lot of debug statements. Reverting PR: "
111-
"azure-functions-python-worker/pull/745")
112105
def check_log_debug_logging(self, host_out: typing.List[str]):
113106
self.assertIn('logging info', host_out)
114107
self.assertIn('logging warning', host_out)
115-
self.assertIn('logging debug', host_out)
116108
self.assertIn('logging error', host_out)
109+
self.assertNotIn('logging debug', host_out)
117110

118-
@unittest.skip("Reverting the debug logs PR as host currently cannot handle"
119-
"apps with lot of debug statements. Reverting PR: "
120-
"azure-functions-python-worker/pull/745")
121111
def test_debug_with_user_logging(self):
122112
r = self.webhost.request('GET', 'debug_user_logging')
123113
self.assertEqual(r.status_code, 200)
124114
self.assertEqual(r.text, 'OK-user-debug')
125115

126-
@unittest.skip("Reverting the debug logs PR as host currently cannot handle"
127-
"apps with lot of debug statements. Reverting PR: "
128-
"azure-functions-python-worker/pull/745")
129116
def check_log_debug_with_user_logging(self, host_out: typing.List[str]):
130117
self.assertIn('logging info', host_out)
131118
self.assertIn('logging warning', host_out)

0 commit comments

Comments
 (0)