Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
**Breaking Changes**
- This library is renamed to `microsoft-opentelemetry-exporter-azuremonitor`.

- Change span to envelope conversion to adhere to common schema and other languages
([#15344](https://github.com/Azure/azure-sdk-for-python/pull/15344))

## 0.5b.0 (2020-09-24)

- Change epoch for live metrics
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
from opentelemetry.sdk.util import ns_to_iso_str
from opentelemetry.trace import Span, SpanKind
from opentelemetry.trace.status import StatusCode

from microsoft.opentelemetry.exporter.azuremonitor import utils
from microsoft.opentelemetry.exporter.azuremonitor._generated.models import (
Expand Down Expand Up @@ -78,75 +77,119 @@ def convert_span_to_envelope(span: Span) -> TelemetryItem:
if span.kind in (SpanKind.CONSUMER, SpanKind.SERVER):
envelope.name = "Microsoft.ApplicationInsights.Request"
data = RequestData(
name=span.name,
id="{:016x}".format(span.context.span_id),
duration=utils.ns_to_duration(span.end_time - span.start_time),
response_code=str(span.status.status_code.value),
success=span.status.status_code
== StatusCode.OK, # Modify based off attributes or Status
success=span.status.is_ok,
properties={},
)
envelope.data = MonitorBase(base_data=data, base_type="RequestData")
if "http.method" in span.attributes:
data.name = span.attributes["http.method"]
if "http.method" in span.attributes: # HTTP
if "http.route" in span.attributes:
data.name = data.name + " " + span.attributes["http.route"]
envelope.tags["ai.operation.name"] = data.name
data.properties["request.name"] = data.name
envelope.tags["ai.operation.name"] = span.attributes["http.route"]
elif "http.path" in span.attributes:
data.properties["request.name"] = (
data.name + " " + span.attributes["http.path"]
)
if "http.url" in span.attributes:
data.url = span.attributes["http.url"]
data.properties["request.url"] = span.attributes["http.url"]
if "http.status_code" in span.attributes:
status_code = span.attributes["http.status_code"]
data.response_code = str(status_code)
data.success = 200 <= status_code < 400
envelope.tags["ai.operation.name"] = span.attributes["http.path"]
else:
envelope.tags["ai.operation.name"] = span.name

if "http.url" in span.attributes:
data.url = span.attributes["http.url"]
data.properties["request.url"] = span.attributes["http.url"]
if "http.status_code" in span.attributes:
status_code = span.attributes["http.status_code"]
data.response_code = str(status_code)
elif "messaging.system" in span.attributes: # Messaging
envelope.tags["ai.operation.name"] = span.name

if "messaging.destination" in span.attributes:
if "net.peer.name" in span.attributes:
data.properties["source"] = "{}/{}".format(
span.attributes["net.peer.name"],
span.attributes["messaging.destination"],
)
elif "net.peer.ip" in span.attributes:
data.properties["source"] = "{}/{}".format(
span.attributes["net.peer.ip"],
span.attributes["messaging.destination"],
)
else:
data.properties["source"] = span.attributes["messaging.destination"]
else:
envelope.name = "Microsoft.ApplicationInsights.RemoteDependency"
data = RemoteDependencyData(
name=span.name,
id="{:016x}".format(span.context.span_id),
result_code=str(span.status.status_code.value),
duration=utils.ns_to_duration(span.end_time - span.start_time),
success=span.status.status_code
== StatusCode.OK, # Modify based off attributes or Status
success=span.status.is_ok,
properties={},
)
envelope.data = MonitorBase(
base_data=data, base_type="RemoteDependencyData"
)
if span.kind in (SpanKind.CLIENT, SpanKind.PRODUCER):
if (
"component" in span.attributes
and span.attributes["component"] == "http"
):
# TODO: check other component types (e.g. db)
if "http.method" in span.attributes: # HTTP
data.type = "HTTP"
if "http.url" in span.attributes:
url = span.attributes["http.url"]
# data is the url
data.data = url
parse_url = urlparse(url)
# TODO: error handling, probably put scheme as well
# target matches authority (host:port)
data.target = parse_url.netloc
if "http.method" in span.attributes:
# name is METHOD/path
data.name = (
span.attributes["http.method"] + "/" + parse_url.path
if "net.peer.port" in span.attributes:
name = ""
if "net.peer.name" in span.attributes:
name = span.attributes["net.peer.name"]
elif "net.peer.ip" in span.attributes:
name = str(span.attributes["net.peer.ip"])
data.target = "{}:{}".format(
name,
str(span.attributes["net.peer.port"]),
)
if "http.status_code" in span.attributes:
status_code = span.attributes["http.status_code"]
data.result_code = str(status_code)
data.success = 200 <= status_code < 400
elif "http.url" in span.attributes:
url = span.attributes["http.url"]
# data is the url
data.data = url
parse_url = urlparse(url)
# target matches authority (host:port)
data.target = parse_url.netloc
if "http.status_code" in span.attributes:
status_code = span.attributes["http.status_code"]
data.result_code = str(status_code)
elif "db.system" in span.attributes: # Database
data.type = span.attributes["db.system"]
# data is the full statement
if "db.statement" in span.attributes:
data.data = span.attributes["db.statement"]
if "db.name" in span.attributes:
data.target = span.attributes["db.name"]
else:
data.target = span.attributes["db.system"]
elif "rpc.system" in span.attributes: # Rpc
data.type = "rpc.system"
if "rpc.service" in span.attributes:
data.target = span.attributes["rpc.service"]
else:
data.target = span.attributes["rpc.system"]
elif "messaging.system" in span.attributes: # Messaging
data.type = "Queue Message | {}" \
.format(span.attributes["messaging.system"])
if "net.peer.ip" in span.attributes and \
"messaging.destination" in span.attributes:
data.target = "{}/{}".format(
span.attributes["net.peer.ip"],
span.attributes["messaging.destination"]
)
else:
data.target = span.attributes["messaging.system"]
else:
# TODO: Azure specific types
data.type = "N/A"
else: # SpanKind.INTERNAL
data.type = "InProc"
data.success = True
for key in span.attributes:
# This removes redundant data from ApplicationInsights
if key.startswith("http."):
# Remove Opentelemetry related span attributes from custom dimensions
if key.startswith("http.") or \
key.startswith("db.") or \
key.startswith("rpc.") or \
key.startswith("net.") or \
key.startswith("messaging."):
continue
data.properties[key] = span.attributes[key]
if span.links:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from microsoft.opentelemetry.exporter.azuremonitor.options import ExporterOptions
from microsoft.opentelemetry.exporter.azuremonitor._generated.models import MonitorBase, TelemetryItem

TEST_FOLDER = os.path.abspath(".test")
TEST_FOLDER = os.path.abspath(".test.base")
STORAGE_PATH = os.path.join(TEST_FOLDER)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
_seconds,
)

TEST_FOLDER = os.path.abspath(".test")
TEST_FOLDER = os.path.abspath(".test.storage")

def throw(exc_type, *args, **kwargs):
def func(*_args, **_kwargs):
Expand Down Expand Up @@ -70,6 +70,14 @@ def test_lease_error(self):

# pylint: disable=protected-access
class TestLocalFileStorage(unittest.TestCase):
@classmethod
def setup_class(cls):
os.makedirs(TEST_FOLDER, exist_ok=True)

@classmethod
def tearDownClass(cls):
shutil.rmtree(TEST_FOLDER, True)

def test_get_nothing(self):
with LocalFileStorage(os.path.join(TEST_FOLDER, "test", "a")) as stor:
pass
Expand Down
Loading