Skip to content

Easier to assert telemetry #1

@xrmx

Description

@xrmx

The telemetry exported from the sink is not easy for asserting becase the attributes are not in a map.

I have the following code downstream:

import base64
import leb128

class Foo:
    def get_telemetry(self) -> dict:
        """Convert to a more user friendly format than the one provided by oteltest Telemetry"""
        telemetry = self.handler.telemetry.to_dict()

        def decode_id(pb_id: str) -> str:
            """Decode protobuf encoded ids to something more recognizable"""
            encoded_id = base64.b64decode(pb_id)
            decoded_id = leb128.u.decode(encoded_id)
            return hex(decoded_id)

        def normalize_attributes(attributes) -> dict:
            """
            oteltest attributes are in the form:
            {
              "key": "telemetry.sdk.language",
              "value": {
                "stringValue": "python"
              }
            },
            ...
            but we want
            {
              "telemetry.sdk.language": "python",
              ...
            }
            """
            return {a["key"]: a["value"]["stringValue"] if "stringValue" in a["value"] else a["value"]["intValue"]  for a in attributes}

        metrics = []
        for request in telemetry["metric_requests"]:
            elems = []
            for proto_elem in request["request"]["resourceMetrics"]:
                scope_metrics = []
                for proto_scope_metric in proto_elem["scopeMetrics"]:
                    scope_metric = proto_scope_metric.copy()
                    for metric in scope_metric["metrics"]:
                        if "sum" in metric:
                            for data_point in metric["sum"]["dataPoints"]:
                                if "attributes" in data_point:
                                    data_point["attributes"] = normalize_attributes(data_point["attributes"])
                    scope_metrics.append(scope_metric)
                elem = {
                    "resource": {"attributes": normalize_attributes(proto_elem["resource"]["attributes"])},
                    "scopeMetrics": scope_metrics,
                }
                elems.append(elem)
            metric = {"resourceMetrics": elems}
            metrics.append(metric)

        traces = []
        for request in telemetry["trace_requests"]:
            for resource_span in request["request"]["resourceSpans"]:
                resource_attributes = normalize_attributes(resource_span["resource"]["attributes"])
                for proto_scope_spans in resource_span["scopeSpans"]:
                    for proto_span in proto_scope_spans["spans"]:
                        span = proto_span.copy()
                        span["attributes"] = normalize_attributes(span["attributes"])
                        span["resource"] = resource_attributes
                        span["spanId"] = decode_id(span["spanId"])
                        span["traceId"] = decode_id(span["traceId"])
                        traces.append(span)

        return {
            "logs": telemetry["log_requests"],  # TODO
            "metrics": metrics,
            "traces": traces,
        }

This adds a leb128 dependency for pb encoded trace and span ids.

I don't have much time to open a PR and add tests but feel free to pick whatever makes sense.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions