Repository: incubator-ariatosca Updated Branches: refs/heads/ARIA-146-Support-colorful-execution-logging a82c3c024 -> b3a2b723c
review fixes Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/b3a2b723 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/b3a2b723 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/b3a2b723 Branch: refs/heads/ARIA-146-Support-colorful-execution-logging Commit: b3a2b723c9876aeea6167df454e95928c8769135 Parents: a82c3c0 Author: max-orlov <[email protected]> Authored: Thu Apr 27 15:00:25 2017 +0300 Committer: max-orlov <[email protected]> Committed: Thu Apr 27 15:00:25 2017 +0300 ---------------------------------------------------------------------- aria/__init__.py | 6 +- aria/cli/color.py | 69 ++++++----- aria/cli/config/config.py | 30 +++-- aria/cli/config/config_template.yaml | 21 ++-- aria/cli/execution_logging.py | 188 +++++++++++++++--------------- 5 files changed, 153 insertions(+), 161 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b3a2b723/aria/__init__.py ---------------------------------------------------------------------- diff --git a/aria/__init__.py b/aria/__init__.py index df75b1e..f77cb70 100644 --- a/aria/__init__.py +++ b/aria/__init__.py @@ -60,9 +60,9 @@ def install_aria_extensions(): for loader, module_name, _ in iter_modules(): if module_name.startswith('aria_extension_'): loader.find_module(module_name).load_module(module_name) - if pkg_resources: - for entry_point in pkg_resources.iter_entry_points(group='aria_extension'): - entry_point.load() + # if pkg_resources: + # for entry_point in pkg_resources.iter_entry_points(group='aria_extension'): + # entry_point.load() extension.init() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b3a2b723/aria/cli/color.py ---------------------------------------------------------------------- diff --git a/aria/cli/color.py b/aria/cli/color.py index 44ace8b..65835cc 100644 --- a/aria/cli/color.py +++ b/aria/cli/color.py @@ -20,21 +20,15 @@ import colorama colorama.init() -def _get_colors(color_type): - for name in dir(color_type): - if not name.startswith('_'): - yield (name.lower(), getattr(color_type, name)) - - -class StylizedString(object): - def __init__(self, str_, schema=None): +class StringStylizer(object): + def __init__(self, str_, color_spec=None): self._str = str_ - self._schema = schema + self._color_spec = color_spec def __repr__(self): - if self._schema: + if self._color_spec: return '{schema}{str}{reset}'.format( - schema=self._schema, str=str(self._str), reset=Color.Style.RESET_ALL) + schema=self._color_spec, str=str(self._str), reset=Colors.Style.RESET_ALL) return self._str def __add__(self, other): @@ -43,8 +37,8 @@ class StylizedString(object): def __radd__(self, other): return other + str(self) - def color(self, schema): - self._schema = schema + def color(self, color_spec): + self._color_spec = color_spec def replace(self, old, new, **kwargs): self._str = self._str.replace(str(old), str(new), **kwargs) @@ -56,10 +50,16 @@ class StylizedString(object): if pattern is None: return for match in set(re.findall(re.compile(pattern), self._str)): - self.replace(match, schema + match + Color.Style.RESET_ALL + self._schema) + self.replace(match, schema + match + Colors.Style.RESET_ALL + self._color_spec) + +def _get_colors(color_type): + for name in dir(color_type): + if not name.startswith('_'): + yield (name.lower(), getattr(color_type, name)) -class Color(object): + +class Colors(object): Fore = colorama.Fore Back = colorama.Back Style = colorama.Style @@ -70,29 +70,26 @@ class Color(object): 'style': dict(_get_colors(Style)) } - class Schema(object): - def __init__(self, fore=None, back=None, style=None): - """ - It is possible to provide fore, back and style arguments. each could be either - the color is lower case letter, or the actual color from colorama. - """ - self._kwargs = dict(fore=fore, back=back, style=style) - self._str = StringIO() - for type_, colors in Color._colors.items(): - value = self._kwargs.get(type_, None) - # the former case is if the value is a string, the latter is in case of an object. - self._str.write(colors.get(value) or value) +class Schema(object): + def __init__(self, fore=None, back=None, style=None): + """ + It is possible to provide fore, back and style arguments. each could be either + the color is lower case letter, or the actual color from colorama. - def __str__(self): - return self._str.getvalue() + """ + self._kwargs = dict(fore=fore, back=back, style=style) + self._str = StringIO() + for type_, colors in Colors._colors.items(): + value = self._kwargs.get(type_, None) + # the former case is if the value is a string, the latter is in case of an object. + self._str.write(colors.get(value) or value) - def __add__(self, other): - return str(self) + str(other) + def __str__(self): + return self._str.getvalue() - def __radd__(self, other): - return str(other) + str(self) + def __add__(self, other): + return str(self) + str(other) - @classmethod - def stylize(cls, *args, **kwargs): - return StylizedString(*args, **kwargs) + def __radd__(self, other): + return str(other) + str(self) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b3a2b723/aria/cli/config/config.py ---------------------------------------------------------------------- diff --git a/aria/cli/config/config.py b/aria/cli/config/config.py index d664518..f5afad6 100644 --- a/aria/cli/config/config.py +++ b/aria/cli/config/config.py @@ -52,10 +52,6 @@ class CliConfig(object): return cls(config_path) @property - def colors(self): - return self._config.get('colors', False) - - @property def logging(self): return self.Logging(self._config.get('logging')) @@ -73,13 +69,23 @@ class CliConfig(object): return self._logging.get('loggers', {}) @property - def styling_enabled(self): - return self.styles.get('enabled', False) + def execution(self): + return self.Execution(self._logging.get('execution')) - @property - def styles(self): - return self._logging.get('execution', {}).get('styles', {}) + class Execution(object): + + def __init__(self, execution_logging): + self._execution_logging = execution_logging + + @property + def colors_enabled(self): + return self.colors.get('enabled', False) + + @property + def colors(self): + return self._execution_logging.get('colors', {}) + + @property + def formats(self): + return self._execution_logging.get('formats', {}) - @property - def formats(self): - return self._logging.get('execution', {}).get('formats', {}) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b3a2b723/aria/cli/config/config_template.yaml ---------------------------------------------------------------------- diff --git a/aria/cli/config/config_template.yaml b/aria/cli/config/config_template.yaml index 85b72d1..94fcac3 100644 --- a/aria/cli/config/config_template.yaml +++ b/aria/cli/config/config_template.yaml @@ -18,30 +18,25 @@ logging: 2: '{timestamp:%H:%M:%S} | {level[0]} | {implementation} | {message}' 3: '{timestamp:%H:%M:%S} | {level[0]} | {implementation} | {inputs} | {message}' - styles: + colors: enabled: true level: - info: {'fore': 'lightmagenta_ex'} - debug: {'fore': 'lightmagenta_ex', 'style': 'dim'} + default: {'fore': 'lightmagenta_ex'} error: {'fore': 'red', 'style': 'bright'} timestamp: - info: {'fore': 'lightmagenta_ex'} - debug: {'fore': 'lightmagenta_ex', 'style': 'dim'} - error: {'fore': 'red', 'style': 'bright'} + default: {'fore': 'lightmagenta_ex'} + error: {'fore': 'red', 'style': 'bright'} message: - info: {'fore': 'lightblue_ex'} - debug: {'fore': 'lightblue_ex', 'style': 'dim'} + default: {'fore': 'lightblue_ex'} error: {'fore': 'red', 'style': 'bright'} implementation: - info: {'fore': 'lightblack_ex'} - debug: {'fore': 'lightblack_ex', 'style': 'dim'} + default: {'fore': 'lightblack_ex'} error: {'fore': 'red', 'style': 'bright'} inputs: - info: {'fore': 'blue'} - debug: {'fore': 'blue', 'style': 'dim'} + default: {'fore': 'blue'} error: {'fore': 'red', 'style': 'bright'} traceback: - error: {'fore': 'red'} + default: {'fore': 'red'} marker: 'lightyellow_ex' http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b3a2b723/aria/cli/execution_logging.py ---------------------------------------------------------------------- diff --git a/aria/cli/execution_logging.py b/aria/cli/execution_logging.py index 5cbf75a..e176862 100644 --- a/aria/cli/execution_logging.py +++ b/aria/cli/execution_logging.py @@ -15,9 +15,12 @@ import os import re from StringIO import StringIO +from functools import partial -from . import logger -from .color import Color +from . import ( + logger, + color +) from .env import env @@ -31,73 +34,67 @@ TRACEBACK = 'traceback' MARKER = 'marker' FINAL_STATES = 'final_states' -SUCCESS_STATE = 'success' -CANCEL_STATE = 'cancel' -FAIL_STATE = 'fail' +SUCCESS_STATE = 'succeeded' +CANCEL_STATE = 'canceled' +FAIL_STATE = 'failed' -_EXECUTION_BASE_PATTERN = "\'.*\' workflow execution " -_SUCCESSFUL_EXECUTION_PATTERN = _EXECUTION_BASE_PATTERN + "succeeded" -_FAILED_EXECUTION_PATTERN = _EXECUTION_BASE_PATTERN + "failed" -_CANCELED_EXECUTION_PATTERN = _EXECUTION_BASE_PATTERN + "canceled" - -_TIMESTAMP_PATTERN = '.*({timestamp.*?}).*' -_LEVEL_PATTERN = '.*({level.*?}).*' -_MESSAGE_PATTERN = '.*({message.*?}).*' -_IMPLEMENTATION_PATTERN = '.*({implementation.*?}).*' -_INPUTS_PATTERN = '.*({inputs.*?}).*' +_EXECUTION_PATTERN = "\'.*\' workflow execution {0}".format +_FIELD_TYPE_PATTERN = partial('.*({starting}{0}{closing}).*'.format, starting='{', closing='.*?}') _PATTERNS = { FINAL_STATES: { - SUCCESS_STATE: re.compile(_SUCCESSFUL_EXECUTION_PATTERN), - CANCEL_STATE: re.compile(_CANCELED_EXECUTION_PATTERN), - FAIL_STATE: re.compile(_FAILED_EXECUTION_PATTERN), + SUCCESS_STATE: re.compile(_EXECUTION_PATTERN(SUCCESS_STATE)), + CANCEL_STATE: re.compile(_EXECUTION_PATTERN(CANCEL_STATE)), + FAIL_STATE: re.compile(_EXECUTION_PATTERN(FAIL_STATE)), }, FIELD_TYPE: { - IMPLEMENTATION: re.compile(_IMPLEMENTATION_PATTERN), - LEVEL: re.compile(_LEVEL_PATTERN), - MESSAGE: re.compile(_MESSAGE_PATTERN), - INPUTS: re.compile(_INPUTS_PATTERN), - TIMESTAMP: re.compile(_TIMESTAMP_PATTERN) + IMPLEMENTATION: re.compile(_FIELD_TYPE_PATTERN(IMPLEMENTATION)), + LEVEL: re.compile(_FIELD_TYPE_PATTERN(LEVEL)), + MESSAGE: re.compile(_FIELD_TYPE_PATTERN(MESSAGE)), + INPUTS: re.compile(_FIELD_TYPE_PATTERN(INPUTS)), + TIMESTAMP: re.compile(_FIELD_TYPE_PATTERN(TIMESTAMP)) } } _FINAL_STATES = { - SUCCESS_STATE: Color.Fore.GREEN, - CANCEL_STATE: Color.Fore.YELLOW, - FAIL_STATE: Color.Fore.RED + SUCCESS_STATE: color.Colors.Fore.GREEN, + CANCEL_STATE: color.Colors.Fore.YELLOW, + FAIL_STATE: color.Colors.Fore.RED } -_DEFAULT_STYLE = { +_DEFAULT_COLORS = { LEVEL: { - 'info': {'fore': 'lightmagenta_ex'}, - 'debug': {'fore': 'lightmagenta_ex', 'style': 'dim'}, + 'default': {'fore': 'lightmagenta_ex'}, 'error': {'fore': 'red', 'style': 'bright'}, }, TIMESTAMP: { - 'info': {'fore': 'lightmagenta_ex'}, - 'debug': {'fore': 'lightmagenta_ex', 'style': 'dim'}, + 'default': {'fore': 'lightmagenta_ex'}, 'error': {'fore': 'red', 'style': 'bright'}, }, MESSAGE: { - 'info': {'fore': 'lightblue_ex'}, - 'debug': {'fore': 'lightblue_ex', 'style': 'dim'}, + 'default': {'fore': 'lightblue_ex'}, 'error': {'fore': 'red', 'style': 'bright'}, }, IMPLEMENTATION:{ - 'info': {'fore': 'lightblack_ex'}, - 'debug': {'fore': 'lightblack_ex', 'style': 'dim'}, + 'default': {'fore': 'lightblack_ex'}, 'error': {'fore': 'red', 'style': 'bright'}, }, INPUTS: { - 'info': {'fore': 'blue'}, - 'debug': {'fore': 'blue', 'style': 'dim'}, + 'default': {'fore': 'blue'}, 'error': {'fore': 'red', 'style': 'bright'}, }, - TRACEBACK: {'error': {'fore': 'red'}}, + TRACEBACK: {'default': {'fore': 'red'}}, MARKER: 'lightyellow_ex' } +_DEFAULT_FORMATS = { + logger.NO_VERBOSE: '{message}', + logger.LOW_VERBOSE: '{timestamp:%H:%M:%S} | {level[0]} | {message}', + logger.MEDIUM_VERBOSE: '{timestamp:%H:%M:%S} | {level[0]} | {implementation} | {message}', + logger.HIGH_VERBOSE: '{timestamp:%H:%M:%S} | {level[0]} | {implementation} | {inputs} | {message}' +} + def stylize_log(item, mark_pattern): @@ -111,20 +108,19 @@ def stylize_log(item, mark_pattern): implementation = item.execution.workflow_name inputs = dict(i.unwrap() for i in item.execution.inputs.values()) - # TODO: use the is_workflow_log - stylized_str = Color.stylize(_get_format()) - _update_level(stylized_str, item) - _update_timestamp(stylized_str, item.created_at, item) - _update_message(stylized_str, item.msg, item, mark_pattern) - _update_inputs(stylized_str, inputs, item, mark_pattern) - _update_implementation(stylized_str, implementation, item, mark_pattern) + stylized_str = color.StringStylizer(_get_format()) + _populate_level(stylized_str, item) + _populate_timestamp(stylized_str, item) + _populate_message(stylized_str, item, mark_pattern) + _populate_inputs(stylized_str, inputs, item, mark_pattern) + _populate_implementation(stylized_str, implementation, item, mark_pattern) msg = StringIO() msg.write(str(stylized_str)) # Add the exception and the error msg. if item.traceback and env.logging.verbosity_level >= logger.MEDIUM_VERBOSE: msg.write(os.linesep) - msg.writelines(_get_traceback('\t' + '|' + line, item, mark_pattern) + msg.writelines(_color_traceback('\t' + '|' + line, item, mark_pattern) for line in item.traceback.splitlines(True)) return msg.getvalue() @@ -143,97 +139,95 @@ def log_list(iterator, mark_pattern=None): return any_logs -def _find_pattern(pattern, field_value): - # TODO: this finds the matching field type according to a pattern - match = re.match(pattern, field_value) - if match: - return match.group(1) - - def _get_format(): - return env.config.logging.formats[env.logging.verbosity_level] + return (env.config.logging.execution.formats.get(env.logging.verbosity_level) or + _DEFAULT_FORMATS.get(env.logging.verbosity_level)) -def _styles(field_type): - return env.config.logging.styles[field_type] +def _get_styles(field_type): + return env.config.logging.execution.colors[field_type] -def _is_styling_enabled(log_item): - return ( - # If styling is enabled - env.config.logging.styling_enabled or - # with the exception of the final string formatting - _end_execution_schema(log_item) - ) +def _is_color_enabled(): + # If styling is enabled and the current log_item isn't final string + return env.config.logging.execution.colors_enabled def _get_marker_schema(): - return Color.Schema(back=_styles(MARKER)) + return color.Schema(back=_get_styles(MARKER)) -def _update_implementation(str_, implementation, log_item, mark_pattern=None): +def _populate_implementation(str_, implementation, log_item, mark_pattern=None): _stylize(str_, implementation, log_item, IMPLEMENTATION, mark_pattern) -def _update_inputs(str_, inputs, log_item, mark_pattern=None): +def _populate_inputs(str_, inputs, log_item, mark_pattern=None): _stylize(str_, inputs, log_item, INPUTS, mark_pattern) -def _update_timestamp(str_, timestamp, log_item): - _stylize(str_, timestamp, log_item, TIMESTAMP) +def _populate_timestamp(str_, log_item): + _stylize(str_, log_item.created_at, log_item, TIMESTAMP) -def _update_message(str_, message, log_item, mark_pattern=None): - _stylize(str_, message, log_item, MESSAGE, mark_pattern) +def _populate_message(str_, log_item, mark_pattern=None): + _stylize(str_, log_item.msg, log_item, MESSAGE, mark_pattern) -def _update_level(str_, log_item): +def _populate_level(str_, log_item): _stylize(str_, log_item.level[0], log_item, LEVEL) def _stylize(stylized_str, msg, log_item, msg_type, mark_pattern=None): - matched_str = _find_pattern(_PATTERNS[FIELD_TYPE][msg_type], stylized_str._str) - if not matched_str: - return stylized_str + match = re.match(_PATTERNS[FIELD_TYPE][msg_type], stylized_str._str) + if not match: + return + matched_substr = match.group(1) - substring = Color.stylize(matched_str) + substring = color.StringStylizer(matched_substr) + # handle format substring.format(**{msg_type: msg}) - if _is_styling_enabled(log_item): + + if _is_color_enabled() and not _is_end_execution_log(log_item): + # handle color substring.color(_resolve_schema(msg_type, log_item)) - if not _end_execution_schema(log_item): + if not _is_end_execution_log(log_item): + # handle highlighting substring.highlight(mark_pattern, _get_marker_schema()) - stylized_str.replace(matched_str, substring) + stylized_str.replace(matched_substr, substring) -def _get_traceback(traceback, log_item, mark_pattern): - stylized_string = Color.stylize(traceback) - if _is_styling_enabled(log_item): - schema = Color.Schema(**_styles(TRACEBACK).get(log_item.level.lower(), {})) - stylized_string.color(schema) +def _color_traceback(traceback, log_item, mark_pattern): + if _is_color_enabled(): + stylized_string = color.StringStylizer(traceback, _resolve_schema(TRACEBACK, log_item)) stylized_string.highlight(mark_pattern, _get_marker_schema()) - return stylized_string + return stylized_string + return traceback + +def _is_end_execution_log(log_item): + return not log_item.task and bool(_end_execution_schema(log_item)) def _end_execution_schema(log_item): - if log_item.task: - # This can't be an end workflow log - return for state, pattern in _PATTERNS[FINAL_STATES].items(): if re.match(pattern, log_item.msg): return _FINAL_STATES[state] def _resolve_schema(msg_type, log_item): - schema = _end_execution_schema(log_item) - - if schema is None: - typed_schema_config = _styles(msg_type).get(log_item.level.lower()) - user_default_schema_config = _styles(msg_type).get('default') - aria_schema_config = _DEFAULT_STYLE[msg_type].get(log_item.level.lower()) - schema = Color.Schema( - **(typed_schema_config or user_default_schema_config or aria_schema_config) + if _is_end_execution_log(log_item): + return _end_execution_schema(log_item) + else: + return color.Schema( + **( + # retrieve the schema from the user config according to the level + _get_styles(msg_type).get(log_item.level.lower()) or + # retrieve the default schema from the user config + _get_styles(msg_type).get('default') or + # retrieve the schema from the aria default config according to the level + _DEFAULT_COLORS[msg_type].get(log_item.level.lower()) or + # retrieve the default schema from the aria default config + _DEFAULT_COLORS[msg_type].get('default') + ) ) - - return schema
