azure-functions-python-worker/azure_functions_worker/logging.py

108 строки
3.5 KiB
Python

# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import logging
import logging.handlers
import sys
import traceback
from typing import Optional
# Logging Prefixes
CONSOLE_LOG_PREFIX = "LanguageWorkerConsoleLog"
SYSTEM_LOG_PREFIX = "azure_functions_worker"
SDK_LOG_PREFIX = "azure.functions"
SYSTEM_ERROR_LOG_PREFIX = "azure_functions_worker_errors"
logger: logging.Logger = logging.getLogger(SYSTEM_LOG_PREFIX)
error_logger: logging.Logger = (
logging.getLogger(SYSTEM_ERROR_LOG_PREFIX))
handler: Optional[logging.Handler] = None
error_handler: Optional[logging.Handler] = None
def format_exception(exception: Exception) -> str:
msg = str(exception) + "\n"
if (sys.version_info.major, sys.version_info.minor) < (3, 10):
msg += ''.join(traceback.format_exception(
etype=type(exception),
tb=exception.__traceback__,
value=exception))
elif (sys.version_info.major, sys.version_info.minor) >= (3, 10):
msg += ''.join(traceback.format_exception(exception))
else:
msg = str(exception)
return msg
def setup(log_level, log_destination):
# Since handler and error_handler are moved to the global scope,
# before assigning to these handlers, we should define 'global' keyword
global handler
global error_handler
if log_level == 'TRACE':
log_level = 'DEBUG'
formatter = logging.Formatter(f'{CONSOLE_LOG_PREFIX}'
' %(levelname)s: %(message)s')
if log_destination is None:
# With no explicit log destination we do split logging,
# errors go into stderr, everything else -- to stdout.
error_handler = logging.StreamHandler(sys.stderr)
error_handler.setFormatter(formatter)
error_handler.setLevel(getattr(logging, log_level))
handler = logging.StreamHandler(sys.stdout)
elif log_destination in ('stdout', 'stderr'):
handler = logging.StreamHandler(getattr(sys, log_destination))
elif log_destination == 'syslog':
handler = logging.handlers.SysLogHandler()
else:
handler = logging.FileHandler(log_destination)
if error_handler is None:
error_handler = handler
handler.setFormatter(formatter)
handler.setLevel(getattr(logging, log_level))
logger.addHandler(handler)
logger.setLevel(getattr(logging, log_level))
error_logger.addHandler(error_handler)
error_logger.setLevel(getattr(logging, log_level))
def disable_console_logging() -> None:
# We should only remove the sys.stdout stream, as error_logger is used for
# unexpected critical error logs handling.
if logger and handler:
handler.flush()
logger.removeHandler(handler)
def enable_console_logging() -> None:
if logger and handler:
logger.addHandler(handler)
def is_system_log_category(ctg: str) -> bool:
"""Check if the logging namespace belongs to system logs. Category starts
with the following name will be treated as system logs.
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)