Categorize SDK logs as system logs (#850)

* Add testing for cases

* Amend expected behavior
This commit is contained in:
Hanzhang Zeng 2021-06-02 10:22:27 -07:00 коммит произвёл GitHub
Родитель 473cd1e264
Коммит a3c047bb01
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 249 добавлений и 4 удалений

Просмотреть файл

@ -9,6 +9,7 @@ import sys
# Logging Prefixes # Logging Prefixes
CONSOLE_LOG_PREFIX = "LanguageWorkerConsoleLog" CONSOLE_LOG_PREFIX = "LanguageWorkerConsoleLog"
SYSTEM_LOG_PREFIX = "azure_functions_worker" SYSTEM_LOG_PREFIX = "azure_functions_worker"
SDK_LOG_PREFIX = "azure.functions"
logger: logging.Logger = logging.getLogger('azure_functions_worker') logger: logging.Logger = logging.getLogger('azure_functions_worker')
@ -76,6 +77,15 @@ def enable_console_logging() -> None:
def is_system_log_category(ctg: str) -> bool: def is_system_log_category(ctg: str) -> bool:
# Category starts with 'azure_functions_worker' or """Check if the logging namespace belongs to system logs. Category starts
# 'azure_functions_worker_errors' will be treated as system logs with the following name will be treated as system logs.
return ctg.lower().startswith(SYSTEM_LOG_PREFIX) 1. 'azure_functions_worker' (Worker Info)
2. 'azure_functions_worker_errors' (Worker Error)
3. 'azure.functions' (SDK)
Expected behaviors for sytem logs and customer logs are listed below:
local_console customer_app_insight functions_kusto_table
system_log false false true
customer_log true true false
"""
return ctg.startswith(SYSTEM_LOG_PREFIX) or ctg.startswith(SDK_LOG_PREFIX)

Просмотреть файл

@ -0,0 +1,16 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import logging
import azure.functions as func
sdk_logger = logging.getLogger('azure.functions')
def main(req: func.HttpRequest):
sdk_logger.info('sdk_logger info')
sdk_logger.warning('sdk_logger warning')
sdk_logger.debug('sdk_logger debug')
sdk_logger.error('sdk_logger error', exc_info=True)
return 'OK-sdk-logger'

Просмотреть файл

@ -0,0 +1,16 @@
{
"scriptFile": "__init__.py",
"bindings": [
{
"type": "httpTrigger",
"direction": "in",
"name": "req"
},
{
"type": "http",
"direction": "out",
"name": "$return"
}
],
"entryPoint": "main"
}

Просмотреть файл

@ -0,0 +1,16 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import logging
import azure.functions as func
sdk_submodule_logger = logging.getLogger('azure.functions.submodule')
def main(req: func.HttpRequest):
sdk_submodule_logger.info('sdk_submodule_logger info')
sdk_submodule_logger.warning('sdk_submodule_logger warning')
sdk_submodule_logger.debug('sdk_submodule_logger debug')
sdk_submodule_logger.error('sdk_submodule_logger error', exc_info=True)
return 'OK-sdk-submodule-logging'

Просмотреть файл

@ -0,0 +1,16 @@
{
"scriptFile": "__init__.py",
"bindings": [
{
"type": "httpTrigger",
"direction": "in",
"name": "req"
},
{
"type": "http",
"direction": "out",
"name": "$return"
}
],
"entryPoint": "main"
}

Просмотреть файл

@ -1,6 +1,5 @@
# Copyright (c) Microsoft Corporation. All rights reserved. # Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. # Licensed under the MIT License.
import typing import typing
from azure_functions_worker import testutils from azure_functions_worker import testutils
@ -20,6 +19,15 @@ HOST_JSON_TEMPLATE_WITH_LOGLEVEL_INFO = """\
class TestLogFilteringFunctions(testutils.WebHostTestCase): class TestLogFilteringFunctions(testutils.WebHostTestCase):
"""This class is for testing the logger behavior in Python Worker when
dealing with customer's log and system's log. Here's a list of expected
behaviors:
local_console customer_app_insight functions_kusto_table
system_log false false true
customer_log true true false
Please ensure the following unit test cases align with the expectations
"""
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
@ -50,6 +58,7 @@ class TestLogFilteringFunctions(testutils.WebHostTestCase):
self.assertIn('logging info', host_out) self.assertIn('logging info', host_out)
self.assertIn('logging warning', host_out) self.assertIn('logging warning', host_out)
self.assertIn('logging error', host_out) self.assertIn('logging error', host_out)
# See HOST_JSON_TEMPLATE_WITH_LOGLEVEL_INFO, debug log is disabled
self.assertNotIn('logging debug', host_out) self.assertNotIn('logging debug', host_out)
def test_debug_with_user_logging(self): def test_debug_with_user_logging(self):
@ -61,4 +70,40 @@ class TestLogFilteringFunctions(testutils.WebHostTestCase):
self.assertIn('logging info', host_out) self.assertIn('logging info', host_out)
self.assertIn('logging warning', host_out) self.assertIn('logging warning', host_out)
self.assertIn('logging error', host_out) self.assertIn('logging error', host_out)
# See HOST_JSON_TEMPLATE_WITH_LOGLEVEL_INFO, debug log is disabled
self.assertNotIn('logging debug', host_out) self.assertNotIn('logging debug', host_out)
def test_info_with_sdk_logging(self):
"""Invoke a HttpTrigger sdk_logging which contains logging invocation
via the azure.functions logger. This should be treated as system logs,
which means the log should not be displayed in local console.
"""
r = self.webhost.request('GET', 'sdk_logging')
self.assertEqual(r.status_code, 200)
self.assertEqual(r.text, 'OK-sdk-logger')
def check_log_info_with_sdk_logging(self, host_out: typing.List[str]):
# See TestLogFilteringFunctions docstring
# System log should not be captured in console
self.assertNotIn('sdk_logger info', host_out)
self.assertNotIn('sdk_logger warning', host_out)
self.assertNotIn('sdk_logger error', host_out)
self.assertNotIn('sdk_logger debug', host_out)
def test_info_with_sdk_submodule_logging(self):
"""Invoke a HttpTrigger sdk_submodule_logging which contains logging
invocation via the azure.functions logger. This should be treated as
system logs.
"""
r = self.webhost.request('GET', 'sdk_submodule_logging')
self.assertEqual(r.status_code, 200)
self.assertEqual(r.text, 'OK-sdk-submodule-logging')
def check_log_info_with_sdk_submodule_logging(self,
host_out: typing.List[str]):
# See TestLogFilteringFunctions docstring
# System log should not be captured in console
self.assertNotIn('sdk_submodule_logger info', host_out)
self.assertNotIn('sdk_submodule_logger warning', host_out)
self.assertNotIn('sdk_submodule_logger error', host_out)
self.assertNotIn('sdk_submodule_logger debug', host_out)

Просмотреть файл

@ -0,0 +1,35 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import unittest
from azure_functions_worker import logging as flog
class TestLogging(unittest.TestCase):
"""This class is for testing the grpc logging behavior in Python Worker.
Here's a list of expected behaviors:
local_console customer_app_insight functions_kusto_table
system_log false false true
customer_log true true false
Please ensure the following unit test cases align with the expectations
"""
def test_system_log_namespace(self):
"""Ensure the following list is part of the system's log
"""
self.assertTrue(flog.is_system_log_category('azure_functions_worker'))
self.assertTrue(
flog.is_system_log_category('azure_functions_worker_error')
)
self.assertTrue(flog.is_system_log_category('azure.functions'))
self.assertTrue(flog.is_system_log_category('azure.functions.module'))
def test_customer_log_namespace(self):
"""Ensure the following list is part of the customer's log
"""
self.assertFalse(flog.is_system_log_category('customer_logger'))
self.assertFalse(flog.is_system_log_category('azure'))
self.assertFalse(flog.is_system_log_category('protobuf'))
self.assertFalse(flog.is_system_log_category('root'))
self.assertFalse(flog.is_system_log_category(''))

Просмотреть файл

@ -0,0 +1,91 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
from unittest.mock import patch, call
from azure_functions_worker import testutils, protos
from azure_functions_worker.logging import is_system_log_category
class TestMockLogFilteringFunctions(testutils.AsyncTestCase):
dir = testutils.UNIT_TESTS_FOLDER / 'log_filtering_functions'
async def test_root_logger_should_be_customer_log(self):
"""When customer use the root logger to send logs, the 'root' namespace
should be treated as customer log, only sending to our customers.
"""
with patch(
'azure_functions_worker.dispatcher.is_system_log_category'
) as islc_mock:
async with testutils.start_mockhost(script_root=self.dir) as host:
await host.load_function('debug_logging')
await self._invoke_function(host, 'debug_logging')
self.assertIn(call('root'), islc_mock.call_args_list)
self.assertFalse(is_system_log_category('root'))
async def test_customer_logging_should_not_be_system_log(self):
"""When sdk uses the 'azure' logger to send logs
(e.g. 'azure.servicebus'), the namespace should be treated as customer
log, only sends to our customers.
"""
with patch(
'azure_functions_worker.dispatcher.is_system_log_category'
) as islc_mock:
async with testutils.start_mockhost(script_root=self.dir) as host:
await host.load_function('debug_user_logging')
await self._invoke_function(host, 'debug_user_logging')
self.assertIn(call('my function'), islc_mock.call_args_list)
self.assertFalse(is_system_log_category('my function'))
async def test_sdk_logger_should_be_system_log(self):
"""When sdk uses the 'azure.functions' logger to send logs, the
namespace should be treated as system log, sending to our customers and
our kusto table.
"""
with patch(
'azure_functions_worker.dispatcher.is_system_log_category'
) as islc_mock:
async with testutils.start_mockhost(script_root=self.dir) as host:
await host.load_function('sdk_logging')
await self._invoke_function(host, 'sdk_logging')
self.assertIn(
call('azure.functions'), islc_mock.call_args_list
)
self.assertTrue(is_system_log_category('azure.functions'))
async def test_sdk_submodule_logger_should_be_system_log(self):
"""When sdk uses the 'azure.functions.submodule' logger to send logs,
the namespace should be treated as system log, sending to our customers
and our kusto table.
"""
with patch(
'azure_functions_worker.dispatcher.is_system_log_category'
) as islc_mock:
async with testutils.start_mockhost(script_root=self.dir) as host:
await host.load_function('sdk_submodule_logging')
await self._invoke_function(host, 'sdk_submodule_logging')
self.assertIn(
call('azure.functions.submodule'), islc_mock.call_args_list
)
self.assertTrue(
is_system_log_category('azure.functions.submodule')
)
async def _invoke_function(self,
host: testutils._MockWebHost,
function_name: str):
_, r = await host.invoke_function(
function_name, [
protos.ParameterBinding(
name='req',
data=protos.TypedData(
http=protos.RpcHttp(method='GET')
)
)
]
)
self.assertEqual(r.response.result.status, protos.StatusResult.Success)