1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

opentelemetry: add span event attributes (#6531)

* add span event attributes (task name and host name)

* add fragment

* refactor: use set_attributes

* Add same span attributes to the event

* chore: change description in the fragment

* as mentioned in the code review

* use flag to disable the attributes in logs

there are some vendors that might not require those attributes since those details are shown in the UI when accessing the spans, i.e.: jaeger

* Update plugins/callback/opentelemetry.py

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
Victor Martinez 2023-06-06 20:54:58 +02:00 committed by GitHub
parent b78d1999e1
commit 58958fc417
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 44 additions and 19 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- opentelemetry callback plugin - add span attributes in the span event (https://github.com/ansible-collections/community.general/pull/6531).

View file

@ -73,6 +73,17 @@ DOCUMENTATION = '''
- section: callback_opentelemetry - section: callback_opentelemetry
key: disable_logs key: disable_logs
version_added: 5.8.0 version_added: 5.8.0
disable_attributes_in_logs:
default: false
type: bool
description:
- Disable populating span attributes to the logs.
env:
- name: ANSIBLE_OPENTELEMETRY_DISABLE_ATTRIBUTES_IN_LOGS
ini:
- section: callback_opentelemetry
key: disable_attributes_in_logs
version_added: 7.1.0
requirements: requirements:
- opentelemetry-api (Python library) - opentelemetry-api (Python library)
- opentelemetry-exporter-otlp (Python library) - opentelemetry-exporter-otlp (Python library)
@ -244,7 +255,7 @@ class OpenTelemetrySource(object):
task.dump = dump task.dump = dump
task.add_host(HostData(host_uuid, host_name, status, result)) task.add_host(HostData(host_uuid, host_name, status, result))
def generate_distributed_traces(self, otel_service_name, ansible_playbook, tasks_data, status, traceparent, disable_logs): def generate_distributed_traces(self, otel_service_name, ansible_playbook, tasks_data, status, traceparent, disable_logs, disable_attributes_in_logs):
""" generate distributed traces from the collected TaskData and HostData """ """ generate distributed traces from the collected TaskData and HostData """
tasks = [] tasks = []
@ -280,9 +291,9 @@ class OpenTelemetrySource(object):
for task in tasks: for task in tasks:
for host_uuid, host_data in task.host_data.items(): for host_uuid, host_data in task.host_data.items():
with tracer.start_as_current_span(task.name, start_time=task.start, end_on_exit=False) as span: with tracer.start_as_current_span(task.name, start_time=task.start, end_on_exit=False) as span:
self.update_span_data(task, host_data, span, disable_logs) self.update_span_data(task, host_data, span, disable_logs, disable_attributes_in_logs)
def update_span_data(self, task_data, host_data, span, disable_logs): def update_span_data(self, task_data, host_data, span, disable_logs, disable_attributes_in_logs):
""" update the span with the given TaskData and HostData """ """ update the span with the given TaskData and HostData """
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)
@ -315,39 +326,47 @@ class OpenTelemetrySource(object):
status = Status(status_code=StatusCode.UNSET) status = Status(status_code=StatusCode.UNSET)
span.set_status(status) span.set_status(status)
# Create the span and log attributes
attributes = {
"ansible.task.module": task_data.action,
"ansible.task.message": message,
"ansible.task.name": name,
"ansible.task.result": rc,
"ansible.task.host.name": host_data.name,
"ansible.task.host.status": host_data.status
}
if isinstance(task_data.args, dict) and "gather_facts" not in task_data.action: if isinstance(task_data.args, dict) and "gather_facts" not in task_data.action:
names = tuple(self.transform_ansible_unicode_to_str(k) for k in task_data.args.keys()) names = tuple(self.transform_ansible_unicode_to_str(k) for k in task_data.args.keys())
values = tuple(self.transform_ansible_unicode_to_str(k) for k in task_data.args.values()) values = tuple(self.transform_ansible_unicode_to_str(k) for k in task_data.args.values())
self.set_span_attribute(span, ("ansible.task.args.name"), names) attributes[("ansible.task.args.name")] = names
self.set_span_attribute(span, ("ansible.task.args.value"), values) attributes[("ansible.task.args.value")] = values
self.set_span_attribute(span, "ansible.task.module", task_data.action)
self.set_span_attribute(span, "ansible.task.message", message) self.set_span_attributes(span, attributes)
self.set_span_attribute(span, "ansible.task.name", name)
self.set_span_attribute(span, "ansible.task.result", rc)
self.set_span_attribute(span, "ansible.task.host.name", host_data.name)
self.set_span_attribute(span, "ansible.task.host.status", host_data.status)
# This will allow to enrich the service map # This will allow to enrich the service map
self.add_attributes_for_service_map_if_possible(span, task_data) self.add_attributes_for_service_map_if_possible(span, task_data)
# Send logs # Send logs
if not disable_logs: if not disable_logs:
span.add_event(task_data.dump) # This will avoid populating span attributes to the logs
span.end(end_time=host_data.finish) span.add_event(task_data.dump, attributes={} if disable_attributes_in_logs else attributes)
span.end(end_time=host_data.finish)
def set_span_attribute(self, span, attributeName, attributeValue): def set_span_attributes(self, span, attributes):
""" update the span attribute with the given attribute and value if not None """ """ update the span attributes with the given attributes if not None """
if span is None and self._display is not None: if span is None and self._display is not None:
self._display.warning('span object is None. Please double check if that is expected.') self._display.warning('span object is None. Please double check if that is expected.')
else: else:
if attributeValue is not None: if attributes is not None:
span.set_attribute(attributeName, attributeValue) span.set_attributes(attributes)
def add_attributes_for_service_map_if_possible(self, span, task_data): def add_attributes_for_service_map_if_possible(self, span, task_data):
"""Update the span attributes with the service that the task interacted with, if possible.""" """Update the span attributes with the service that the task interacted with, if possible."""
redacted_url = self.parse_and_redact_url_if_possible(task_data.args) redacted_url = self.parse_and_redact_url_if_possible(task_data.args)
if redacted_url: if redacted_url:
self.set_span_attribute(span, "http.url", redacted_url.geturl()) span.set_attribute("http.url", redacted_url.geturl())
@staticmethod @staticmethod
def parse_and_redact_url_if_possible(args): def parse_and_redact_url_if_possible(args):
@ -434,6 +453,7 @@ class CallbackModule(CallbackBase):
def __init__(self, display=None): def __init__(self, display=None):
super(CallbackModule, self).__init__(display=display) super(CallbackModule, self).__init__(display=display)
self.hide_task_arguments = None self.hide_task_arguments = None
self.disable_attributes_in_logs = None
self.disable_logs = None self.disable_logs = None
self.otel_service_name = None self.otel_service_name = None
self.ansible_playbook = None self.ansible_playbook = None
@ -465,6 +485,8 @@ class CallbackModule(CallbackBase):
self.hide_task_arguments = self.get_option('hide_task_arguments') self.hide_task_arguments = self.get_option('hide_task_arguments')
self.disable_attributes_in_logs = self.get_option('disable_attributes_in_logs')
self.disable_logs = self.get_option('disable_logs') self.disable_logs = self.get_option('disable_logs')
self.otel_service_name = self.get_option('otel_service_name') self.otel_service_name = self.get_option('otel_service_name')
@ -562,7 +584,8 @@ class CallbackModule(CallbackBase):
self.tasks_data, self.tasks_data,
status, status,
self.traceparent, self.traceparent,
self.disable_logs self.disable_logs,
self.disable_attributes_in_logs
) )
def v2_runner_on_async_failed(self, result, **kwargs): def v2_runner_on_async_failed(self, result, **kwargs):