From dd1290e38e3f01cccf1959ea5155a30725a73814 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 19 Jul 2018 21:12:27 +0200 Subject: [PATCH] Add multiple verbosity levels --- freqtrade/arguments.py | 8 ++-- freqtrade/configuration.py | 29 +++++++++--- freqtrade/main.py | 12 +---- freqtrade/tests/test_arguments.py | 11 +++-- freqtrade/tests/test_configuration.py | 63 ++++++++++++++++++++++++++- freqtrade/tests/test_main.py | 25 ++--------- 6 files changed, 96 insertions(+), 52 deletions(-) diff --git a/freqtrade/arguments.py b/freqtrade/arguments.py index 16a01396b..022a2c739 100644 --- a/freqtrade/arguments.py +++ b/freqtrade/arguments.py @@ -3,7 +3,6 @@ This module contains the argument manager class """ import argparse -import logging import os import re from typing import List, NamedTuple, Optional @@ -64,11 +63,10 @@ class Arguments(object): """ self.parser.add_argument( '-v', '--verbose', - help='be verbose', - action='store_const', + help='verbose mode (-vv for more, -vvv to get all messages)', + action='count', dest='loglevel', - const=logging.DEBUG, - default=logging.INFO, + default=0, ) self.parser.add_argument( '--version', diff --git a/freqtrade/configuration.py b/freqtrade/configuration.py index f5c1c398d..dcc6e4332 100644 --- a/freqtrade/configuration.py +++ b/freqtrade/configuration.py @@ -12,10 +12,22 @@ from jsonschema import Draft4Validator, validate from jsonschema.exceptions import ValidationError, best_match from freqtrade import OperationalException, constants - logger = logging.getLogger(__name__) +def set_loggers(log_level: int = 0) -> None: + """ + Set the logger level for Third party libs + :return: None + """ + + logging.getLogger('requests').setLevel(logging.INFO if log_level <= 1 else logging.DEBUG) + logging.getLogger("urllib3").setLevel(logging.INFO if log_level <= 1 else logging.DEBUG) + logging.getLogger('ccxt.base.exchange').setLevel( + logging.INFO if log_level <= 2 else logging.DEBUG) + logging.getLogger('telegram').setLevel(logging.INFO) + + class Configuration(object): """ Class to read and init the bot configuration @@ -79,12 +91,15 @@ class Configuration(object): # Log level if 'loglevel' in self.args and self.args.loglevel: - config.update({'loglevel': self.args.loglevel}) - logging.basicConfig( - level=config['loglevel'], - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', - ) - logger.info('Log level set to %s', logging.getLevelName(config['loglevel'])) + config.update({'verbosity': self.args.loglevel}) + else: + config.update({'verbosity': 0}) + logging.basicConfig( + level=logging.INFO if config['verbosity'] < 1 else logging.DEBUG, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + ) + set_loggers(config['verbosity']) + logger.info('Verbosity set to %s', config['verbosity']) # Add dynamic_whitelist if found if 'dynamic_whitelist' in self.args and self.args.dynamic_whitelist: diff --git a/freqtrade/main.py b/freqtrade/main.py index 977212faf..3ed478ec3 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -10,7 +10,7 @@ from typing import List from freqtrade import OperationalException from freqtrade.arguments import Arguments -from freqtrade.configuration import Configuration +from freqtrade.configuration import Configuration, set_loggers from freqtrade.freqtradebot import FreqtradeBot from freqtrade.state import State from freqtrade.rpc import RPCMessageType @@ -84,16 +84,6 @@ def reconfigure(freqtrade: FreqtradeBot, args: Namespace) -> FreqtradeBot: return freqtrade -def set_loggers() -> None: - """ - Set the logger level for Third party libs - :return: None - """ - logging.getLogger('requests.packages.urllib3').setLevel(logging.INFO) - logging.getLogger('ccxt.base.exchange').setLevel(logging.INFO) - logging.getLogger('telegram').setLevel(logging.INFO) - - if __name__ == '__main__': set_loggers() main(sys.argv[1:]) diff --git a/freqtrade/tests/test_arguments.py b/freqtrade/tests/test_arguments.py index 8a41e3379..07018c79e 100644 --- a/freqtrade/tests/test_arguments.py +++ b/freqtrade/tests/test_arguments.py @@ -5,7 +5,6 @@ Unit test file for arguments.py """ import argparse -import logging import pytest @@ -35,7 +34,7 @@ def test_parse_args_defaults() -> None: args = Arguments([], '').get_parsed_arg() assert args.config == 'config.json' assert args.dynamic_whitelist is None - assert args.loglevel == logging.INFO + assert args.loglevel == 0 def test_parse_args_config() -> None: @@ -53,10 +52,10 @@ def test_parse_args_db_url() -> None: def test_parse_args_verbose() -> None: args = Arguments(['-v'], '').get_parsed_arg() - assert args.loglevel == logging.DEBUG + assert args.loglevel == 1 args = Arguments(['--verbose'], '').get_parsed_arg() - assert args.loglevel == logging.DEBUG + assert args.loglevel == 1 def test_scripts_options() -> None: @@ -153,7 +152,7 @@ def test_parse_args_backtesting_custom() -> None: call_args = Arguments(args, '').get_parsed_arg() assert call_args.config == 'test_conf.json' assert call_args.live is True - assert call_args.loglevel == logging.INFO + assert call_args.loglevel == 0 assert call_args.subparser == 'backtesting' assert call_args.func is not None assert call_args.ticker_interval == '1m' @@ -170,7 +169,7 @@ def test_parse_args_hyperopt_custom() -> None: call_args = Arguments(args, '').get_parsed_arg() assert call_args.config == 'test_conf.json' assert call_args.epochs == 20 - assert call_args.loglevel == logging.INFO + assert call_args.loglevel == 0 assert call_args.subparser == 'hyperopt' assert call_args.spaces == ['buy'] assert call_args.func is not None diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py index da8d3bebf..a8a2c5fce 100644 --- a/freqtrade/tests/test_configuration.py +++ b/freqtrade/tests/test_configuration.py @@ -6,6 +6,7 @@ Unit test file for configuration.py import json from argparse import Namespace from copy import deepcopy +import logging from unittest.mock import MagicMock import pytest @@ -13,7 +14,7 @@ from jsonschema import ValidationError from freqtrade import OperationalException from freqtrade.arguments import Arguments -from freqtrade.configuration import Configuration +from freqtrade.configuration import Configuration, set_loggers from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL from freqtrade.tests.conftest import log_has @@ -406,3 +407,63 @@ def test_check_exchange(default_conf) -> None: match=r'.*Exchange "unknown_exchange" not supported.*' ): configuration.check_exchange(conf) + + +def test_cli_verbose_with_params(default_conf, mocker, caplog) -> None: + """ + Test Configuration.load_config() with cli params used + """ + + mocker.patch('freqtrade.configuration.open', mocker.mock_open( + read_data=json.dumps(default_conf))) + # Prevent setting loggers + mocker.patch('freqtrade.configuration.set_loggers', MagicMock) + arglist = ['-vvv'] + args = Arguments(arglist, '').get_parsed_arg() + + configuration = Configuration(args) + validated_conf = configuration.load_config() + + assert validated_conf.get('verbosity') == 3 + assert log_has('Verbosity set to 3', caplog.record_tuples) + + +def test_set_loggers() -> None: + """ + Test set_loggers() update the logger level for third-party libraries + """ + # Reset Logging to Debug, otherwise this fails randomly as it's set globally + logging.getLogger('requests').setLevel(logging.DEBUG) + logging.getLogger("urllib3").setLevel(logging.DEBUG) + logging.getLogger('ccxt.base.exchange').setLevel(logging.DEBUG) + logging.getLogger('telegram').setLevel(logging.DEBUG) + + previous_value1 = logging.getLogger('requests').level + previous_value2 = logging.getLogger('ccxt.base.exchange').level + previous_value3 = logging.getLogger('telegram').level + + set_loggers() + + value1 = logging.getLogger('requests').level + assert previous_value1 is not value1 + assert value1 is logging.INFO + + value2 = logging.getLogger('ccxt.base.exchange').level + assert previous_value2 is not value2 + assert value2 is logging.INFO + + value3 = logging.getLogger('telegram').level + assert previous_value3 is not value3 + assert value3 is logging.INFO + + set_loggers(log_level=2) + + assert logging.getLogger('requests').level is logging.DEBUG + assert logging.getLogger('ccxt.base.exchange').level is logging.INFO + assert logging.getLogger('telegram').level is logging.INFO + + set_loggers(log_level=3) + + assert logging.getLogger('requests').level is logging.DEBUG + assert logging.getLogger('ccxt.base.exchange').level is logging.DEBUG + assert logging.getLogger('telegram').level is logging.INFO diff --git a/freqtrade/tests/test_main.py b/freqtrade/tests/test_main.py index 20a02eedc..446945a07 100644 --- a/freqtrade/tests/test_main.py +++ b/freqtrade/tests/test_main.py @@ -2,7 +2,6 @@ Unit test file for main.py """ -import logging from copy import deepcopy from unittest.mock import MagicMock @@ -11,7 +10,7 @@ import pytest from freqtrade import OperationalException from freqtrade.arguments import Arguments from freqtrade.freqtradebot import FreqtradeBot -from freqtrade.main import main, reconfigure, set_loggers +from freqtrade.main import main, reconfigure from freqtrade.state import State from freqtrade.tests.conftest import log_has, patch_exchange @@ -27,7 +26,7 @@ def test_parse_args_backtesting(mocker) -> None: call_args = backtesting_mock.call_args[0][0] assert call_args.config == 'config.json' assert call_args.live is False - assert call_args.loglevel == 20 + assert call_args.loglevel == 0 assert call_args.subparser == 'backtesting' assert call_args.func is not None assert call_args.ticker_interval is None @@ -42,29 +41,11 @@ def test_main_start_hyperopt(mocker) -> None: assert hyperopt_mock.call_count == 1 call_args = hyperopt_mock.call_args[0][0] assert call_args.config == 'config.json' - assert call_args.loglevel == 20 + assert call_args.loglevel == 0 assert call_args.subparser == 'hyperopt' assert call_args.func is not None -def test_set_loggers() -> None: - """ - Test set_loggers() update the logger level for third-party libraries - """ - previous_value1 = logging.getLogger('requests.packages.urllib3').level - previous_value2 = logging.getLogger('telegram').level - - set_loggers() - - value1 = logging.getLogger('requests.packages.urllib3').level - assert previous_value1 is not value1 - assert value1 is logging.INFO - - value2 = logging.getLogger('telegram').level - assert previous_value2 is not value2 - assert value2 is logging.INFO - - def test_main_fatal_exception(mocker, default_conf, caplog) -> None: """ Test main() function