json[l] callback: add parameter to set JSON prettyprint indent level

Add ANSIBLE_JSON_INDENT parameter to both the json and jsonl callback
plugins.  The default values are different between the two modules to
maintain their existing behavior:

* json: indent==4, causing a prettyprint output
* jsonl: indent==0, causing a 1-line output

One specific use-case that is enabled by this feature: if a user
chooses to use the jsonl plugin so that they still get output at the
end of each task (vs. only at the end of the play), they may also want
human-readable output so that they can monitor the status of their
play.  For example, setting the jsonl indent level to 4 gives a)
output at the end of each task, and b) making that output be both
machine readable and human readable.

Signed-off-by: Jeff Squyres <jsquyres@cisco.com>
This commit is contained in:
Jeff Squyres 2023-04-08 08:01:06 -04:00
parent b6587a783e
commit 281f957ece
3 changed files with 36 additions and 2 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- json and jsonl - Add the ``ANSIBLE_JSON_INDENT`` parameter

View file

@ -25,6 +25,16 @@ DOCUMENTATION = '''
- key: show_custom_stats - key: show_custom_stats
section: defaults section: defaults
type: bool type: bool
json_indent:
name: Use indenting for the JSON output
description: 'If specified, use this many spaces for indenting in the JSON output. If <= 0, write to a single line.'
default: 4
env:
- name: ANSIBLE_JSON_INDENT
ini:
- key: json_indent
section: defaults
type: integer
notes: notes:
- When using a strategy such as free, host_pinned, or a custom strategy, host results will - When using a strategy such as free, host_pinned, or a custom strategy, host results will
be added to new task results in ``.plays[].tasks[]``. As such, there will exist duplicate be added to new task results in ``.plays[].tasks[]``. As such, there will exist duplicate
@ -61,6 +71,12 @@ class CallbackModule(CallbackBase):
self._task_map = {} self._task_map = {}
self._is_lockstep = False self._is_lockstep = False
self.set_options()
self._json_indent = self.get_option('json_indent')
if self._json_indent <= 0:
self._json_indent = None
def _new_play(self, play): def _new_play(self, play):
self._is_lockstep = play.strategy in LOCKSTEP_CALLBACKS self._is_lockstep = play.strategy in LOCKSTEP_CALLBACKS
return { return {
@ -143,7 +159,7 @@ class CallbackModule(CallbackBase):
'global_custom_stats': global_custom_stats, 'global_custom_stats': global_custom_stats,
} }
self._display.display(json.dumps(output, cls=AnsibleJSONEncoder, indent=4, sort_keys=True)) self._display.display(json.dumps(output, cls=AnsibleJSONEncoder, indent=self._json_indent, sort_keys=True))
def _record_task_result(self, on_info, result, **kwargs): def _record_task_result(self, on_info, result, **kwargs):
"""This function is used as a partial to add failed/skipped info in a single method""" """This function is used as a partial to add failed/skipped info in a single method"""

View file

@ -26,6 +26,16 @@ DOCUMENTATION = '''
- key: show_custom_stats - key: show_custom_stats
section: defaults section: defaults
type: bool type: bool
json_indent:
name: Use indenting for the JSON output
description: 'If specified, use this many spaces for indenting in the JSON output. If not specified or <= 0, write to a single line.'
default: 0
env:
- name: ANSIBLE_JSON_INDENT
ini:
- key: json_indent
section: defaults
type: integer
notes: notes:
- When using a strategy such as free, host_pinned, or a custom strategy, host results will - When using a strategy such as free, host_pinned, or a custom strategy, host results will
be added to new task results in ``.plays[].tasks[]``. As such, there will exist duplicate be added to new task results in ``.plays[].tasks[]``. As such, there will exist duplicate
@ -63,6 +73,12 @@ class CallbackModule(CallbackBase):
self._task_map = {} self._task_map = {}
self._is_lockstep = False self._is_lockstep = False
self.set_options()
self._json_indent = self.get_option('json_indent')
if self._json_indent <= 0:
self._json_indent = None
def _new_play(self, play): def _new_play(self, play):
self._is_lockstep = play.strategy in LOCKSTEP_CALLBACKS self._is_lockstep = play.strategy in LOCKSTEP_CALLBACKS
return { return {
@ -156,7 +172,7 @@ class CallbackModule(CallbackBase):
def _write_event(self, event_name, output): def _write_event(self, event_name, output):
output['_event'] = event_name output['_event'] = event_name
output['_timestamp'] = current_time() output['_timestamp'] = current_time()
self._display.display(json.dumps(output, cls=AnsibleJSONEncoder, separators=',:', sort_keys=True)) self._display.display(json.dumps(output, cls=AnsibleJSONEncoder, indent=self._json_indent, separators=',:', sort_keys=True))
def _record_task_result(self, event_name, on_info, result, **kwargs): def _record_task_result(self, event_name, on_info, result, **kwargs):
"""This function is used as a partial to add failed/skipped info in a single method""" """This function is used as a partial to add failed/skipped info in a single method"""