Skip to content

Commit 36ac252

Browse files
authored
Adding support for debug logs in executed functions. (#745)
* Adding support for debug logs in executed functions. * Adding tests to see make sure host prevents debug logs
1 parent 825f0b0 commit 36ac252

File tree

14 files changed

+235
-17
lines changed

14 files changed

+235
-17
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ celerybeat-schedule
8484

8585
# virtualenv (.venv/.venv36/.venv37/.venv38)
8686
.venv*
87-
venv/
87+
venv*/
8888
ENV/
8989
py3env/
9090

azure_functions_worker/dispatcher.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ async def dispatch_forever(self):
126126
# established, should use it for system and user logs
127127
logging_handler = AsyncLoggingHandler()
128128
root_logger = logging.getLogger()
129-
root_logger.setLevel(logging.INFO)
129+
root_logger.setLevel(logging.DEBUG)
130130
root_logger.addHandler(logging_handler)
131131
logger.info('Switched to gRPC logging.')
132132
logging_handler.flush()

azure_functions_worker/main.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,28 @@
55

66
import argparse
77

8-
from ._thirdparty import aio_compat
98
from . import dispatcher
109
from . import logging
10+
from ._thirdparty import aio_compat
1111
from .logging import error_logger, logger
1212

1313

1414
def parse_args():
1515
parser = argparse.ArgumentParser(
1616
description='Python Azure Functions Worker')
17-
parser.add_argument('--host')
18-
parser.add_argument('--port', type=int)
19-
parser.add_argument('--workerId', dest='worker_id')
20-
parser.add_argument('--requestId', dest='request_id')
17+
parser.add_argument('--host',
18+
help="host address")
19+
parser.add_argument('--port', type=int,
20+
help='id for the requests')
21+
parser.add_argument('--workerId', dest='worker_id',
22+
help='id for the worker')
23+
parser.add_argument('--requestId', dest='request_id',
24+
help='log destination: stdout, stderr, '
25+
'syslog, or a file path')
2126
parser.add_argument('--log-level', type=str, default='INFO',
22-
choices=['TRACE', 'INFO', 'WARNING', 'ERROR'],)
27+
choices=['TRACE', 'INFO', 'WARNING', 'ERROR'],
28+
help="log level: 'TRACE', 'INFO', 'WARNING', "
29+
"or 'ERROR'")
2330
parser.add_argument('--log-to', type=str, default=None,
2431
help='log destination: stdout, stderr, '
2532
'syslog, or a file path')
@@ -45,8 +52,9 @@ def main():
4552

4653

4754
async def start_async(host, port, worker_id, request_id):
48-
disp = await dispatcher.Dispatcher.connect(
49-
host, port, worker_id, request_id,
50-
connect_timeout=5.0)
55+
disp = await dispatcher.Dispatcher.connect(host=host, port=port,
56+
worker_id=worker_id,
57+
request_id=request_id,
58+
connect_timeout=5.0)
5159

5260
await disp.dispatch_forever()

azure_functions_worker/testutils.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def wrapper(self, *args, __meth__=test_case,
148148
# Trim off host output timestamps
149149
host_output = getattr(self, 'host_out', '')
150150
output_lines = host_output.splitlines()
151-
ts_re = r"^\[\d+\/\d+\/\d+ \d+\:\d+\:\d+ (A|P)M\]"
151+
ts_re = r"^\[\d+\/\d+\/\d+ \d+\:\d+\:\d+.*(A|P)*M*\]"
152152
output = list(map(
153153
lambda s: re.sub(ts_re, '', s).strip(),
154154
output_lines))
@@ -171,6 +171,11 @@ class WebHostTestCase(unittest.TestCase, metaclass=WebHostTestCaseMeta):
171171
In addition to automatically starting up a WebHost instance,
172172
this test case class logs WebHost stdout/stderr in case
173173
a unit test fails.
174+
175+
You can write two sets of test - test_* and check_log_* tests.
176+
177+
test_ABC - Unittest
178+
check_log_ABC - Check logs generated during the execution of test_ABC.
174179
"""
175180
host_stdout_logger = logging.getLogger('webhosttests')
176181

@@ -728,7 +733,7 @@ def call(*args, **kwargs):
728733
return decorate
729734

730735

731-
def _remove_path(path):
736+
def remove_path(path):
732737
if path.is_symlink():
733738
path.unlink()
734739
elif path.is_dir():
@@ -738,7 +743,7 @@ def _remove_path(path):
738743

739744

740745
def _symlink_dir(src, dst):
741-
_remove_path(dst)
746+
remove_path(dst)
742747

743748
if ON_WINDOWS:
744749
shutil.copytree(str(src), str(dst))
@@ -751,8 +756,9 @@ def _setup_func_app(app_root):
751756
ping_func = app_root / 'ping'
752757
host_json = app_root / 'host.json'
753758

754-
with open(host_json, 'w') as f:
755-
f.write(HOST_JSON_TEMPLATE)
759+
if not os.path.isfile(host_json):
760+
with open(host_json, 'w') as f:
761+
f.write(HOST_JSON_TEMPLATE)
756762

757763
_symlink_dir(TESTS_ROOT / 'common' / 'ping', ping_func)
758764
_symlink_dir(EXTENSIONS_PATH, extensions)
@@ -764,7 +770,7 @@ def _teardown_func_app(app_root):
764770
host_json = app_root / 'host.json'
765771

766772
for path in (extensions, ping_func, host_json):
767-
_remove_path(path)
773+
remove_path(path)
768774

769775

770776
def _main():
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"scriptFile": "main.py",
3+
"bindings": [
4+
{
5+
"type": "httpTrigger",
6+
"direction": "in",
7+
"name": "req"
8+
},
9+
{
10+
"type": "http",
11+
"direction": "out",
12+
"name": "$return"
13+
}
14+
]
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
import logging
4+
5+
import azure.functions
6+
7+
8+
def main(req: azure.functions.HttpRequest):
9+
logging.info('logging info', exc_info=True)
10+
logging.warning('logging warning', exc_info=True)
11+
logging.debug('logging debug', exc_info=True)
12+
logging.error('logging error', exc_info=True)
13+
return 'OK-debug'
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"scriptFile": "main.py",
3+
"bindings": [
4+
{
5+
"type": "httpTrigger",
6+
"direction": "in",
7+
"name": "req"
8+
},
9+
{
10+
"type": "http",
11+
"direction": "out",
12+
"name": "$return"
13+
}
14+
]
15+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
import logging
4+
5+
import azure.functions
6+
7+
8+
logger = logging.getLogger('my function')
9+
10+
11+
def main(req: azure.functions.HttpRequest):
12+
logger.info('logging info', exc_info=True)
13+
logger.warning('logging warning', exc_info=True)
14+
logger.debug('logging debug', exc_info=True)
15+
logger.error('logging error', exc_info=True)
16+
return 'OK-user-debug'
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"scriptFile": "main.py",
3+
"bindings": [
4+
{
5+
"type": "httpTrigger",
6+
"direction": "in",
7+
"name": "req"
8+
},
9+
{
10+
"type": "http",
11+
"direction": "out",
12+
"name": "$return"
13+
}
14+
]
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
import logging
4+
5+
import azure.functions
6+
7+
8+
def main(req: azure.functions.HttpRequest):
9+
logging.info('logging info', exc_info=True)
10+
logging.warning('logging warning', exc_info=True)
11+
logging.debug('logging debug', exc_info=True)
12+
logging.error('logging error', exc_info=True)
13+
return 'OK-debug'

0 commit comments

Comments
 (0)