myansible/callback_plugins/json_logger.py

76 lines
2.4 KiB
Python

from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
name: json_logger
type: notification
short_description: Appends one JSON line per playbook run to ansible_runs.jsonl
description:
- Records playbook execution results as newline-delimited JSON.
'''
import json
import os
import time
from datetime import datetime
from ansible.plugins.callback import CallbackBase
CALLBACK_TYPE = 'notification'
CALLBACK_NAME = 'json_logger'
CALLBACK_NEEDS_WHITELIST = False
_LOG_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'ansible_runs.jsonl')
class CallbackModule(CallbackBase):
def __init__(self):
super(CallbackModule, self).__init__()
self._playbook_name = None
self._start_time = None
self._tasks = []
def v2_playbook_on_start(self, playbook):
self._playbook_name = os.path.basename(playbook._file_name)
self._start_time = time.time()
self._tasks = []
def v2_playbook_on_task_start(self, task, is_conditional):
name = task.get_name()
if name and name not in self._tasks:
self._tasks.append(name)
def v2_playbook_on_stats(self, stats):
duration = round(time.time() - self._start_time, 1) if self._start_time else 0.0
hosts_data = {}
overall_failed = False
for host in sorted(stats.processed.keys()):
s = stats.summarize(host)
hosts_data[host] = {
'ok': s['ok'],
'changed': s['changed'],
'failed': s['failures'],
'unreachable': s['unreachable'],
'skipped': s['skipped'],
}
if s['failures'] > 0 or s['unreachable'] > 0:
overall_failed = True
record = {
'ts': datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S'),
'playbook': self._playbook_name or 'unknown',
'user': os.environ.get('USER', os.environ.get('LOGNAME', 'unknown')),
'duration': duration,
'status': 'failed' if overall_failed else 'success',
'hosts': hosts_data,
'tasks': self._tasks,
}
try:
with open(_LOG_PATH, 'a') as f:
f.write(json.dumps(record) + '\n')
except Exception as e:
self._display.warning(f'json_logger: could not write to {_LOG_PATH}: {e}')