otlp-json 0.9.4__py3-none-any.whl → 0.9.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
otlp_json/__init__.py
CHANGED
|
@@ -18,45 +18,51 @@ if TYPE_CHECKING:
|
|
|
18
18
|
_VALUE: TypeAlias = "_LEAF_VALUE | Sequence[_LEAF_VALUE]"
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
__all__ = [
|
|
22
|
+
"CONTENT_TYPE",
|
|
23
|
+
"encode_spans",
|
|
24
|
+
]
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
# NOTE: order matters, for isinstance(True, int).
|
|
26
|
-
bool: ("boolValue", bool),
|
|
27
|
-
int: ("intValue", str),
|
|
28
|
-
float: ("doubleValue", float),
|
|
29
|
-
bytes: ("bytesValue", bytes),
|
|
30
|
-
str: ("stringValue", str),
|
|
31
|
-
Sequence: (
|
|
32
|
-
"arrayValue",
|
|
33
|
-
lambda value: {"values": [_value(e) for e in _homogeneous_array(value)]},
|
|
34
|
-
),
|
|
35
|
-
Mapping: (
|
|
36
|
-
"kvlistValue",
|
|
37
|
-
lambda value: {"values": [{k: _value(v) for k, v in value.items()}]},
|
|
38
|
-
),
|
|
39
|
-
}
|
|
26
|
+
CONTENT_TYPE = "application/json"
|
|
40
27
|
|
|
41
28
|
|
|
42
29
|
def encode_spans(spans: Sequence[ReadableSpan]) -> bytes:
|
|
43
|
-
|
|
30
|
+
resource_cache: dict[Resource, tuple] = {}
|
|
31
|
+
scope_cache: dict[InstrumentationScope, tuple] = {}
|
|
32
|
+
|
|
33
|
+
def linearise(span: ReadableSpan):
|
|
34
|
+
r = span.resource
|
|
35
|
+
if r not in resource_cache:
|
|
36
|
+
resource_cache[r] = (r.schema_url, tuple(r.attributes.items()))
|
|
37
|
+
s = span.instrumentation_scope
|
|
38
|
+
assert s
|
|
39
|
+
assert s.attributes is not None
|
|
40
|
+
if s not in scope_cache:
|
|
41
|
+
scope_cache[s] = (
|
|
42
|
+
s.schema_url,
|
|
43
|
+
s.name,
|
|
44
|
+
s.version,
|
|
45
|
+
tuple(s.attributes.items()),
|
|
46
|
+
)
|
|
47
|
+
return (resource_cache[r], scope_cache[s])
|
|
48
|
+
|
|
49
|
+
spans = sorted(spans, key=linearise)
|
|
44
50
|
rv = {"resourceSpans": []}
|
|
45
|
-
|
|
51
|
+
last_resource = last_scope = None
|
|
46
52
|
for span in spans:
|
|
47
53
|
assert span.resource
|
|
48
54
|
assert span.instrumentation_scope
|
|
49
|
-
if span.resource
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
if span.resource != last_resource:
|
|
56
|
+
last_resource = span.resource
|
|
57
|
+
last_scope = None
|
|
52
58
|
rv["resourceSpans"].append(
|
|
53
59
|
{
|
|
54
60
|
"resource": _resource(span.resource),
|
|
55
61
|
"scopeSpans": [],
|
|
56
62
|
}
|
|
57
63
|
)
|
|
58
|
-
if span.instrumentation_scope
|
|
59
|
-
|
|
64
|
+
if span.instrumentation_scope != last_scope:
|
|
65
|
+
last_scope = span.instrumentation_scope
|
|
60
66
|
rv["resourceSpans"][-1]["scopeSpans"].append(
|
|
61
67
|
{
|
|
62
68
|
"scope": _scope(span.instrumentation_scope),
|
|
@@ -93,35 +99,51 @@ def _attributes(
|
|
|
93
99
|
return rv
|
|
94
100
|
|
|
95
101
|
|
|
96
|
-
def
|
|
102
|
+
def _ensure_homogeneous(value: Sequence[_LEAF_VALUE]) -> Sequence[_LEAF_VALUE]:
|
|
97
103
|
# TODO: empty lists are allowed, aren't they?
|
|
98
104
|
if len(types := {type(v) for v in value}) > 1:
|
|
99
105
|
raise ValueError(f"Attribute value arrays must be homogeneous, got {types=}")
|
|
100
106
|
return value
|
|
101
107
|
|
|
102
108
|
|
|
103
|
-
def _value(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
def _value(v: _VALUE) -> dict[str, Any]:
|
|
110
|
+
if isinstance(v, bool):
|
|
111
|
+
return {"boolValue": bool(v)}
|
|
112
|
+
if isinstance(v, int):
|
|
113
|
+
return {"intValue": str(int(v))}
|
|
114
|
+
if isinstance(v, float):
|
|
115
|
+
return {"doubleValue": float(v)}
|
|
116
|
+
if isinstance(v, bytes):
|
|
117
|
+
return {
|
|
118
|
+
"bytesValue": bytes(v)
|
|
119
|
+
} # FIXME this can't be right; gotta encode this somehow
|
|
120
|
+
if isinstance(v, str):
|
|
121
|
+
return {"stringValue": str(v)}
|
|
122
|
+
if isinstance(v, Sequence):
|
|
123
|
+
return {"arrayValue": {"values": [_value(e) for e in _ensure_homogeneous(v)]}}
|
|
124
|
+
if isinstance(v, Mapping):
|
|
125
|
+
return {"kvlistValue": {"values": [{k: _value(vv) for k, vv in v.items()}]}}
|
|
111
126
|
|
|
112
|
-
raise ValueError(f"Cannot convert attribute of {type(
|
|
127
|
+
raise ValueError(f"Cannot convert attribute value of {type(v)=}")
|
|
113
128
|
|
|
114
129
|
|
|
115
130
|
def _scope(scope: InstrumentationScope):
|
|
116
131
|
rv = {
|
|
117
132
|
"name": scope.name,
|
|
118
|
-
|
|
133
|
+
# Upstream code for attrs and schema has landed, but wasn't released yet
|
|
134
|
+
# https://github.com/open-telemetry/opentelemetry-python/pull/4359
|
|
135
|
+
# "schema_url": scope.schema_url, # check if it may be null
|
|
136
|
+
# **_attributes(scope),
|
|
119
137
|
}
|
|
120
138
|
if scope.version:
|
|
121
139
|
rv["version"] = scope.version
|
|
122
140
|
return rv
|
|
123
141
|
|
|
124
142
|
|
|
143
|
+
_REMOTE = 0x300
|
|
144
|
+
_LOCAL = 0x100
|
|
145
|
+
|
|
146
|
+
|
|
125
147
|
def _span(span: ReadableSpan):
|
|
126
148
|
assert span.context
|
|
127
149
|
rv = {
|
|
@@ -129,7 +151,7 @@ def _span(span: ReadableSpan):
|
|
|
129
151
|
"kind": span.kind.value or 1, # unspecified -> internal
|
|
130
152
|
"traceId": _trace_id(span.context.trace_id),
|
|
131
153
|
"spanId": _span_id(span.context.span_id),
|
|
132
|
-
"flags":
|
|
154
|
+
"flags": _REMOTE if span.parent and span.parent.is_remote else _LOCAL,
|
|
133
155
|
"startTimeUnixNano": str(span.start_time),
|
|
134
156
|
"endTimeUnixNano": str(span.end_time), # can this be unset?
|
|
135
157
|
"status": _status(span.status),
|
|
@@ -159,8 +181,12 @@ def _span_id(span_id: int) -> str:
|
|
|
159
181
|
|
|
160
182
|
|
|
161
183
|
def _status(status: Status) -> dict[str, Any]:
|
|
162
|
-
|
|
163
|
-
|
|
184
|
+
rv = {}
|
|
185
|
+
if status.status_code.value:
|
|
186
|
+
rv["code"] = status.status_code.value
|
|
187
|
+
if status.description:
|
|
188
|
+
rv["message"] = status.description
|
|
189
|
+
return rv
|
|
164
190
|
|
|
165
191
|
|
|
166
192
|
def _event(event: Event) -> dict[str, Any]:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: otlp-json
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.5
|
|
4
4
|
Summary: 🐍Lightweight OTEL span to JSON converter, no dependencies, pure Python🐍
|
|
5
5
|
Project-URL: Repository, https://github.com/dimaqq/otlp-json
|
|
6
6
|
Project-URL: Issues, https://github.com/dimaqq/otlp-json/issues
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
otlp_json/__init__.py,sha256=sUClLoVbl5pXQtY3qBpKHx8O7ahZPPTcbRdv6enmC4Q,6198
|
|
2
|
+
otlp_json/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
otlp_json-0.9.5.dist-info/METADATA,sha256=3g016j1QprMogf3He-BHvBK1meNoOLGWOf6pUcjQLJM,2810
|
|
4
|
+
otlp_json-0.9.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
5
|
+
otlp_json-0.9.5.dist-info/RECORD,,
|
otlp_json-0.9.4.dist-info/RECORD
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
otlp_json/__init__.py,sha256=Ur95iX9xoeylZxq_ZKr5jqUNPvlp7ELcgxwU71SC42A,5364
|
|
2
|
-
otlp_json/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
otlp_json-0.9.4.dist-info/METADATA,sha256=swioA9pQ_LXYVL-J0WgTcKjkbdaC7xh70m2rQ4FSBSs,2810
|
|
4
|
-
otlp_json-0.9.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
5
|
-
otlp_json-0.9.4.dist-info/RECORD,,
|
|
File without changes
|