Skip to content

Commit 3205f3c

Browse files
authored
Fix Opentelemetry Azmon trace exporter (Azure#15344)
1 parent 42dcda0 commit 3205f3c

File tree

5 files changed

+214
-528
lines changed

5 files changed

+214
-528
lines changed

sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
**Breaking Changes**
66
- This library is renamed to `microsoft-opentelemetry-exporter-azuremonitor`.
77

8+
- Change span to envelope conversion to adhere to common schema and other languages
9+
([#15344](https://github.com/Azure/azure-sdk-for-python/pull/15344))
10+
811
## 0.5b.0 (2020-09-24)
912

1013
- Change epoch for live metrics

sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/microsoft/opentelemetry/exporter/azuremonitor/export/trace/__init__.py

Lines changed: 86 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
99
from opentelemetry.sdk.util import ns_to_iso_str
1010
from opentelemetry.trace import Span, SpanKind
11-
from opentelemetry.trace.status import StatusCode
1211

1312
from microsoft.opentelemetry.exporter.azuremonitor import utils
1413
from microsoft.opentelemetry.exporter.azuremonitor._generated.models import (
@@ -78,75 +77,119 @@ def convert_span_to_envelope(span: Span) -> TelemetryItem:
7877
if span.kind in (SpanKind.CONSUMER, SpanKind.SERVER):
7978
envelope.name = "Microsoft.ApplicationInsights.Request"
8079
data = RequestData(
80+
name=span.name,
8181
id="{:016x}".format(span.context.span_id),
8282
duration=utils.ns_to_duration(span.end_time - span.start_time),
8383
response_code=str(span.status.status_code.value),
84-
success=span.status.status_code
85-
== StatusCode.OK, # Modify based off attributes or Status
84+
success=span.status.is_ok,
8685
properties={},
8786
)
8887
envelope.data = MonitorBase(base_data=data, base_type="RequestData")
89-
if "http.method" in span.attributes:
90-
data.name = span.attributes["http.method"]
88+
if "http.method" in span.attributes: # HTTP
9189
if "http.route" in span.attributes:
92-
data.name = data.name + " " + span.attributes["http.route"]
93-
envelope.tags["ai.operation.name"] = data.name
94-
data.properties["request.name"] = data.name
90+
envelope.tags["ai.operation.name"] = span.attributes["http.route"]
9591
elif "http.path" in span.attributes:
96-
data.properties["request.name"] = (
97-
data.name + " " + span.attributes["http.path"]
98-
)
99-
if "http.url" in span.attributes:
100-
data.url = span.attributes["http.url"]
101-
data.properties["request.url"] = span.attributes["http.url"]
102-
if "http.status_code" in span.attributes:
103-
status_code = span.attributes["http.status_code"]
104-
data.response_code = str(status_code)
105-
data.success = 200 <= status_code < 400
92+
envelope.tags["ai.operation.name"] = span.attributes["http.path"]
93+
else:
94+
envelope.tags["ai.operation.name"] = span.name
95+
96+
if "http.url" in span.attributes:
97+
data.url = span.attributes["http.url"]
98+
data.properties["request.url"] = span.attributes["http.url"]
99+
if "http.status_code" in span.attributes:
100+
status_code = span.attributes["http.status_code"]
101+
data.response_code = str(status_code)
102+
elif "messaging.system" in span.attributes: # Messaging
103+
envelope.tags["ai.operation.name"] = span.name
104+
105+
if "messaging.destination" in span.attributes:
106+
if "net.peer.name" in span.attributes:
107+
data.properties["source"] = "{}/{}".format(
108+
span.attributes["net.peer.name"],
109+
span.attributes["messaging.destination"],
110+
)
111+
elif "net.peer.ip" in span.attributes:
112+
data.properties["source"] = "{}/{}".format(
113+
span.attributes["net.peer.ip"],
114+
span.attributes["messaging.destination"],
115+
)
116+
else:
117+
data.properties["source"] = span.attributes["messaging.destination"]
106118
else:
107119
envelope.name = "Microsoft.ApplicationInsights.RemoteDependency"
108120
data = RemoteDependencyData(
109121
name=span.name,
110122
id="{:016x}".format(span.context.span_id),
111123
result_code=str(span.status.status_code.value),
112124
duration=utils.ns_to_duration(span.end_time - span.start_time),
113-
success=span.status.status_code
114-
== StatusCode.OK, # Modify based off attributes or Status
125+
success=span.status.is_ok,
115126
properties={},
116127
)
117128
envelope.data = MonitorBase(
118129
base_data=data, base_type="RemoteDependencyData"
119130
)
120131
if span.kind in (SpanKind.CLIENT, SpanKind.PRODUCER):
121-
if (
122-
"component" in span.attributes
123-
and span.attributes["component"] == "http"
124-
):
125-
# TODO: check other component types (e.g. db)
132+
if "http.method" in span.attributes: # HTTP
126133
data.type = "HTTP"
127-
if "http.url" in span.attributes:
128-
url = span.attributes["http.url"]
129-
# data is the url
130-
data.data = url
131-
parse_url = urlparse(url)
132-
# TODO: error handling, probably put scheme as well
133-
# target matches authority (host:port)
134-
data.target = parse_url.netloc
135-
if "http.method" in span.attributes:
136-
# name is METHOD/path
137-
data.name = (
138-
span.attributes["http.method"] + "/" + parse_url.path
134+
if "net.peer.port" in span.attributes:
135+
name = ""
136+
if "net.peer.name" in span.attributes:
137+
name = span.attributes["net.peer.name"]
138+
elif "net.peer.ip" in span.attributes:
139+
name = str(span.attributes["net.peer.ip"])
140+
data.target = "{}:{}".format(
141+
name,
142+
str(span.attributes["net.peer.port"]),
139143
)
140-
if "http.status_code" in span.attributes:
141-
status_code = span.attributes["http.status_code"]
142-
data.result_code = str(status_code)
143-
data.success = 200 <= status_code < 400
144+
elif "http.url" in span.attributes:
145+
url = span.attributes["http.url"]
146+
# data is the url
147+
data.data = url
148+
parse_url = urlparse(url)
149+
# target matches authority (host:port)
150+
data.target = parse_url.netloc
151+
if "http.status_code" in span.attributes:
152+
status_code = span.attributes["http.status_code"]
153+
data.result_code = str(status_code)
154+
elif "db.system" in span.attributes: # Database
155+
data.type = span.attributes["db.system"]
156+
# data is the full statement
157+
if "db.statement" in span.attributes:
158+
data.data = span.attributes["db.statement"]
159+
if "db.name" in span.attributes:
160+
data.target = span.attributes["db.name"]
161+
else:
162+
data.target = span.attributes["db.system"]
163+
elif "rpc.system" in span.attributes: # Rpc
164+
data.type = "rpc.system"
165+
if "rpc.service" in span.attributes:
166+
data.target = span.attributes["rpc.service"]
167+
else:
168+
data.target = span.attributes["rpc.system"]
169+
elif "messaging.system" in span.attributes: # Messaging
170+
data.type = "Queue Message | {}" \
171+
.format(span.attributes["messaging.system"])
172+
if "net.peer.ip" in span.attributes and \
173+
"messaging.destination" in span.attributes:
174+
data.target = "{}/{}".format(
175+
span.attributes["net.peer.ip"],
176+
span.attributes["messaging.destination"]
177+
)
178+
else:
179+
data.target = span.attributes["messaging.system"]
180+
else:
181+
# TODO: Azure specific types
182+
data.type = "N/A"
144183
else: # SpanKind.INTERNAL
145184
data.type = "InProc"
146185
data.success = True
147186
for key in span.attributes:
148-
# This removes redundant data from ApplicationInsights
149-
if key.startswith("http."):
187+
# Remove Opentelemetry related span attributes from custom dimensions
188+
if key.startswith("http.") or \
189+
key.startswith("db.") or \
190+
key.startswith("rpc.") or \
191+
key.startswith("net.") or \
192+
key.startswith("messaging."):
150193
continue
151194
data.properties[key] = span.attributes[key]
152195
if span.links:

sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/tests/test_base_exporter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from microsoft.opentelemetry.exporter.azuremonitor.options import ExporterOptions
2121
from microsoft.opentelemetry.exporter.azuremonitor._generated.models import MonitorBase, TelemetryItem
2222

23-
TEST_FOLDER = os.path.abspath(".test")
23+
TEST_FOLDER = os.path.abspath(".test.base")
2424
STORAGE_PATH = os.path.join(TEST_FOLDER)
2525

2626

sdk/monitor/microsoft-opentelemetry-exporter-azuremonitor/tests/test_storage.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
_seconds,
1414
)
1515

16-
TEST_FOLDER = os.path.abspath(".test")
16+
TEST_FOLDER = os.path.abspath(".test.storage")
1717

1818
def throw(exc_type, *args, **kwargs):
1919
def func(*_args, **_kwargs):
@@ -70,6 +70,14 @@ def test_lease_error(self):
7070

7171
# pylint: disable=protected-access
7272
class TestLocalFileStorage(unittest.TestCase):
73+
@classmethod
74+
def setup_class(cls):
75+
os.makedirs(TEST_FOLDER, exist_ok=True)
76+
77+
@classmethod
78+
def tearDownClass(cls):
79+
shutil.rmtree(TEST_FOLDER, True)
80+
7381
def test_get_nothing(self):
7482
with LocalFileStorage(os.path.join(TEST_FOLDER, "test", "a")) as stor:
7583
pass

0 commit comments

Comments
 (0)