ferrero-opentext/Python-Version/venv/lib/python3.12/site-packages/boxsdk/util/log.py

115 lines
3.7 KiB
Python

import logging
import re
import sys
from collections.abc import Mapping
from typing import Union, IO, Optional
_no_logger = object()
class Logging:
_has_setup = False
KEYS_TO_SANITIZE = (
'Authorization',
'access_token',
'refresh_token',
'subject_token',
'token',
'client_id',
'client_secret',
'code',
'shared_link',
'download_url',
'jwt_private_key',
'jwt_private_key_passphrase',
'password',
)
PROXY_KEYS_TO_SANITIZE = (
'http',
'https',
)
def setup_logging(self, stream_or_file=_no_logger, debug=False, name=None):
if not self._has_setup:
self._has_setup = True
self._setup_logging(stream_or_file, debug, name)
@staticmethod
def _setup_logging(stream_or_file=_no_logger, debug=False, name=None):
logger = logging.getLogger(name)
if isinstance(stream_or_file, str):
logger.addHandler(logging.FileHandler(stream_or_file, mode='a'))
elif stream_or_file is not _no_logger:
logger.addHandler(logging.StreamHandler(stream_or_file or sys.stdout))
logger.setLevel(logging.DEBUG if debug else logging.INFO)
@staticmethod
def sanitize_value(value):
return f'---{value[-4:]}'
@staticmethod
def sanitize_proxy_value(value: str) -> str:
return re.sub(
'^(.*://)(.*):(.*)(@.*)$',
lambda repl: f'{repl.group(1)}{"---"}:{"---"}{repl.group(4)}',
value
)
def sanitize_dictionary(self, dictionary: Mapping) -> Mapping:
if not isinstance(dictionary, Mapping):
return dictionary
sanitized_dictionary = {}
for key, value in dictionary.items():
if key in self.KEYS_TO_SANITIZE and isinstance(value, str):
sanitized_dictionary[key] = self.sanitize_value(value)
elif key in self.PROXY_KEYS_TO_SANITIZE and isinstance(value, str):
sanitized_dictionary[key] = self.sanitize_proxy_value(value)
elif isinstance(value, Mapping):
sanitized_dictionary[key] = self.sanitize_dictionary(value)
else:
sanitized_dictionary[key] = value
return sanitized_dictionary
_logging = Logging()
def setup_logging(
stream_or_file: Optional[Union[str, IO]] = _no_logger,
debug: Optional[bool] = False,
name: Optional[str] = None
) -> None:
"""
Create a logger for communicating with the user or writing to log files.
Sets the level to INFO or DEBUG, depending on the debug flag.
If a stream or file is passed (or None is passed to stream_or_file), then
a handler to that stream or file (stdout for None) is added to the logger.
:param stream_or_file:
The destination of the log messages. If None, stdout will be used.
:param debug:
Whether or not the logger will be at the DEBUG level (if False, the logger will be at the INFO level).
:param name:
The logging channel. If None, a root logger will be created.
"""
_logging.setup_logging(stream_or_file, debug, name)
def sanitize_dictionary(dictionary: Mapping) -> dict:
"""
Get a copy of a dictionary that has sensitive information redacted. Should be called on objects that will be
logged or printed.
:param dictionary: Dictionary that may contain sensitive information.
:return: Copy of the dictionary with sensitive information redacted.
"""
return _logging.sanitize_dictionary(dictionary)
logging.getLogger(__name__).addHandler(logging.NullHandler())
__all__ = ['setup_logging', 'sanitize_dictionary']