otlp-json 0.9.1__py3-none-any.whl → 0.9.2__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.py → otlp_json/__init__.py +99 -26
- {otlp_json-0.9.1.dist-info → otlp_json-0.9.2.dist-info}/METADATA +7 -5
- otlp_json-0.9.2.dist-info/RECORD +4 -0
- {otlp_json-0.9.1.dist-info → otlp_json-0.9.2.dist-info}/WHEEL +1 -2
- otlp_json-0.9.1.dist-info/RECORD +0 -5
- otlp_json-0.9.1.dist-info/top_level.txt +0 -1
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
from
|
|
4
|
+
from collections.abc import Mapping, Sequence
|
|
5
|
+
from typing import Any, TYPE_CHECKING
|
|
5
6
|
|
|
6
7
|
if TYPE_CHECKING:
|
|
7
8
|
from typing_extensions import TypeAlias
|
|
8
9
|
|
|
9
|
-
from opentelemetry.sdk.trace import ReadableSpan
|
|
10
|
+
from opentelemetry.sdk.trace import ReadableSpan, Event
|
|
10
11
|
from opentelemetry.sdk.resources import Resource
|
|
11
12
|
from opentelemetry.sdk.util.instrumentation import InstrumentationScope
|
|
12
13
|
from opentelemetry.trace.status import Status
|
|
@@ -18,6 +19,24 @@ if TYPE_CHECKING:
|
|
|
18
19
|
CONTENT_TYPE = "application/json"
|
|
19
20
|
|
|
20
21
|
|
|
22
|
+
_VALUE_TYPES = {
|
|
23
|
+
# NOTE: order matters, for isinstance(True, int).
|
|
24
|
+
bool: ("boolValue", bool),
|
|
25
|
+
int: ("intValue", str),
|
|
26
|
+
float: ("doubleValue", float),
|
|
27
|
+
bytes: ("bytesValue", bytes),
|
|
28
|
+
str: ("stringValue", str),
|
|
29
|
+
Sequence: (
|
|
30
|
+
"arrayValue",
|
|
31
|
+
lambda value: {"values": [_value(e) for e in _homogeneous_array(value)]},
|
|
32
|
+
),
|
|
33
|
+
Mapping: (
|
|
34
|
+
"kvlistValue",
|
|
35
|
+
lambda value: {"values": [{k: _value(v) for k, v in value.items()}]},
|
|
36
|
+
),
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
21
40
|
def encode_spans(spans: Sequence[ReadableSpan]) -> bytes:
|
|
22
41
|
spans = sorted(spans, key=lambda s: (id(s.resource), id(s.instrumentation_scope)))
|
|
23
42
|
rv = {"resourceSpans": []}
|
|
@@ -47,36 +66,46 @@ def encode_spans(spans: Sequence[ReadableSpan]) -> bytes:
|
|
|
47
66
|
|
|
48
67
|
|
|
49
68
|
def _resource(resource: Resource):
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
69
|
+
rv = {"attributes": []}
|
|
70
|
+
for k, v in resource.attributes.items():
|
|
71
|
+
try:
|
|
72
|
+
rv["attributes"].append({"key": k, "value": _value(v)})
|
|
73
|
+
except ValueError:
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
# NOTE: blocks that contain droppedAttributesCount:
|
|
77
|
+
# - Event
|
|
78
|
+
# - Link
|
|
79
|
+
# - InstrumentationScope
|
|
80
|
+
# - LogRecord (out of scope for this library)
|
|
81
|
+
# - Resource
|
|
82
|
+
rv["dropped_attributes_count"] = len(resource.attributes) - len(rv["attributes"]) # type: ignore
|
|
83
|
+
|
|
84
|
+
if not rv["attributes"]:
|
|
85
|
+
del rv["attributes"]
|
|
86
|
+
|
|
87
|
+
if not rv["dropped_attributes_count"]:
|
|
88
|
+
del rv["dropped_attributes_count"]
|
|
89
|
+
|
|
90
|
+
return rv
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _homogeneous_array(value: list[_LEAF_VALUE]) -> list[_LEAF_VALUE]:
|
|
94
|
+
# TODO: empty lists are allowed, aren't they?
|
|
95
|
+
if len(types := {type(v) for v in value}) > 1:
|
|
96
|
+
raise ValueError(f"Attribute value arrays must be homogeneous, got {types=}")
|
|
97
|
+
return value
|
|
55
98
|
|
|
56
99
|
|
|
57
100
|
def _value(value: _VALUE) -> dict[str, Any]:
|
|
58
101
|
# Attribute value can be a primitive type, excluging None...
|
|
59
102
|
# TODO: protobuf allows bytes, but I think OTLP doesn't.
|
|
60
103
|
# TODO: protobuf allows k:v pairs, but I think OTLP doesn't.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
str: "stringValue",
|
|
65
|
-
int: "intValue",
|
|
66
|
-
float: "floatValue",
|
|
67
|
-
bool: "boolValue",
|
|
68
|
-
}[type(value)]
|
|
69
|
-
return {k: value}
|
|
70
|
-
|
|
71
|
-
# Or a homogenous array of a primitive type, excluding None.
|
|
72
|
-
value = list(value)
|
|
73
|
-
|
|
74
|
-
# TODO: empty lists are allowed, aren't they?
|
|
75
|
-
if len({type(v) for v in value}) > 1:
|
|
76
|
-
raise ValueError(f"Attribute value arrays must be homogenous, got {value}")
|
|
104
|
+
for klass, (key, post) in _VALUE_TYPES.items():
|
|
105
|
+
if isinstance(value, klass):
|
|
106
|
+
return {key: post(value)}
|
|
77
107
|
|
|
78
|
-
|
|
79
|
-
return {"arrayValue": [_value(e) for e in value]}
|
|
108
|
+
raise ValueError(f"Cannot convert attribute of {type(value)=}")
|
|
80
109
|
|
|
81
110
|
|
|
82
111
|
def _scope(scope: InstrumentationScope):
|
|
@@ -93,13 +122,33 @@ def _span(span: ReadableSpan):
|
|
|
93
122
|
"kind": span.kind.value or 1, # unspecified -> internal
|
|
94
123
|
"traceId": _trace_id(span.context.trace_id),
|
|
95
124
|
"spanId": _span_id(span.context.span_id),
|
|
96
|
-
"flags": 0x100 | ([0, 0x200][bool(span.parent)]),
|
|
125
|
+
"flags": 0x100 | ([0, 0x200][bool(span.parent and span.parent.is_remote)]),
|
|
97
126
|
"startTimeUnixNano": str(span.start_time), # TODO: is it ever optional?
|
|
98
127
|
"endTimeUnixNano": str(span.end_time), # -"-
|
|
99
128
|
"status": _status(span.status),
|
|
129
|
+
"attributes": [],
|
|
100
130
|
}
|
|
131
|
+
|
|
101
132
|
if span.parent:
|
|
102
133
|
rv["parentSpanId"] = _span_id(span.parent.span_id)
|
|
134
|
+
|
|
135
|
+
for k, v in span.attributes.items(): # type: ignore
|
|
136
|
+
try:
|
|
137
|
+
rv["attributes"].append({"key": k, "value": _value(v)})
|
|
138
|
+
except ValueError:
|
|
139
|
+
pass
|
|
140
|
+
|
|
141
|
+
rv["dropped_attributes_count"] = len(span.attributes) - len(rv["attributes"]) # type: ignore
|
|
142
|
+
|
|
143
|
+
if not rv["attributes"]:
|
|
144
|
+
del rv["attributes"]
|
|
145
|
+
|
|
146
|
+
if not rv["dropped_attributes_count"]:
|
|
147
|
+
del rv["dropped_attributes_count"]
|
|
148
|
+
|
|
149
|
+
if span.events:
|
|
150
|
+
rv["events"] = [_event(e) for e in span.events]
|
|
151
|
+
|
|
103
152
|
return rv
|
|
104
153
|
|
|
105
154
|
|
|
@@ -118,3 +167,27 @@ def _span_id(span_id: int) -> str:
|
|
|
118
167
|
def _status(status: Status) -> dict[str, Any]:
|
|
119
168
|
# FIXME
|
|
120
169
|
return {}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _event(event: Event) -> dict[str, Any]:
|
|
173
|
+
rv = {
|
|
174
|
+
"name": event.name,
|
|
175
|
+
"timeUnixNano": str(event.timestamp),
|
|
176
|
+
"attributes": [],
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
for k, v in event.attributes.items(): # type: ignore
|
|
180
|
+
try:
|
|
181
|
+
rv["attributes"].append({"key": k, "value": _value(v)})
|
|
182
|
+
except ValueError:
|
|
183
|
+
pass
|
|
184
|
+
|
|
185
|
+
rv["dropped_attributes_count"] = len(event.attributes) - len(rv["attributes"]) # type: ignore
|
|
186
|
+
|
|
187
|
+
if not rv["attributes"]:
|
|
188
|
+
del rv["attributes"]
|
|
189
|
+
|
|
190
|
+
if not rv["dropped_attributes_count"]:
|
|
191
|
+
del rv["dropped_attributes_count"]
|
|
192
|
+
|
|
193
|
+
return rv
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: otlp-json
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.2
|
|
4
4
|
Summary: 🐍Lightweight OTEL span to JSON converter, no dependencies, pure Python🐍
|
|
5
|
+
Project-URL: Repository, https://github.com/dimaqq/otlp-json
|
|
6
|
+
Project-URL: Issues, https://github.com/dimaqq/otlp-json/issues
|
|
5
7
|
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Framework :: OpenTelemetry
|
|
9
|
+
Classifier: Framework :: OpenTelemetry :: Exporters
|
|
6
10
|
Classifier: Intended Audience :: Developers
|
|
7
11
|
Classifier: License :: OSI Approved :: MIT License
|
|
8
12
|
Classifier: Natural Language :: English
|
|
9
13
|
Classifier: Operating System :: OS Independent
|
|
10
14
|
Classifier: Programming Language :: Python
|
|
11
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
17
|
Classifier: Programming Language :: Python :: 3.8
|
|
13
18
|
Classifier: Programming Language :: Python :: 3.9
|
|
14
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -16,12 +21,9 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
16
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
23
|
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
24
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
21
25
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
22
26
|
Classifier: Topic :: Software Development :: Libraries
|
|
23
|
-
Classifier: Framework :: OpenTelemetry
|
|
24
|
-
Classifier: Framework :: OpenTelemetry :: Exporters
|
|
25
27
|
Requires-Python: >=3.8
|
|
26
28
|
Description-Content-Type: text/markdown
|
|
27
29
|
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
otlp_json/__init__.py,sha256=KFI4PCFiyTOw3wTUClS1hIeUyAMCCZp1F9F6K6Qi_IQ,5862
|
|
2
|
+
otlp_json-0.9.2.dist-info/METADATA,sha256=1CqS5onFoYgi8n4ft4DuQnsHwV_kp4aHC5zoQVxLIZA,2810
|
|
3
|
+
otlp_json-0.9.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
4
|
+
otlp_json-0.9.2.dist-info/RECORD,,
|
otlp_json-0.9.1.dist-info/RECORD
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
otlp_json.py,sha256=GCHauwquLROkov7v4WfmevNlKzC45aYDbvqGYCb66lg,3869
|
|
2
|
-
otlp_json-0.9.1.dist-info/METADATA,sha256=3OlCQjfep4nN69aMkIVHxTOawNDCnEqOLIu5cDCKHVQ,2685
|
|
3
|
-
otlp_json-0.9.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
4
|
-
otlp_json-0.9.1.dist-info/top_level.txt,sha256=7jOohtnyFAP4ixxnF_1gQz1NKEuVS8ELe3ymA4I5FtU,10
|
|
5
|
-
otlp_json-0.9.1.dist-info/RECORD,,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
otlp_json
|