Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <>.
# Inspired from:
# Improved and made compatible with Ansible v2
from __future__ import (absolute_import, division, print_function)
from __future__ import unicode_literals
import locale
import json
except ImportError:
import simplejson as json
import shutil
term_size = shutil.get_terminal_size
term_size = lambda:(80,24)
__metaclass__ = type
from ansible.plugins.callback import CallbackBase
# Fields to reformat output for
FIELDS = set(('cmd', 'command', 'delta', 'end', 'msg', 'results', 'start', 'stderr', 'stdout'))
encoding = locale.getpreferredencoding()
class CallbackModule(CallbackBase):
def human_log(self, host, res, *args, **kwargs):
if type(res) == dict:
for field in FIELDS.intersection(res):
if res[field]:
output = self._format_output(data[field])
print("\n{0}: {1}".format(field, output.replace("\\n","\n")))
def _format_output(self, output):
if not output:
return repr(output)
# Strip unicode
if type(output) == unicode:
output = output.encode(encoding, 'replace')
# If output is a dict
if type(output) == dict:
return json.dumps(output, indent=2)
# If output is a list of dicts
if type(output) == list and type(output[0]) == dict:
# This gets a little complicated because it potentially means
# nested results, usually because of with_items.
real_output = list()
for index, item in enumerate(output):
copy = item
if type(item) == dict:
for field in FIELDS:
if field in item.keys():
copy[field] = self._format_output(item[field])
return json.dumps(output, indent=2)
# If output is a list of strings
if type(output) == list and type(output[0]) != dict:
# Strip newline characters
real_output = list()
for item in output:
if "\n" in item:
for string in item.split("\n"):
# Reformat lists with line breaks
if len("".join(real_output)) > term_size()[0] - 2:
return "\n" + "\n".join(real_output)
return " ".join(real_output)
# Otherwise it's a string, just return it
return output
runner_on_async_failed = human_log
runner_on_async_ok = human_log
runner_on_async_poll = human_log
runner_on_failed = human_log
runner_on_ok = human_log
runner_on_unreachable = human_log
Copy link

romain-dartigues commented Mar 14, 2016


  • simplification
  • loop only on the keys present in res instead of all from FIELDS
  • wrap long lines based on terminal size, when possible
  • my term support utf-8, why limit it to ASCII

Copy link

KVInventoR commented Oct 14, 2017

ERROR! Unexpected Exception, this is probably a bug: 'CallbackModule' object has no attribute 'set_options'
new version has this error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment