Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: meta_struct support #184

Merged
merged 7 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions ddapm_test_agent/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class Span(TypedDict):
meta: NotRequired[Dict[str, str]]
metrics: NotRequired[Dict[str, MetricType]]
span_links: NotRequired[List[SpanLink]]
meta_struct: NotRequired[Dict[str, Dict[str, Any]]]


SpanAttr = Literal[
Expand All @@ -87,6 +88,7 @@ class Span(TypedDict):
"meta",
"metrics",
"span_links",
"meta_struct",
]
TopLevelSpanValue = Union[None, SpanId, TraceId, int, str, Dict[str, str], Dict[str, MetricType], List[SpanLink]]
Trace = List[Span]
Expand Down Expand Up @@ -124,6 +126,21 @@ def verify_span(d: Any) -> Span:
for k, v in d["meta"].items():
assert isinstance(k, str), f"Expected key 'meta.{k}' to be of type: 'str', got: {type(k)}"
assert isinstance(v, str), f"Expected value of key 'meta.{k}' to be of type: 'str', got: {type(v)}"
if "meta_struct" in d:
assert isinstance(d["meta_struct"], dict)
for k, v in d["meta_struct"].items():
assert isinstance(k, str), f"Expected key 'meta_struct.{k}' to be of type: 'str', got: {type(k)}"
assert isinstance(
v, bytes
), f"Expected msgpack'd value of key 'meta_struct.{k}' to be of type: 'bytes', got: {type(v)}"
for k, val in d["meta_struct"].items():
assert isinstance(
val, dict
), f"Expected msgpack decoded value of key 'meta_struct.{k}' to be of type: 'dict', got: {type(val)}"
for inner_k in val:
assert isinstance(
inner_k, str
), f"Expected key 'meta_struct.{k}.{inner_k}' to be of type: 'str', got: {type(inner_k)}"
if "metrics" in d:
assert isinstance(d["metrics"], dict)
for k, v in d["metrics"].items():
Expand Down Expand Up @@ -168,6 +185,26 @@ def verify_span(d: Any) -> Span:
raise TypeError(*e.args) from e


def _parse_meta_struct(value: Any) -> Dict[str, Dict[str, Any]]:
Kyle-Verhoog marked this conversation as resolved.
Show resolved Hide resolved
if not isinstance(value, dict):
raise TypeError("Expected meta_struct to be of type: 'dict', got: %s" % type(value))

return {key: msgpack.unpackb(val_bytes) for key, val_bytes in value.items()}


def _flexible_decode_meta_struct(value: Any) -> None:
Kyle-Verhoog marked this conversation as resolved.
Show resolved Hide resolved
if not isinstance(value, list):
return
for maybe_trace in value:
if not isinstance(maybe_trace, list):
continue
for maybe_span in maybe_trace:
if not isinstance(maybe_span, dict):
continue
if "meta_struct" in maybe_span:
maybe_span["meta_struct"] = _parse_meta_struct(maybe_span["meta_struct"])
Kyle-Verhoog marked this conversation as resolved.
Show resolved Hide resolved


def v04_verify_trace(maybe_trace: Any) -> Trace:
if not isinstance(maybe_trace, list):
raise TypeError("Trace must be a list.")
Expand Down Expand Up @@ -391,6 +428,7 @@ def decode_v04(content_type: str, data: bytes, suppress_errors: bool) -> v04Trac
payload = _trace_decoder_flexible(data) if suppress_errors else json.loads(data)
else:
raise TypeError("Content type %r not supported" % content_type)
_flexible_decode_meta_struct(payload)
Kyle-Verhoog marked this conversation as resolved.
Show resolved Hide resolved
return _verify_v04_payload(payload)


Expand Down
6 changes: 6 additions & 0 deletions releasenotes/notes/meta-struct-2cce08475cb05470.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
features:
- |
Add support for parsing the `meta_struct` field in traces. This field just like the `meta` field map but the values
are arbitrary inner msgpack-encoded values. When the `meta_struct` field is present, its inner messages will be
parsed and added to the trace as a dictionary.
62 changes: 61 additions & 1 deletion tests/test_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,21 @@ def test_trace_chunk():
]
),
),
(
"application/msgpack",
msgpack.packb(
[
[
{
"name": "span",
"span_id": 1234,
"trace_id": 321,
"meta_struct": {"key": msgpack.packb({"subkey": "value"})},
}
]
]
),
),
],
)
def test_decode_v04(content_type, payload):
Expand All @@ -58,8 +73,53 @@ def test_decode_v04(content_type, payload):
@pytest.mark.parametrize(
"content_type, payload",
[
("application/msgpack", msgpack.packb([{"name": "test"}])),
("application/json", json.dumps([{"name": "test"}])),
("application/msgpack", msgpack.packb([{"name": "test"}])),
(
"application/msgpack",
msgpack.packb(
[
[
{
"name": "span",
"span_id": 1234,
"trace_id": 321,
"meta_struct": "not a valid msgpack",
}
]
]
),
),
(
"application/msgpack",
msgpack.packb(
[
[
{
"name": "span",
"span_id": 1234,
"trace_id": 321,
"meta_struct": ["this is not a dict"],
}
]
]
),
),
(
"application/msgpack",
msgpack.packb(
[
[
{
"name": "span",
"span_id": 1234,
"trace_id": 321,
"meta_struct": {"key": msgpack.packb(["this is not a dict"])},
}
]
]
),
),
],
)
def test_decode_v04_bad(content_type, payload):
Expand Down
Loading