mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
[opentelemetry][callback] Support loops error handling (#3599)
* [opentelemetry][callback] simplify condition * [opentelemetry][callback] initial support for loops * [opentelemetry][callback] support for loops * [opentelemetry][callback] enrich loop message with action and item * [opentelemetry][callback] add UTs for get_error_message_from_results * [opentelemetry][callback] add UTs for enrich_error_message_from_results * [opentelemetry][callback] add changelog fragment * Update plugins/callback/opentelemetry.py Co-authored-by: Ajpantuso <ajpantuso@gmail.com> Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
This commit is contained in:
parent
7e1412e5e1
commit
a362879ff6
3 changed files with 66 additions and 12 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- opentelemetry callback plugin - enriched the stacktrace information for loops with the ``message``, ``exception`` and ``stderr`` fields from the failed item in the tasks in addition to the name of the task and failed item (https://github.com/ansible-collections/community.general/pull/3599).
|
|
@ -245,22 +245,27 @@ class OpenTelemetrySource(object):
|
||||||
name = '[%s] %s: %s' % (host_data.name, task_data.play, task_data.name)
|
name = '[%s] %s: %s' % (host_data.name, task_data.play, task_data.name)
|
||||||
|
|
||||||
message = 'success'
|
message = 'success'
|
||||||
status = Status(status_code=StatusCode.OK)
|
res = {}
|
||||||
if host_data.status == 'included':
|
|
||||||
rc = 0
|
rc = 0
|
||||||
|
status = Status(status_code=StatusCode.OK)
|
||||||
|
if host_data.status != 'included':
|
||||||
|
# Support loops
|
||||||
|
if 'results' in host_data.result._result:
|
||||||
|
if host_data.status == 'failed':
|
||||||
|
message = self.get_error_message_from_results(host_data.result._result['results'], task_data.action)
|
||||||
|
enriched_error_message = self.enrich_error_message_from_results(host_data.result._result['results'], task_data.action)
|
||||||
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':
|
|
||||||
message = self.get_error_message(res)
|
message = self.get_error_message(res)
|
||||||
|
enriched_error_message = self.enrich_error_message(res)
|
||||||
|
|
||||||
|
if host_data.status == 'failed':
|
||||||
status = Status(status_code=StatusCode.ERROR, description=message)
|
status = Status(status_code=StatusCode.ERROR, description=message)
|
||||||
# Record an exception with the task message
|
# Record an exception with the task message
|
||||||
span.record_exception(BaseException(self.enrich_error_message(res)))
|
span.record_exception(BaseException(enriched_error_message))
|
||||||
elif host_data.status == 'skipped':
|
elif host_data.status == 'skipped':
|
||||||
if 'skip_reason' in res:
|
message = res['skip_reason'] if 'skip_reason' in res else 'skipped'
|
||||||
message = res['skip_reason']
|
|
||||||
else:
|
|
||||||
message = 'skipped'
|
|
||||||
status = Status(status_code=StatusCode.UNSET)
|
status = Status(status_code=StatusCode.UNSET)
|
||||||
|
|
||||||
span.set_status(status)
|
span.set_status(status)
|
||||||
|
@ -340,6 +345,12 @@ class OpenTelemetrySource(object):
|
||||||
return OpenTelemetrySource._last_line(result['exception'])
|
return OpenTelemetrySource._last_line(result['exception'])
|
||||||
return result.get('msg', 'failed')
|
return result.get('msg', 'failed')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_error_message_from_results(results, action):
|
||||||
|
for result in results:
|
||||||
|
if result.get('failed', False):
|
||||||
|
return ('{0}({1}) - {2}').format(action, result.get('item', 'none'), OpenTelemetrySource.get_error_message(result))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _last_line(text):
|
def _last_line(text):
|
||||||
lines = text.strip().split('\n')
|
lines = text.strip().split('\n')
|
||||||
|
@ -352,6 +363,14 @@ class OpenTelemetrySource(object):
|
||||||
stderr = result.get('stderr')
|
stderr = result.get('stderr')
|
||||||
return ('message: "{0}"\nexception: "{1}"\nstderr: "{2}"').format(message, exception, stderr)
|
return ('message: "{0}"\nexception: "{1}"\nstderr: "{2}"').format(message, exception, stderr)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def enrich_error_message_from_results(results, action):
|
||||||
|
message = ""
|
||||||
|
for result in results:
|
||||||
|
if result.get('failed', False):
|
||||||
|
message = ('{0}({1}) - {2}\n{3}').format(action, result.get('item', 'none'), OpenTelemetrySource.enrich_error_message(result), message)
|
||||||
|
return message
|
||||||
|
|
||||||
|
|
||||||
class CallbackModule(CallbackBase):
|
class CallbackModule(CallbackBase):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -103,6 +103,20 @@ class TestOpentelemetry(unittest.TestCase):
|
||||||
result = self.opentelemetry.get_error_message(generate_test_data(tc[0], tc[1], tc[2]))
|
result = self.opentelemetry.get_error_message(generate_test_data(tc[0], tc[1], tc[2]))
|
||||||
self.assertEqual(result, tc[3])
|
self.assertEqual(result, tc[3])
|
||||||
|
|
||||||
|
def test_get_error_message_from_results(self):
|
||||||
|
test_cases = (
|
||||||
|
('my-exception', 'my-msg', None, False, None),
|
||||||
|
(None, 'my-msg', None, False, None),
|
||||||
|
(None, None, None, False, None),
|
||||||
|
('my-exception', 'my-msg', None, True, 'shell(none) - my-exception'),
|
||||||
|
(None, 'my-msg', None, True, 'shell(none) - my-msg'),
|
||||||
|
(None, None, None, True, 'shell(none) - failed'),
|
||||||
|
)
|
||||||
|
|
||||||
|
for tc in test_cases:
|
||||||
|
result = self.opentelemetry.get_error_message_from_results([generate_test_data(tc[0], tc[1], tc[2], tc[3])], 'shell')
|
||||||
|
self.assertEqual(result, tc[4])
|
||||||
|
|
||||||
def test_enrich_error_message(self):
|
def test_enrich_error_message(self):
|
||||||
test_cases = (
|
test_cases = (
|
||||||
('my-exception', 'my-msg', 'my-stderr', 'message: "my-msg"\nexception: "my-exception"\nstderr: "my-stderr"'),
|
('my-exception', 'my-msg', 'my-stderr', 'message: "my-msg"\nexception: "my-exception"\nstderr: "my-stderr"'),
|
||||||
|
@ -116,6 +130,24 @@ class TestOpentelemetry(unittest.TestCase):
|
||||||
result = self.opentelemetry.enrich_error_message(generate_test_data(tc[0], tc[1], tc[2]))
|
result = self.opentelemetry.enrich_error_message(generate_test_data(tc[0], tc[1], tc[2]))
|
||||||
self.assertEqual(result, tc[3])
|
self.assertEqual(result, tc[3])
|
||||||
|
|
||||||
|
def test_enrich_error_message_from_results(self):
|
||||||
|
test_cases = (
|
||||||
|
('my-exception', 'my-msg', 'my-stderr', False, ''),
|
||||||
|
('my-exception', None, 'my-stderr', False, ''),
|
||||||
|
(None, 'my-msg', 'my-stderr', False, ''),
|
||||||
|
('my-exception', 'my-msg', None, False, ''),
|
||||||
|
('my-exception', 'my-msg', '\nline1\nline2', False, ''),
|
||||||
|
('my-exception', 'my-msg', 'my-stderr', True, 'shell(none) - message: "my-msg"\nexception: "my-exception"\nstderr: "my-stderr"\n'),
|
||||||
|
('my-exception', None, 'my-stderr', True, 'shell(none) - message: "failed"\nexception: "my-exception"\nstderr: "my-stderr"\n'),
|
||||||
|
(None, 'my-msg', 'my-stderr', True, 'shell(none) - message: "my-msg"\nexception: "None"\nstderr: "my-stderr"\n'),
|
||||||
|
('my-exception', 'my-msg', None, True, 'shell(none) - message: "my-msg"\nexception: "my-exception"\nstderr: "None"\n'),
|
||||||
|
('my-exception', 'my-msg', '\nline1\nline2', True, 'shell(none) - message: "my-msg"\nexception: "my-exception"\nstderr: "\nline1\nline2"\n')
|
||||||
|
)
|
||||||
|
|
||||||
|
for tc in test_cases:
|
||||||
|
result = self.opentelemetry.enrich_error_message_from_results([generate_test_data(tc[0], tc[1], tc[2], tc[3])], 'shell')
|
||||||
|
self.assertEqual(result, tc[4])
|
||||||
|
|
||||||
def test_url_from_args(self):
|
def test_url_from_args(self):
|
||||||
test_cases = (
|
test_cases = (
|
||||||
({}, ""),
|
({}, ""),
|
||||||
|
@ -148,7 +180,7 @@ class TestOpentelemetry(unittest.TestCase):
|
||||||
self.assertEqual(result, tc[1])
|
self.assertEqual(result, tc[1])
|
||||||
|
|
||||||
|
|
||||||
def generate_test_data(exception=None, msg=None, stderr=None):
|
def generate_test_data(exception=None, msg=None, stderr=None, failed=False):
|
||||||
res_data = OrderedDict()
|
res_data = OrderedDict()
|
||||||
if exception:
|
if exception:
|
||||||
res_data['exception'] = exception
|
res_data['exception'] = exception
|
||||||
|
@ -156,4 +188,5 @@ def generate_test_data(exception=None, msg=None, stderr=None):
|
||||||
res_data['msg'] = msg
|
res_data['msg'] = msg
|
||||||
if stderr:
|
if stderr:
|
||||||
res_data['stderr'] = stderr
|
res_data['stderr'] = stderr
|
||||||
|
res_data['failed'] = failed
|
||||||
return res_data
|
return res_data
|
||||||
|
|
Loading…
Reference in a new issue