diff --git a/CHANGELOG.md b/CHANGELOG.md index 56d043ed5..3d84a4621 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). instead of $HOME. #1143 - Authentication mechanism to use bcrypt on server side. #1139 - Removed relevant dead code as reported by Vulture. #1149 +- Removed island logger config and added "log_level" to server config. #1151 ### Fixed - Attempted to delete a directory when monkey config reset was called. #1054 diff --git a/appimage/island_logger_config.json b/appimage/island_logger_config.json deleted file mode 100644 index 4acf875e3..000000000 --- a/appimage/island_logger_config.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "version": 1, - "disable_existing_loggers": false, - "formatters": { - "simple": { - "format": "%(asctime)s - %(filename)s:%(lineno)s - %(funcName)10s() - %(levelname)s - %(message)s" - } - }, - "handlers": { - "console": { - "class": "logging.StreamHandler", - "level": "DEBUG", - "formatter": "simple", - "stream": "ext://sys.stdout" - }, - "info_file_handler": { - "class": "logging.handlers.RotatingFileHandler", - "level": "INFO", - "formatter": "simple", - "filename": "~/.monkey_island/info.log", - "maxBytes": 10485760, - "backupCount": 20, - "encoding": "utf8" - } - }, - "root": { - "level": "DEBUG", - "handlers": [ - "console", - "info_file_handler" - ] - } -} diff --git a/monkey/monkey_island.py b/monkey/monkey_island.py index d32019b6a..5363ac5de 100644 --- a/monkey/monkey_island.py +++ b/monkey/monkey_island.py @@ -5,8 +5,10 @@ from monkey_island.cc.arg_parser import parse_cli_args gevent_monkey.patch_all() import json # noqa: E402 +import os # noqa: E402 -from monkey_island.cc.server_utils.island_logger import json_setup_logging # noqa: E402 +from monkey_island import config_loader # noqa: E402 +from monkey_island.cc.server_utils.island_logger import setup_logging # noqa: E402 if "__main__" == __name__: island_args = parse_cli_args() @@ -14,9 +16,18 @@ if "__main__" == __name__: # This is here in order to catch EVERYTHING, some functions are being called on # imports, so the log init needs to be first. try: - json_setup_logging(island_args.logger_config) + server_config_path = os.path.expanduser(island_args.server_config) + + config = config_loader.load_server_config_from_file(server_config_path) + + setup_logging(config["data_dir"], config["log_level"]) + + except OSError as ex: + print(f"Error opening server config file: {ex}") + exit(1) + except json.JSONDecodeError as ex: - print(f"Error loading logging config: {ex}") + print(f"Error loading server config: {ex}") exit(1) from monkey_island.cc.main import main # noqa: E402 diff --git a/monkey/monkey_island/cc/arg_parser.py b/monkey/monkey_island/cc/arg_parser.py index 91a2b7d25..6e12ef38f 100644 --- a/monkey/monkey_island/cc/arg_parser.py +++ b/monkey/monkey_island/cc/arg_parser.py @@ -1,16 +1,12 @@ from dataclasses import dataclass -from monkey_island.cc.server_utils.consts import ( - DEFAULT_LOGGER_CONFIG_PATH, - DEFAULT_SERVER_CONFIG_PATH, -) +from monkey_island.cc.server_utils.consts import DEFAULT_SERVER_CONFIG_PATH @dataclass class IslandArgs: setup_only: bool server_config: str - logger_config: str def parse_cli_args() -> IslandArgs: @@ -34,12 +30,6 @@ def parse_cli_args() -> IslandArgs: help="The path to the server configuration file.", default=DEFAULT_SERVER_CONFIG_PATH, ) - parser.add_argument( - "--logger-config", - action="store", - help="The path to the logging configuration file.", - default=DEFAULT_LOGGER_CONFIG_PATH, - ) args = parser.parse_args() - return IslandArgs(args.setup_only, args.server_config, args.logger_config) + return IslandArgs(args.setup_only, args.server_config) diff --git a/monkey/monkey_island/cc/island_logger_default_config.json b/monkey/monkey_island/cc/island_logger_default_config.json deleted file mode 100644 index 522177cda..000000000 --- a/monkey/monkey_island/cc/island_logger_default_config.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "version": 1, - "disable_existing_loggers": false, - "formatters": { - "simple": { - "format": "%(asctime)s - %(filename)s:%(lineno)s - %(funcName)10s() - %(levelname)s - %(message)s" - } - }, - "handlers": { - "console": { - "class": "logging.StreamHandler", - "level": "DEBUG", - "formatter": "simple", - "stream": "ext://sys.stdout" - }, - "info_file_handler": { - "class": "logging.handlers.RotatingFileHandler", - "level": "INFO", - "formatter": "simple", - "filename": "info.log", - "maxBytes": 10485760, - "backupCount": 20, - "encoding": "utf8" - } - }, - "root": { - "level": "DEBUG", - "handlers": [ - "console", - "info_file_handler" - ] - } -} \ No newline at end of file diff --git a/monkey/monkey_island/cc/server_config.json.develop b/monkey/monkey_island/cc/server_config.json.develop index ecc4c1708..33fb33487 100644 --- a/monkey/monkey_island/cc/server_config.json.develop +++ b/monkey/monkey_island/cc/server_config.json.develop @@ -1,4 +1,5 @@ { "server_config": "password", - "deployment": "develop" + "deployment": "develop", + "log_level": "DEBUG" } diff --git a/monkey/monkey_island/cc/server_utils/consts.py b/monkey/monkey_island/cc/server_utils/consts.py index 67c7209eb..f0dba26dc 100644 --- a/monkey/monkey_island/cc/server_utils/consts.py +++ b/monkey/monkey_island/cc/server_utils/consts.py @@ -11,8 +11,4 @@ DEFAULT_DEVELOP_SERVER_CONFIG_PATH = os.path.join( MONKEY_ISLAND_ABS_PATH, "cc", "server_config.json.develop" ) -DEFAULT_LOGGER_CONFIG_PATH = os.path.join( - MONKEY_ISLAND_ABS_PATH, "cc", "island_logger_default_config.json" -) - DEFAULT_DATA_DIR = os.path.join(MONKEY_ISLAND_ABS_PATH, "cc") diff --git a/monkey/monkey_island/cc/server_utils/island_logger.py b/monkey/monkey_island/cc/server_utils/island_logger.py index a32f6505f..c05915bc8 100644 --- a/monkey/monkey_island/cc/server_utils/island_logger.py +++ b/monkey/monkey_island/cc/server_utils/island_logger.py @@ -1,43 +1,53 @@ -import json import logging.config import os -from typing import Dict +from copy import deepcopy -from monkey_island.cc.server_utils.consts import DEFAULT_LOGGER_CONFIG_PATH +ISLAND_LOG_FILENAME = "monkey_island.log" -__author__ = "Maor.Rayzin" +LOGGER_CONFIG_DICT = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "simple": { + "format": "%(asctime)s - %(filename)s:%(lineno)s - " + + "%(funcName)10s() - %(levelname)s - %(message)s" + } + }, + "handlers": { + "console_handler": { + "class": "logging.StreamHandler", + "formatter": "simple", + "stream": "ext://sys.stdout", + }, + "file_handler": { + "class": "logging.handlers.RotatingFileHandler", + "formatter": "simple", + "filename": None, # set in setup_logging() + "maxBytes": 10485760, + "backupCount": 20, + "encoding": "utf8", + }, + }, + "root": { + "level": None, # set in setup_logging() + "handlers": ["console_handler", "file_handler"], + }, +} -def json_setup_logging( - default_path=DEFAULT_LOGGER_CONFIG_PATH, - default_level=logging.INFO, - env_key="LOG_CFG", -): +def setup_logging(data_dir_path, log_level): """ Setup the logging configuration - :param default_path: the default log configuration file path - :param default_level: Default level to log from - :param env_key: SYS ENV key to use for external configuration file path + :param data_dir_path: data directory file path + :param log_level: level to log from :return: """ - path = os.path.expanduser(default_path) - value = os.getenv(env_key, None) - if value: - path = value + logger_configuration = deepcopy(LOGGER_CONFIG_DICT) - if os.path.exists(path): - with open(path, "rt") as f: - config = json.load(f) - _expanduser_log_file_paths(config) - logging.config.dictConfig(config) - else: - logging.basicConfig(level=default_level) + logger_configuration["handlers"]["file_handler"]["filename"] = os.path.join( + data_dir_path, ISLAND_LOG_FILENAME + ) + logger_configuration["root"]["level"] = log_level.upper() - -def _expanduser_log_file_paths(config: Dict): - handlers = config.get("handlers", {}) - - for handler_settings in handlers.values(): - if "filename" in handler_settings: - handler_settings["filename"] = os.path.expanduser(handler_settings["filename"]) + logging.config.dictConfig(logger_configuration) diff --git a/monkey/monkey_island/config_loader.py b/monkey/monkey_island/config_loader.py new file mode 100644 index 000000000..aaa9185d7 --- /dev/null +++ b/monkey/monkey_island/config_loader.py @@ -0,0 +1,25 @@ +import json +import os + +from monkey_island.cc.server_utils.consts import DEFAULT_DATA_DIR + +DEFAULT_LOG_LEVEL = "INFO" + + +def load_server_config_from_file(server_config_path): + with open(server_config_path, "r") as f: + config_content = f.read() + config = json.loads(config_content) + add_default_values_to_config(config) + + return config + + +def add_default_values_to_config(config): + config["data_dir"] = os.path.abspath( + os.path.expanduser(os.path.expandvars(config.get("data_dir", DEFAULT_DATA_DIR))) + ) + + config.setdefault("log_level", DEFAULT_LOG_LEVEL) + + return config diff --git a/monkey/tests/conftest.py b/monkey/tests/conftest.py index 81806ef69..328cb109c 100644 --- a/monkey/tests/conftest.py +++ b/monkey/tests/conftest.py @@ -46,3 +46,20 @@ def with_data_dir(environment_resources_dir): @pytest.fixture(scope="session") def with_data_dir_home(environment_resources_dir): return os.path.join(environment_resources_dir, "server_config_with_data_dir_home.json") + + +@pytest.fixture(scope="session") +def server_config_resources_dir(resources_dir): + return os.path.join(resources_dir, "server_configs") + + +@pytest.fixture(scope="session") +def test_server_config(server_config_resources_dir): + return os.path.join(server_config_resources_dir, "test_server_config.json") + + +@pytest.fixture +def mock_home_env(monkeypatch, tmpdir): + monkeypatch.setenv("HOME", str(tmpdir)) + + return tmpdir diff --git a/monkey/tests/monkey_island/cc/server_utils/test_island_logger.py b/monkey/tests/monkey_island/cc/server_utils/test_island_logger.py index 57f0cc5b0..9f4e59af8 100644 --- a/monkey/tests/monkey_island/cc/server_utils/test_island_logger.py +++ b/monkey/tests/monkey_island/cc/server_utils/test_island_logger.py @@ -1,32 +1,80 @@ import logging import os -import pytest - -from monkey_island.cc.server_utils.island_logger import json_setup_logging +from monkey_island.cc.server_utils.island_logger import ISLAND_LOG_FILENAME, setup_logging -@pytest.fixture() -def test_logger_config_path(resources_dir): - return os.path.join(resources_dir, "logger_config.json") +def test_setup_logging_file_log_level_debug(tmpdir): + DATA_DIR = tmpdir + LOG_FILE = os.path.join(DATA_DIR, ISLAND_LOG_FILENAME) + LOG_LEVEL = "DEBUG" + TEST_STRING = "Hello, Monkey! (File; Log level: debug)" - -# TODO move into monkey/monkey_island/cc/test_common/fixtures after rebase/backmerge -@pytest.fixture -def mock_home_env(monkeypatch, tmpdir): - monkeypatch.setenv("HOME", str(tmpdir)) - - -def test_expanduser_filename(mock_home_env, tmpdir, test_logger_config_path): - INFO_LOG = os.path.join(tmpdir, "info.log") - TEST_STRING = "Hello, Monkey!" - - json_setup_logging(test_logger_config_path) + setup_logging(DATA_DIR, LOG_LEVEL) logger = logging.getLogger("TestLogger") - logger.info(TEST_STRING) + logger.debug(TEST_STRING) - assert os.path.isfile(INFO_LOG) - with open(INFO_LOG, "r") as f: + assert os.path.isfile(LOG_FILE) + with open(LOG_FILE, "r") as f: line = f.readline() assert TEST_STRING in line + + +def test_setup_logging_file_log_level_info(tmpdir): + DATA_DIR = tmpdir + LOG_FILE = os.path.join(DATA_DIR, ISLAND_LOG_FILENAME) + LOG_LEVEL = "INFO" + TEST_STRING = "Hello, Monkey! (File; Log level: info)" + + setup_logging(DATA_DIR, LOG_LEVEL) + + logger = logging.getLogger("TestLogger") + logger.debug(TEST_STRING) + + assert os.path.isfile(LOG_FILE) + with open(LOG_FILE, "r") as f: + line = f.readline() + assert TEST_STRING not in line + + +def test_setup_logging_console_log_level_debug(capsys, tmpdir): + DATA_DIR = tmpdir + LOG_LEVEL = "DEBUG" + TEST_STRING = "Hello, Monkey! (Console; Log level: debug)" + + setup_logging(DATA_DIR, LOG_LEVEL) + + logger = logging.getLogger("TestLogger") + logger.debug(TEST_STRING) + + captured = capsys.readouterr() + assert TEST_STRING in captured.out + + +def test_setup_logging_console_log_level_info(capsys, tmpdir): + DATA_DIR = tmpdir + LOG_LEVEL = "INFO" + TEST_STRING = "Hello, Monkey! (Console; Log level: info)" + + setup_logging(DATA_DIR, LOG_LEVEL) + + logger = logging.getLogger("TestLogger") + logger.debug(TEST_STRING) + + captured = capsys.readouterr() + assert TEST_STRING not in captured.out + + +def test_setup_logging_console_log_level_lower_case(capsys, tmpdir): + DATA_DIR = tmpdir + LOG_LEVEL = "debug" + TEST_STRING = "Hello, Monkey! (Console; Log level: debug)" + + setup_logging(DATA_DIR, LOG_LEVEL) + + logger = logging.getLogger("TestLogger") + logger.debug(TEST_STRING) + + captured = capsys.readouterr() + assert TEST_STRING in captured.out diff --git a/monkey/tests/monkey_island/test_config_loader.py b/monkey/tests/monkey_island/test_config_loader.py new file mode 100644 index 000000000..20c330f6a --- /dev/null +++ b/monkey/tests/monkey_island/test_config_loader.py @@ -0,0 +1,27 @@ +import os + +from monkey_island import config_loader +from monkey_island.cc.server_utils.consts import DEFAULT_DATA_DIR + + +def test_load_server_config_from_file(test_server_config, mock_home_env): + config = config_loader.load_server_config_from_file(test_server_config) + + assert config["data_dir"] == os.path.join(mock_home_env, ".monkey_island") + assert config["log_level"] == "NOTICE" + + +def test_default_log_level(): + test_config = {} + config = config_loader.add_default_values_to_config(test_config) + + assert "log_level" in config + assert config["log_level"] == "INFO" + + +def test_default_data_dir(mock_home_env): + test_config = {} + config = config_loader.add_default_values_to_config(test_config) + + assert "data_dir" in config + assert config["data_dir"] == DEFAULT_DATA_DIR diff --git a/monkey/tests/resources/server_configs/test_server_config.json b/monkey/tests/resources/server_configs/test_server_config.json new file mode 100644 index 000000000..25082b0b3 --- /dev/null +++ b/monkey/tests/resources/server_configs/test_server_config.json @@ -0,0 +1,4 @@ +{ + "data_dir": "~/.monkey_island", + "log_level": "NOTICE" +}