import os
import re
from dataclasses import dataclass
VALID_LOG_LEVELS = ["critical", "error", "warn", "info", "debug", "null"]
VALID_BOOLEAN = ["true", "false", "1", "0"]
VALID_CYCLE = ["daily", "weekly", "monthly", "yearly"]
MAX_STRING_LENGTH = 256
COMPONENT_NAME = "sd"
ALL_COMPONENT_NAME = "*"
MAX_FILE_SIZE = 500
MAX_ROTATE_DAY = 180
MAX_FILE_NUM = 64
def parser_env_to_dict(mindie_log_level, valid_keys=None):
log_level = {}
modules = mindie_log_level.split(';')
for module in modules:
module = module.strip()
if ":" in module:
module_name, level = module.split(':')[:2]
level = level.strip().lower()
if valid_keys is None or level in valid_keys:
log_level[module_name.strip()] = level
module = module.lower()
if valid_keys is None or module in valid_keys:
log_level[ALL_COMPONENT_NAME] = module
return log_level
def check_string_valid(env_str: str) -> bool:
invalid_chars = {
'\f', '\r', '\b', '\t', '\v', '\n',
'\u000A', '\u000D', '\u000C', '\u000B',
'\u0008', '\u007F', '\u0009'
}
for char in invalid_chars:
if char in env_str:
return False
return True
@dataclass
class EnvVar:
"""
Environment variable.
"""
mindie_log_level: str = os.getenv("MINDIE_LOG_LEVEL", "info")
mindie_log_stdout: str = os.getenv("MINDIE_LOG_TO_STDOUT", "true")
mindie_log_to_file: str = os.getenv("MINDIE_LOG_TO_FILE", "true")
mindie_log_path: str = os.getenv("MINDIE_LOG_PATH", "~/mindie/log")
mindie_log_verbose: str = os.getenv("MINDIE_LOG_VERBOSE", "true")
mindie_log_rotate: str = os.getenv("MINDIE_LOG_ROTATE", "-s 30 -fs 20 -r 10")
disable_log: bool = False
component_log_level: str = "info"
component_log_stdout: str = "true"
component_log_to_file: str = "true"
component_log_path: str = "~/mindie/log/"
component_log_verbose: str = "true"
rotate_cycle: str = "daily"
rotate_cycle_num: int = 30
rotate_max_file_size: int = 20
rotate_max_file_num: int = 10
def __post_init__(self):
self._check()
log_level = parser_env_to_dict(self.mindie_log_level, VALID_LOG_LEVELS)
if log_level.get(ALL_COMPONENT_NAME, "") == "null" or log_level.get(COMPONENT_NAME, "") == "null":
self.disable_log = True
return
self.component_log_level = log_level.get(COMPONENT_NAME,
log_level.get(ALL_COMPONENT_NAME, self.component_log_level))
log_stdout = parser_env_to_dict(self.mindie_log_stdout, VALID_BOOLEAN)
self.component_log_stdout = log_stdout.get(COMPONENT_NAME,
log_stdout.get(ALL_COMPONENT_NAME, self.component_log_stdout))
log_to_file = parser_env_to_dict(self.mindie_log_to_file, VALID_BOOLEAN)
self.component_log_to_file = log_to_file.get(COMPONENT_NAME,
log_to_file.get(ALL_COMPONENT_NAME, self.component_log_to_file))
log_path = parser_env_to_dict(self.mindie_log_path)
self.component_log_path = log_path.get(COMPONENT_NAME,
log_path.get(ALL_COMPONENT_NAME, self.component_log_path))
log_verbose = parser_env_to_dict(self.mindie_log_verbose, VALID_BOOLEAN)
self.component_log_verbose = log_verbose.get(COMPONENT_NAME,
log_verbose.get(ALL_COMPONENT_NAME, self.component_log_verbose))
log_rotate = parser_env_to_dict(self.mindie_log_rotate)
component_log_rotate = log_rotate.get(COMPONENT_NAME,
log_rotate.get(ALL_COMPONENT_NAME, None))
self._get_rotate_parameter(component_log_rotate)
def _check(self):
if len(self.mindie_log_level) > MAX_STRING_LENGTH:
raise ValueError(f"The length of the environment variable MINDIE_LOG_LEVEL " \
f"[{len(self.mindie_log_level)}] is > {MAX_STRING_LENGTH}.")
if not check_string_valid(self.mindie_log_level):
raise ValueError(f"The environment variable MINDIE_LOG_LEVEL is invalid!")
if len(self.mindie_log_stdout) > MAX_STRING_LENGTH:
raise ValueError(f"The length of the environment variable MINDIE_LOG_TO_STDOUT" \
f"[{len(self.mindie_log_stdout)}] is > {MAX_STRING_LENGTH}.")
if not check_string_valid(self.mindie_log_stdout):
raise ValueError(f"The environment variable MINDIE_LOG_TO_STDOUT is invalid!")
if len(self.mindie_log_to_file) > MAX_STRING_LENGTH:
raise ValueError(f"The length of the environment variable MINDIE_LOG_TO_FILE" \
f"[{len(self.mindie_log_to_file)}] is > {MAX_STRING_LENGTH}.")
if not check_string_valid(self.mindie_log_to_file):
raise ValueError(f"The environment variable MINDIE_LOG_TO_FILE is invalid!")
if len(self.mindie_log_path) > MAX_STRING_LENGTH:
raise ValueError(f"The length of the environment variable MINDIE_LOG_PATH" \
f"[{len(self.mindie_log_path)}] is > {MAX_STRING_LENGTH}.")
if not check_string_valid(self.mindie_log_path):
raise ValueError(f"The environment variable MINDIE_LOG_PATH is invalid!")
if len(self.mindie_log_verbose) > MAX_STRING_LENGTH:
raise ValueError(f"The length of the environment variable MINDIE_LOG_VERBOSE" \
f"[{len(self.mindie_log_verbose)}] is > {MAX_STRING_LENGTH}.")
if not check_string_valid(self.mindie_log_verbose):
raise ValueError(f"The environment variable MINDIE_LOG_VERBOSE is invalid!")
if len(self.mindie_log_rotate) > MAX_STRING_LENGTH:
raise ValueError(f"The length of the environment variable MINDIE_LOG_ROTATE" \
f"[{len(self.mindie_log_rotate)}] is > {MAX_STRING_LENGTH}.")
if not check_string_valid(self.mindie_log_rotate):
raise ValueError(f"The environment variable MINDIE_LOG_ROTATE is invalid!")
def _get_rotate_parameter(self, component_log_rotate):
s_match = re.search(r'-s (\d+)', component_log_rotate)
if s_match is not None:
self.rotate_cycle_num = int(s_match.group(1))
if self.rotate_cycle_num < 1 or self.rotate_cycle_num > MAX_ROTATE_DAY:
raise ValueError(f"The number of days for log rotation must be in range [1, {MAX_ROTATE_DAY}], "
f"but got {self.rotate_cycle_num}.")
s_match = re.search(r'-s ([a-z]+)', component_log_rotate)
if s_match is not None and s_match.group(1) in VALID_CYCLE:
self.rotate_cycle = s_match.group(1)
self.rotate_cycle_num = 1
fs_match = re.search(r'-fs (\d+)', component_log_rotate)
if fs_match is not None:
self.rotate_max_file_size = int(fs_match.group(1))
if self.rotate_max_file_size < 1 or self.rotate_max_file_size > MAX_FILE_SIZE:
raise ValueError(f"The size of the log rotation file must be in range [1, {MAX_FILE_SIZE}]MB, "
f"but got {self.rotate_max_file_size}MB.")
fc_match = re.search(r'-r (\d+)', component_log_rotate)
if fc_match is not None:
self.rotate_max_file_num = int(fc_match.group(1))
if self.rotate_max_file_num < 1 or self.rotate_max_file_num > MAX_FILE_NUM:
raise ValueError(f"The number of the log rotation file must be in range [1, {MAX_FILE_NUM}], "
f"but got {self.rotate_max_file_num}.")
ENV = EnvVar()