diff --git a/fedn/cli/run_cmd.py b/fedn/cli/run_cmd.py index 54958db2f..c6d88381e 100644 --- a/fedn/cli/run_cmd.py +++ b/fedn/cli/run_cmd.py @@ -102,16 +102,15 @@ def run_cmd(ctx): @click.option('-tr', '--trainer', required=False, default=True) @click.option('-in', '--init', required=False, default=None, help='Set to a filename to (re)init client from file state.') -@click.option('-l', '--logfile', required=False, default='{}-client.log'.format(time.strftime("%Y%m%d-%H%M%S")), +@click.option('-l', '--logfile', required=False, default=None, help='Set logfile for client log to file.') @click.option('--heartbeat-interval', required=False, default=2) @click.option('--reconnect-after-missed-heartbeat', required=False, default=30) @click.option('--verbosity', required=False, default='INFO', type=click.Choice(['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG'], case_sensitive=False)) -@click.option('--theme', required=False, default='default', type=click.Choice(['dark', 'light', 'default'], case_sensitive=False)) @click.pass_context def client_cmd(ctx, discoverhost, discoverport, token, name, client_id, local_package, force_ssl, dry_run, secure, preshared_cert, verify, preferred_combiner, validator, trainer, init, logfile, heartbeat_interval, reconnect_after_missed_heartbeat, - verbosity, theme): + verbosity): """ :param ctx: @@ -131,7 +130,6 @@ def client_cmd(ctx, discoverhost, discoverport, token, name, client_id, local_pa :param hearbeat_interval :param reconnect_after_missed_heartbeat :param verbosity - :param theme :return: """ remote = False if local_package else True @@ -139,7 +137,7 @@ def client_cmd(ctx, discoverhost, discoverport, token, name, client_id, local_pa 'client_id': client_id, 'remote_compute_context': remote, 'force_ssl': force_ssl, 'dry_run': dry_run, 'secure': secure, 'preshared_cert': preshared_cert, 'verify': verify, 'preferred_combiner': preferred_combiner, 'validator': validator, 'trainer': trainer, 'init': init, 'logfile': logfile, 'heartbeat_interval': heartbeat_interval, - 'reconnect_after_missed_heartbeat': reconnect_after_missed_heartbeat, 'verbosity': verbosity, 'theme': theme} + 'reconnect_after_missed_heartbeat': reconnect_after_missed_heartbeat, 'verbosity': verbosity} if init: apply_config(config) diff --git a/fedn/fedn/common/color_handler.py b/fedn/fedn/common/color_handler.py deleted file mode 100644 index 1708f3ee3..000000000 --- a/fedn/fedn/common/color_handler.py +++ /dev/null @@ -1,49 +0,0 @@ -import logging - -from termcolor import colored - - -class ColorizingStreamHandler(logging.StreamHandler): - dark_theme = { - 'DEBUG': 'white', - 'INFO': 'green', - 'WARNING': 'yellow', - 'ERROR': 'red', - 'CRITICAL': 'red', - } - - light_theme = { - 'DEBUG': 'black', - 'INFO': 'blue', - 'WARNING': 'magenta', - 'ERROR': 'red', - 'CRITICAL': 'red', - } - - def __init__(self, theme='dark'): - super().__init__() - self.set_theme(theme) - - def set_theme(self, theme): - if theme == 'dark': - self.color_map = self.dark_theme - elif theme == 'light': - self.color_map = self.light_theme - elif theme == 'default': - self.color_map = {} # No color applied - else: - self.color_map = {} # No color applied - - def emit(self, record): - try: - # Separate the log level from the message - level = '[{}]'.format(record.levelname) - color = self.color_map.get(record.levelname, 'white') - colored_level = colored(level, color) - - # Combine the colored log level with the rest of the message - message = self.format(record).replace(level, colored_level) - self.stream.write(message + "\n") - self.flush() - except Exception: - self.handleError(record) diff --git a/fedn/fedn/common/log_config.py b/fedn/fedn/common/log_config.py index 22368818e..09d7368d6 100644 --- a/fedn/fedn/common/log_config.py +++ b/fedn/fedn/common/log_config.py @@ -3,12 +3,10 @@ import urllib3 -from fedn.common.color_handler import ColorizingStreamHandler - urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) logging.getLogger("urllib3").setLevel(logging.ERROR) -handler = ColorizingStreamHandler(theme='dark') +handler = logging.StreamHandler() logger = logging.getLogger() logger.addHandler(handler) logger.setLevel(logging.DEBUG) @@ -38,15 +36,20 @@ def set_log_level_from_string(level_str): # Set the log level logger.setLevel(level) - -def set_theme_from_string(theme_str): +def set_log_stream(log_file): """ - Set the logging color theme based on a string input. + Redirect the log stream to a specified file, if log_file is set. """ - # Check if the theme string is valid - valid_themes = ['dark', 'light', 'default'] - if theme_str.lower() not in valid_themes: - raise ValueError(f"Invalid theme: {theme_str}. Valid themes are: {', '.join(valid_themes)}") + if not log_file: + return + + # Remove existing handlers + for h in logger.handlers[:]: + logger.removeHandler(h) + + # Create a FileHandler + file_handler = logging.FileHandler(log_file) + file_handler.setFormatter(formatter) - # Set the theme for the ColorizingStreamHandler - handler.set_theme(theme_str.lower()) + # Add the file handler to the logger + logger.addHandler(file_handler) \ No newline at end of file diff --git a/fedn/fedn/network/clients/client.py b/fedn/fedn/network/clients/client.py index 7dadb91db..bebf8ba5a 100644 --- a/fedn/fedn/network/clients/client.py +++ b/fedn/fedn/network/clients/client.py @@ -20,7 +20,7 @@ import fedn.common.net.grpc.fedn_pb2 as fedn import fedn.common.net.grpc.fedn_pb2_grpc as rpc from fedn.common.log_config import (logger, set_log_level_from_string, - set_theme_from_string) + set_log_stream) from fedn.network.clients.connect import ConnectorClient, Status from fedn.network.clients.package import PackageRuntime from fedn.network.clients.state import ClientState, ClientStateToString @@ -57,7 +57,7 @@ def __init__(self, config): self.config = config set_log_level_from_string(config.get('verbosity', "INFO")) - set_theme_from_string(config.get('theme', 'default')) + set_log_stream(config.get('logfile', None)) self.connector = ConnectorClient(host=config['discover_host'], port=config['discover_port'], @@ -79,8 +79,6 @@ def __init__(self, config): self.run_path = os.path.join(os.getcwd(), dirname) os.mkdir(self.run_path) - # self.logger = Logger( - # to_file=config['logfile'], file_path=self.run_path) self.started_at = datetime.now() self.logs = [] @@ -666,7 +664,7 @@ def _send_heartbeat(self, update_frequency=2.0): self._missed_heartbeat = 0 except grpc.RpcError as e: status_code = e.code() - logger.warning("CLIENT heartbeat: GRPC ERROR {} retrying..".format( + logger.warning("Client heartbeat: GRPC error, {}. Retrying.".format( status_code.name)) logger.debug(e) self._handle_combiner_failure() diff --git a/fedn/fedn/network/clients/package.py b/fedn/fedn/network/clients/package.py index 44a048ada..8a86bb6cc 100644 --- a/fedn/fedn/network/clients/package.py +++ b/fedn/fedn/network/clients/package.py @@ -126,7 +126,7 @@ def unpack(self): f = tarfile.open(os.path.join( self.pkg_path, self.pkg_name), 'r:bz2') else: - print( + logger.error( "Failed to unpack compute package, no pkg_name set." "Has the reducer been configured with a compute package?" ) @@ -138,11 +138,11 @@ def unpack(self): if f: f.extractall() - print("Successfully extracted compute package content in {}".format( + logger.info("Successfully extracted compute package content in {}".format( self.dir), flush=True) return True except Exception: - print("Error extracting files!") + logger.error("Error extracting files!") return False def dispatcher(self, run_path):