Skip to content

Commit fa55c2d

Browse files
authored
Refactor out target and data mapping logic to trace utils (Azure#37897)
1 parent 8829540 commit fa55c2d

File tree

4 files changed

+235
-151
lines changed

4 files changed

+235
-151
lines changed

sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# Release History
22

3+
## 1.0.0b32 (Unreleased)
4+
5+
### Features Added
6+
7+
### Breaking Changes
8+
9+
### Bugs Fixed
10+
11+
### Other Changes
12+
13+
- Refactor trace mapping logic for target and data into trace utils
14+
([#37897](https://github.com/Azure/azure-sdk-for-python/pull/37897))
15+
316
## 1.0.0b31 (2024-10-08)
417

518
### Features Added

sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
# license information.
66
# --------------------------------------------------------------------------
77

8-
VERSION = "1.0.0b31"
8+
VERSION = "1.0.0b32"

sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py

Lines changed: 38 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
import json
55
import logging
66
from time import time_ns
7-
from typing import Dict, List, Optional, Sequence, Any
7+
from typing import Any, Dict, List, Sequence
88
from urllib.parse import urlparse
99

10-
from opentelemetry.util.types import Attributes
1110
from opentelemetry.semconv.trace import DbSystemValues, SpanAttributes
1211
from opentelemetry.sdk.resources import Resource
1312
from opentelemetry.sdk.trace import ReadableSpan
@@ -43,6 +42,8 @@
4342
BaseExporter,
4443
ExportResult,
4544
)
45+
from . import _utils as trace_utils
46+
4647

4748
_logger = logging.getLogger(__name__)
4849

@@ -211,7 +212,7 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem:
211212
envelope.tags[ContextTagKeys.AI_LOCATION_IP] = span.attributes[SpanAttributes.NET_PEER_IP]
212213
if _AZURE_SDK_NAMESPACE_NAME in span.attributes: # Azure specific resources
213214
# Currently only eventhub and servicebus are supported (kind CONSUMER)
214-
data.source = _get_azure_sdk_target_source(span.attributes)
215+
data.source = trace_utils._get_azure_sdk_target_source(span.attributes)
215216
if span.links:
216217
total = 0
217218
for link in span.links:
@@ -345,91 +346,39 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem:
345346
port = span.attributes[SpanAttributes.NET_PEER_PORT]
346347
# TODO: check default port for rpc
347348
# This logic assumes default ports never conflict across dependency types
348-
# type: ignore
349-
if port != _get_default_port_http(str(span.attributes.get(SpanAttributes.HTTP_SCHEME))) and \
350-
port != _get_default_port_db(str(span.attributes.get(SpanAttributes.DB_SYSTEM))):
349+
# type: ignore
350+
if port != trace_utils._get_default_port_http(
351+
str(span.attributes.get(SpanAttributes.HTTP_SCHEME))) and \
352+
port != trace_utils._get_default_port_db(str(span.attributes.get(SpanAttributes.DB_SYSTEM))):
351353
target = "{}:{}".format(target, port)
352354
if span.kind is SpanKind.CLIENT:
353355
if _AZURE_SDK_NAMESPACE_NAME in span.attributes: # Azure specific resources
354356
# Currently only eventhub and servicebus are supported
355357
# https://github.com/Azure/azure-sdk-for-python/issues/9256
356358
data.type = span.attributes[_AZURE_SDK_NAMESPACE_NAME]
357-
data.target = _get_azure_sdk_target_source(span.attributes)
359+
data.target = trace_utils._get_azure_sdk_target_source(span.attributes)
358360
elif SpanAttributes.HTTP_METHOD in span.attributes: # HTTP
359361
data.type = "HTTP"
360362
if SpanAttributes.HTTP_USER_AGENT in span.attributes:
361363
# TODO: Not exposed in Swagger, need to update def
362364
envelope.tags["ai.user.userAgent"] = span.attributes[SpanAttributes.HTTP_USER_AGENT]
363-
scheme = span.attributes.get(SpanAttributes.HTTP_SCHEME)
364-
# url
365-
url = None
366-
if SpanAttributes.HTTP_URL in span.attributes:
367-
url = span.attributes[SpanAttributes.HTTP_URL]
368-
elif scheme and SpanAttributes.HTTP_TARGET in span.attributes:
369-
http_target = span.attributes[SpanAttributes.HTTP_TARGET]
370-
if SpanAttributes.HTTP_HOST in span.attributes:
371-
url = "{}://{}{}".format(
372-
str(scheme),
373-
span.attributes[SpanAttributes.HTTP_HOST],
374-
http_target,
375-
)
376-
elif SpanAttributes.NET_PEER_PORT in span.attributes:
377-
peer_port = span.attributes[SpanAttributes.NET_PEER_PORT]
378-
if SpanAttributes.NET_PEER_NAME in span.attributes:
379-
peer_name = span.attributes[SpanAttributes.NET_PEER_NAME]
380-
url = "{}://{}:{}{}".format(
381-
scheme,
382-
peer_name,
383-
peer_port,
384-
http_target,
385-
)
386-
elif SpanAttributes.NET_PEER_IP in span.attributes:
387-
peer_ip = span.attributes[SpanAttributes.NET_PEER_IP]
388-
url = "{}://{}:{}{}".format(
389-
scheme,
390-
peer_ip,
391-
peer_port,
392-
http_target,
393-
)
394-
target_from_url = None
395-
path = ""
365+
scheme = trace_utils._get_scheme_for_http_dependency(span.attributes)
366+
url = trace_utils._get_url_for_http_dependency(scheme, span.attributes)
367+
# data
396368
if url:
397-
try:
398-
parse_url = urlparse(url)
399-
path = parse_url.path
400-
if not path:
401-
path = "/"
402-
if parse_url.port and parse_url.port == _get_default_port_http(str(scheme)):
403-
target_from_url = parse_url.hostname
404-
else:
405-
target_from_url = parse_url.netloc
406-
except Exception: # pylint: disable=broad-except
407-
pass
369+
data.data = url
370+
target, path = trace_utils._get_target_and_path_for_http_dependency(
371+
target, # type: ignore
372+
url,
373+
scheme,
374+
span.attributes,
375+
)
408376
# http specific logic for name
409377
if path:
410378
data.name = "{} {}".format(
411379
span.attributes[SpanAttributes.HTTP_METHOD],
412380
path,
413381
)
414-
# http specific logic for target
415-
if SpanAttributes.PEER_SERVICE not in span.attributes:
416-
if SpanAttributes.HTTP_HOST in span.attributes:
417-
host = span.attributes[SpanAttributes.HTTP_HOST]
418-
try:
419-
# urlparse insists on absolute URLs starting with "//"
420-
# This logic assumes host does not include a "//"
421-
host_name = urlparse("//" + str(host))
422-
if host_name.port == _get_default_port_http(str(scheme)):
423-
target = host_name.hostname
424-
else:
425-
target = host
426-
except Exception: # pylint: disable=broad-except
427-
_logger.warning("Error while parsing hostname.")
428-
elif target_from_url:
429-
target = target_from_url
430-
# data is url
431-
if url:
432-
data.data = url
433382
status_code = span.attributes.get(SpanAttributes.HTTP_STATUS_CODE)
434383
if status_code:
435384
try:
@@ -449,7 +398,7 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem:
449398
data.type = "mongodb"
450399
elif db_system == DbSystemValues.REDIS.value:
451400
data.type = "redis"
452-
elif _is_sql_db(str(db_system)):
401+
elif trace_utils._is_sql_db(str(db_system)):
453402
data.type = "SQL"
454403
else:
455404
data.type = db_system
@@ -459,42 +408,39 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem:
459408
elif SpanAttributes.DB_OPERATION in span.attributes:
460409
data.data = span.attributes[SpanAttributes.DB_OPERATION]
461410
# db specific logic for target
462-
if SpanAttributes.DB_NAME in span.attributes:
463-
db_name = span.attributes[SpanAttributes.DB_NAME]
464-
if target is None:
465-
target = db_name
466-
else:
467-
target = "{}|{}".format(target, db_name)
468-
if target is None:
469-
target = db_system
411+
target = trace_utils._get_target_for_db_dependency(
412+
target, # type: ignore
413+
db_system, # type: ignore
414+
span.attributes,
415+
)
470416
elif SpanAttributes.MESSAGING_SYSTEM in span.attributes: # Messaging
471417
data.type = span.attributes[SpanAttributes.MESSAGING_SYSTEM]
472-
if target is None:
473-
if SpanAttributes.MESSAGING_DESTINATION in span.attributes:
474-
target = span.attributes[SpanAttributes.MESSAGING_DESTINATION]
475-
else:
476-
target = span.attributes[SpanAttributes.MESSAGING_SYSTEM]
418+
target = trace_utils._get_target_for_messaging_dependency(
419+
target, # type: ignore
420+
span.attributes,
421+
)
477422
elif SpanAttributes.RPC_SYSTEM in span.attributes: # Rpc
478423
data.type = SpanAttributes.RPC_SYSTEM
479-
if target is None:
480-
target = span.attributes[SpanAttributes.RPC_SYSTEM]
424+
target = trace_utils._get_target_for_rpc_dependency(
425+
target, # type: ignore
426+
span.attributes,
427+
)
481428
else:
482429
data.type = "N/A"
483430
elif span.kind is SpanKind.PRODUCER: # Messaging
484431
# Currently only eventhub and servicebus are supported that produce PRODUCER spans
485432
if _AZURE_SDK_NAMESPACE_NAME in span.attributes:
486433
data.type = "Queue Message | {}".format(span.attributes[_AZURE_SDK_NAMESPACE_NAME])
487-
data.target = _get_azure_sdk_target_source(span.attributes)
434+
target = trace_utils._get_azure_sdk_target_source(span.attributes)
488435
else:
489436
data.type = "Queue Message"
490437
msg_system = span.attributes.get(SpanAttributes.MESSAGING_SYSTEM)
491438
if msg_system:
492439
data.type += " | {}".format(msg_system)
493-
if target is None:
494-
if SpanAttributes.MESSAGING_DESTINATION in span.attributes:
495-
target = span.attributes[SpanAttributes.MESSAGING_DESTINATION]
496-
else:
497-
target = msg_system
440+
target = trace_utils._get_target_for_messaging_dependency(
441+
target, # type: ignore
442+
span.attributes,
443+
)
498444
else: # SpanKind.INTERNAL
499445
data.type = "InProc"
500446
if _AZURE_SDK_NAMESPACE_NAME in span.attributes:
@@ -596,54 +542,6 @@ def _convert_span_events_to_envelopes(span: ReadableSpan) -> Sequence[TelemetryI
596542

597543
return envelopes
598544

599-
# pylint:disable=too-many-return-statements
600-
def _get_default_port_db(db_system: str) -> int:
601-
if db_system == DbSystemValues.POSTGRESQL.value:
602-
return 5432
603-
if db_system == DbSystemValues.CASSANDRA.value:
604-
return 9042
605-
if db_system in (DbSystemValues.MARIADB.value, DbSystemValues.MYSQL.value):
606-
return 3306
607-
if db_system == DbSystemValues.MSSQL.value:
608-
return 1433
609-
# TODO: Add in memcached
610-
if db_system == "memcached":
611-
return 11211
612-
if db_system == DbSystemValues.DB2.value:
613-
return 50000
614-
if db_system == DbSystemValues.ORACLE.value:
615-
return 1521
616-
if db_system == DbSystemValues.H2.value:
617-
return 8082
618-
if db_system == DbSystemValues.DERBY.value:
619-
return 1527
620-
if db_system == DbSystemValues.REDIS.value:
621-
return 6379
622-
return 0
623-
624-
625-
def _get_default_port_http(scheme: str) -> int:
626-
if scheme == "http":
627-
return 80
628-
if scheme == "https":
629-
return 443
630-
return 0
631-
632-
633-
def _is_sql_db(db_system: str) -> bool:
634-
return db_system in (
635-
DbSystemValues.DB2.value,
636-
DbSystemValues.DERBY.value,
637-
DbSystemValues.MARIADB.value,
638-
DbSystemValues.MSSQL.value,
639-
DbSystemValues.ORACLE.value,
640-
DbSystemValues.SQLITE.value,
641-
DbSystemValues.OTHER_SQL.value,
642-
# spell-checker:ignore HSQLDB
643-
DbSystemValues.HSQLDB.value,
644-
DbSystemValues.H2.value,
645-
)
646-
647545

648546
def _check_instrumentation_span(span: ReadableSpan) -> None:
649547
# Special use-case for spans generated from azure-sdk services
@@ -669,16 +567,6 @@ def _is_standard_attribute(key: str) -> bool:
669567
return key in _STANDARD_AZURE_MONITOR_ATTRIBUTES
670568

671569

672-
def _get_azure_sdk_target_source(attributes: Attributes) -> Optional[str]:
673-
# Currently logic only works for ServiceBus and EventHub
674-
if attributes:
675-
peer_address = attributes.get("peer.address")
676-
destination = attributes.get("message_bus.destination")
677-
if peer_address and destination:
678-
return str(peer_address) + "/" + str(destination)
679-
return None
680-
681-
682570
def _get_trace_export_result(result: ExportResult) -> SpanExportResult:
683571
if result == ExportResult.SUCCESS:
684572
return SpanExportResult.SUCCESS

0 commit comments

Comments
 (0)