def test_colorize(self): """ Passing ``colorize=True`` will colorize the output. """ fd = BytesIO() tree = Tree() tree.merge_tasks([action_task, action_task_end]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=0, colorize=True) C = COLORS(colored) self.assertThat( fd.getvalue(), ExactlyEquals( u'\n'.join([ C.root(u'f3a32bb3-ea6b-457c-aa99-08a3d0491ab4'), u'\u2514\u2500\u2500 {}'.format( C.success(u'app:action@1/started')), u' \u251c\u2500\u2500 {}: {}'.format( C.prop(u'timestamp'), u'1425356800'), u' \u2514\u2500\u2500 {}'.format( C.success(u'app:action@2/succeeded')), u' \u2514\u2500\u2500 {}: {}'.format( C.prop('timestamp'), u'1425356802'), u'\n', ]).encode('utf-8')))
def display_task_tree(args): """ Read the input files, apply any command-line-specified behaviour and display the task tree. """ def task_transformers(): if args.human_readable: yield _convert_timestamp yield json.loads def filter_funcs(): if args.select: for query in args.select: yield filter_by_jmespath(query) if args.task_uuid: yield filter_by_uuid(args.task_uuid) if not args.files: args.files.append(sys.stdin) tree = Tree() tasks = imap(compose(*task_transformers()), chain.from_iterable(args.files)) render_task_nodes( write=sys.stdout.write, nodes=tree.nodes(tree.merge_tasks(tasks, filter_funcs())), ignored_task_keys=set(args.ignored_task_keys) or None, field_limit=args.field_limit, )
def display_task_tree(args): """ Read the input files, apply any command-line-specified behaviour and display the task tree. """ nodes = build_task_nodes( files=args.files, select=args.select, task_uuid=args.task_uuid, human_readable=args.human_readable) render_task_nodes( write=sys.stdout.write, nodes=nodes, ignored_task_keys=set(args.ignored_task_keys) or None, field_limit=args.field_limit)
def test_deprecated(self): """ `render_task_nodes` is deprecated. """ self.assertThat( lambda: render_task_nodes(lambda: None, [], field_limit=0), IsDeprecated(Contains('render_task_nodes is deprecated')))
def test_nested(self): """ Render nested tasks in a way that visually represents that nesting. """ fd = StringIO() tree = Tree() tree.merge_tasks([action_task, nested_action_task]) render_task_nodes(fd.write, tree.nodes(), 0) self.assertThat( fd.getvalue(), Equals( 'f3a32bb3-ea6b-457c-aa99-08a3d0491ab4\n' '+-- app:action@1/started\n' ' `-- timestamp: 1425356800\n' ' +-- app:action:nested@1,1/started\n' ' `-- timestamp: 1425356900\n\n'))
def test_nested(self): """ Render nested tasks in a way that visually represents that nesting. """ fd = BytesIO() tree = Tree() tree.merge_tasks([action_task, nested_action_task]) render_task_nodes(fd.write, tree.nodes(), 0) self.assertThat( fd.getvalue(), ExactlyEquals( u'f3a32bb3-ea6b-457c-aa99-08a3d0491ab4\n' u'\u2514\u2500\u2500 app:action@1/started\n' u' \u251c\u2500\u2500 timestamp: 1425356800\n' u' \u2514\u2500\u2500 app:action:nest@1,1/started\n' u' \u2514\u2500\u2500 timestamp: 1425356900\n\n' .encode('utf-8')))
def test_task_data(self): """ Task data is rendered as tree elements. """ fd = StringIO() tree = Tree() tree.merge_tasks([message_task]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=0) self.assertThat( fd.getvalue(), Equals( 'cdeb220d-7605-4d5f-8341-1a170222e308\n' '+-- twisted:log@1\n' ' |-- error: False\n' ' |-- message: Main loop terminated.\n' ' `-- timestamp: 1425356700\n\n'))
def test_dict_data(self): """ Task values that are ``dict``s are rendered as tree elements. """ fd = StringIO() tree = Tree() tree.merge_tasks([dict_action_task]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=0) self.assertThat( fd.getvalue(), Equals( 'f3a32bb3-ea6b-457c-aa99-08a3d0491ab4\n' '+-- app:action@1/started\n' ' |-- some_data:\n' ' `-- a: 42\n' ' `-- timestamp: 1425356800\n\n'))
def test_multiline_field_limit(self): """ When a field limit is specified for task values, only the first of multiple lines is output. """ fd = StringIO() tree = Tree() tree.merge_tasks([multiline_action_task]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=1000) self.assertThat( fd.getvalue(), Equals( 'f3a32bb3-ea6b-457c-aa99-08a3d0491ab4\n' '+-- app:action@1/started\n' ' |-- message: this is a [...]\n' ' `-- timestamp: 1425356800\n\n'))
def test_field_limit(self): """ Truncate task values that are longer than the field_limit if specified. """ fd = StringIO() tree = Tree() tree.merge_tasks([message_task]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=5) self.assertThat( fd.getvalue(), Equals( 'cdeb220d-7605-4d5f-8341-1a170222e308\n' '+-- twisted:log@1\n' ' |-- error: False\n' ' |-- message: Main [...]\n' ' `-- timestamp: 14253 [...]\n\n'))
def test_multiline_field_limit(self): """ When a field limit is specified for task values, only the first of multiple lines is output. """ fd = BytesIO() tree = Tree() tree.merge_tasks([multiline_action_task]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=1000) self.assertThat( fd.getvalue(), ExactlyEquals( u'f3a32bb3-ea6b-457c-aa99-08a3d0491ab4\n' u'\u2514\u2500\u2500 app:action@1/started\n' u' \u251c\u2500\u2500 message: this is a\u2026\n' u' \u2514\u2500\u2500 timestamp: 1425356800\n\n' .encode('utf-8')))
def test_dict_data(self): """ Task values that are ``dict``s are rendered as tree elements. """ fd = BytesIO() tree = Tree() tree.merge_tasks([dict_action_task]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=0) self.assertThat( fd.getvalue(), ExactlyEquals( u'f3a32bb3-ea6b-457c-aa99-08a3d0491ab4\n' u'\u2514\u2500\u2500 app:action@1/started\n' u' \u251c\u2500\u2500 some_data\n' u' \u2502 \u2514\u2500\u2500 a: 42\n' u' \u2514\u2500\u2500 timestamp: 1425356800\n\n' .encode('utf-8')))
def test_task_data(self): """ Task data is rendered as tree elements. """ fd = BytesIO() tree = Tree() tree.merge_tasks([message_task]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=0) self.assertThat( fd.getvalue(), ExactlyEquals( u'cdeb220d-7605-4d5f-8341-1a170222e308\n' u'\u2514\u2500\u2500 twisted:log@1\n' u' \u251c\u2500\u2500 error: False\n' u' \u251c\u2500\u2500 message: Main loop terminated.\n' u' \u2514\u2500\u2500 timestamp: 1425356700\n\n' .encode('utf-8')))
def _eliottree(logs): """ Render some Eliot log events into a tree-like string. :param list[dict] logs: The Eliot log events to render. These should be dicts like those passed to an Eliot destination. :return bytes: The rendered string. """ tree = Tree() tree.merge_tasks(logs) nodes = tree.nodes() out = BytesIO() render_task_nodes( write=out.write, nodes=nodes, field_limit=0, ) return out.getvalue()
def test_field_limit(self): """ Truncate task values that are longer than the field_limit if specified. """ fd = BytesIO() tree = Tree() tree.merge_tasks([message_task]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=5) self.assertThat( fd.getvalue(), ExactlyEquals( u'cdeb220d-7605-4d5f-8341-1a170222e308\n' u'\u2514\u2500\u2500 twisted:log@1\n' u' \u251c\u2500\u2500 error: False\n' u' \u251c\u2500\u2500 message: Main \u2026\n' u' \u2514\u2500\u2500 timestamp: 14253\u2026\n\n' .encode('utf-8')))
def test_tasks(self): """ Render two tasks of sequential levels, by default most standard Eliot task keys are ignored. """ fd = StringIO() tree = Tree() tree.merge_tasks([action_task, action_task_end]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=0) self.assertThat( fd.getvalue(), Equals( 'f3a32bb3-ea6b-457c-aa99-08a3d0491ab4\n' '+-- app:action@1/started\n' ' `-- timestamp: 1425356800\n' ' +-- app:action@2/succeeded\n' ' `-- timestamp: 1425356800\n\n'))
def test_tasks_human_readable(self): """ Render two tasks of sequential levels, by default most standard Eliot task keys are ignored, values are formatted to be human readable. """ fd = StringIO() tree = Tree() tree.merge_tasks([action_task, action_task_end]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=0, human_readable=True) self.assertThat( fd.getvalue(), Equals( 'f3a32bb3-ea6b-457c-aa99-08a3d0491ab4\n' '+-- app:action@1/started\n' ' `-- timestamp: 2015-03-03 04:26:40\n' ' +-- app:action@2/succeeded\n' ' `-- timestamp: 2015-03-03 04:26:40\n\n'))
def test_tasks(self): """ Render two tasks of sequential levels, by default most standard Eliot task keys are ignored. """ fd = BytesIO() tree = Tree() tree.merge_tasks([action_task, action_task_end]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=0) self.assertThat( fd.getvalue(), ExactlyEquals( u'f3a32bb3-ea6b-457c-aa99-08a3d0491ab4\n' u'\u2514\u2500\u2500 app:action@1/started\n' u' \u251c\u2500\u2500 timestamp: 1425356800\n' u' \u2514\u2500\u2500 app:action@2/succeeded\n' u' \u2514\u2500\u2500 timestamp: 1425356802\n\n' .encode('utf-8')))
def test_ignored_keys(self): """ Task keys can be ignored. """ fd = StringIO() tree = Tree() tree.merge_tasks([action_task]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=0, ignored_task_keys=set(['task_level'])) self.assertThat( fd.getvalue(), Equals( 'f3a32bb3-ea6b-457c-aa99-08a3d0491ab4\n' '+-- app:action@1/started\n' ' |-- action_status: started\n' ' |-- action_type: app:action\n' ' |-- task_uuid: f3a32bb3-ea6b-457c-aa99-08a3d0491ab4\n' ' `-- timestamp: 1425356800\n\n'))
def display_task_tree(args): """ Read the input files, apply any command-line-specified behaviour and display the task tree. """ if PY3: write = sys.stdout.write else: write = codecs.getwriter('utf-8')(sys.stdout).write nodes = build_task_nodes( files=args.files, select=args.select, task_uuid=args.task_uuid, start=args.start, end=args.end) render_task_nodes( write=write, nodes=nodes, ignored_task_keys=set(args.ignored_task_keys) or None, field_limit=args.field_limit, human_readable=args.human_readable)
def test_janky_message(self): """ Task names, UUIDs, keys and values in messages all have control characters escaped. """ fd = BytesIO() tree = Tree() tree.merge_tasks([janky_message_task]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=0) self.assertThat( fd.getvalue(), ExactlyEquals( u'cdeb220d-7605-4d5f-\u241b(08341-1a170222e308\n' u'\u2514\u2500\u2500 M\u241b(0@1\n' u' \u251c\u2500\u2500 er\u241bror: False\n' u' \u251c\u2500\u2500 mes\u240asage: ' u'Main loop\u241b(0terminated.\n' u' \u2514\u2500\u2500 timestamp: 1425356700\n\n' .encode('utf-8')))
def test_janky_action(self): """ Task names, UUIDs, keys and values in actions all have control characters escaped. """ fd = BytesIO() tree = Tree() tree.merge_tasks([janky_action_task]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=0) self.assertThat( fd.getvalue(), ExactlyEquals( u'f3a32bb3-ea6b-457c-\u241b(0aa99-08a3d0491ab4\n' u'\u2514\u2500\u2500 A\u241b(0@1/started\n' u' \u251c\u2500\u2500 \u241b(0\n' u' \u2502 \u2514\u2500\u2500 \u241b(0: nope\n' u' \u251c\u2500\u2500 mes\u240asage: hello\u241b(0world\n' u' \u2514\u2500\u2500 timestamp: 1425356800\u241b(0\n\n' .encode('utf-8')))
def test_tasks_human_readable(self): """ Render two tasks of sequential levels, by default most standard Eliot task keys are ignored, values are formatted to be human readable. """ fd = BytesIO() tree = Tree() tree.merge_tasks([action_task, action_task_end]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=0, human_readable=True) self.assertThat( fd.getvalue(), ExactlyEquals( u'f3a32bb3-ea6b-457c-aa99-08a3d0491ab4\n' u'\u2514\u2500\u2500 app:action@1/started\n' u' \u251c\u2500\u2500 timestamp: 2015-03-03 04:26:40\n' u' \u2514\u2500\u2500 app:action@2/succeeded\n' u' \u2514\u2500\u2500 timestamp: 2015-03-03 04:26:42\n' u'\n' .encode('utf-8')))
def test_ignored_keys(self): """ Task keys can be ignored. """ fd = BytesIO() tree = Tree() tree.merge_tasks([action_task]) render_task_nodes( write=fd.write, nodes=tree.nodes(), field_limit=0, ignored_task_keys=set(['task_level'])) self.assertThat( fd.getvalue(), ExactlyEquals( u'f3a32bb3-ea6b-457c-aa99-08a3d0491ab4\n' u'\u2514\u2500\u2500 app:action@1/started\n' u' \u251c\u2500\u2500 action_status: started\n' u' \u251c\u2500\u2500 action_type: app:action\n' u' \u251c\u2500\u2500 task_uuid: ' u'f3a32bb3-ea6b-457c-aa99-08a3d0491ab4\n' u' \u2514\u2500\u2500 timestamp: 1425356800\n\n' .encode('utf-8')))