mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
[callback][elastic] enrich stacktrace errors (#3556)
* [callback][elastic] enrich stacktrace errors * [callback][elastic] add changelog fragment
This commit is contained in:
parent
d25554df9d
commit
18f8195983
3 changed files with 59 additions and 7 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- elastic callback plugin - enriched the stacktrace information with the ``message``, ``exception`` and ``stderr`` fields from the failed task (https://github.com/ansible-collections/community.general/pull/3556).
|
|
@ -226,18 +226,15 @@ class ElasticSource(object):
|
||||||
|
|
||||||
message = "success"
|
message = "success"
|
||||||
status = "success"
|
status = "success"
|
||||||
|
enriched_error_message = None
|
||||||
if host_data.status == 'included':
|
if host_data.status == 'included':
|
||||||
rc = 0
|
rc = 0
|
||||||
else:
|
else:
|
||||||
res = host_data.result._result
|
res = host_data.result._result
|
||||||
rc = res.get('rc', 0)
|
rc = res.get('rc', 0)
|
||||||
if host_data.status == 'failed':
|
if host_data.status == 'failed':
|
||||||
if res.get('exception') is not None:
|
message = self.get_error_message(res)
|
||||||
message = res['exception'].strip().split('\n')[-1]
|
enriched_error_message = self.enrich_error_message(res)
|
||||||
elif 'msg' in res:
|
|
||||||
message = res['msg']
|
|
||||||
else:
|
|
||||||
message = 'failed'
|
|
||||||
status = "failure"
|
status = "failure"
|
||||||
elif host_data.status == 'skipped':
|
elif host_data.status == 'skipped':
|
||||||
if 'skip_reason' in res:
|
if 'skip_reason' in res:
|
||||||
|
@ -259,7 +256,7 @@ class ElasticSource(object):
|
||||||
"ansible.task.host.status": host_data.status}) as span:
|
"ansible.task.host.status": host_data.status}) as span:
|
||||||
span.outcome = status
|
span.outcome = status
|
||||||
if 'failure' in status:
|
if 'failure' in status:
|
||||||
exception = AnsibleRuntimeError(message="{0}: {1} failed with error message {2}".format(task_data.action, name, message))
|
exception = AnsibleRuntimeError(message="{0}: {1} failed with error message {2}".format(task_data.action, name, enriched_error_message))
|
||||||
apm_cli.capture_exception(exc_info=(type(exception), exception, exception.__traceback__), handled=True)
|
apm_cli.capture_exception(exc_info=(type(exception), exception, exception.__traceback__), handled=True)
|
||||||
|
|
||||||
def init_apm_client(self, apm_server_url, apm_service_name, apm_verify_server_cert, apm_secret_token, apm_api_key):
|
def init_apm_client(self, apm_server_url, apm_service_name, apm_verify_server_cert, apm_secret_token, apm_api_key):
|
||||||
|
@ -272,6 +269,24 @@ class ElasticSource(object):
|
||||||
use_elastic_traceparent_header=True,
|
use_elastic_traceparent_header=True,
|
||||||
debug=True)
|
debug=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_error_message(result):
|
||||||
|
if result.get('exception') is not None:
|
||||||
|
return ElasticSource._last_line(result['exception'])
|
||||||
|
return result.get('msg', 'failed')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _last_line(text):
|
||||||
|
lines = text.strip().split('\n')
|
||||||
|
return lines[-1]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def enrich_error_message(result):
|
||||||
|
message = result.get('msg', 'failed')
|
||||||
|
exception = result.get('exception')
|
||||||
|
stderr = result.get('stderr')
|
||||||
|
return ('message: "{0}"\nexception: "{1}"\nstderr: "{2}"').format(message, exception, stderr)
|
||||||
|
|
||||||
|
|
||||||
class CallbackModule(CallbackBase):
|
class CallbackModule(CallbackBase):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -89,3 +89,38 @@ class TestOpentelemetry(unittest.TestCase):
|
||||||
self.assertEqual(host_data.uuid, 'include')
|
self.assertEqual(host_data.uuid, 'include')
|
||||||
self.assertEqual(host_data.name, 'include')
|
self.assertEqual(host_data.name, 'include')
|
||||||
self.assertEqual(host_data.status, 'ok')
|
self.assertEqual(host_data.status, 'ok')
|
||||||
|
|
||||||
|
def test_get_error_message(self):
|
||||||
|
test_cases = (
|
||||||
|
('my-exception', 'my-msg', None, 'my-exception'),
|
||||||
|
(None, 'my-msg', None, 'my-msg'),
|
||||||
|
(None, None, None, 'failed'),
|
||||||
|
)
|
||||||
|
|
||||||
|
for tc in test_cases:
|
||||||
|
result = self.elastic.get_error_message(generate_test_data(tc[0], tc[1], tc[2]))
|
||||||
|
self.assertEqual(result, tc[3])
|
||||||
|
|
||||||
|
def test_enrich_error_message(self):
|
||||||
|
test_cases = (
|
||||||
|
('my-exception', 'my-msg', 'my-stderr', 'message: "my-msg"\nexception: "my-exception"\nstderr: "my-stderr"'),
|
||||||
|
('my-exception', None, 'my-stderr', 'message: "failed"\nexception: "my-exception"\nstderr: "my-stderr"'),
|
||||||
|
(None, 'my-msg', 'my-stderr', 'message: "my-msg"\nexception: "None"\nstderr: "my-stderr"'),
|
||||||
|
('my-exception', 'my-msg', None, 'message: "my-msg"\nexception: "my-exception"\nstderr: "None"'),
|
||||||
|
('my-exception', 'my-msg', '\nline1\nline2', 'message: "my-msg"\nexception: "my-exception"\nstderr: "\nline1\nline2"')
|
||||||
|
)
|
||||||
|
|
||||||
|
for tc in test_cases:
|
||||||
|
result = self.elastic.enrich_error_message(generate_test_data(tc[0], tc[1], tc[2]))
|
||||||
|
self.assertEqual(result, tc[3])
|
||||||
|
|
||||||
|
|
||||||
|
def generate_test_data(exception=None, msg=None, stderr=None):
|
||||||
|
res_data = OrderedDict()
|
||||||
|
if exception:
|
||||||
|
res_data['exception'] = exception
|
||||||
|
if msg:
|
||||||
|
res_data['msg'] = msg
|
||||||
|
if stderr:
|
||||||
|
res_data['stderr'] = stderr
|
||||||
|
return res_data
|
||||||
|
|
Loading…
Reference in a new issue