Skip to content
This repository has been archived by the owner on Nov 16, 2023. It is now read-only.

Commit

Permalink
Merge pull request #121 from lzchen/release2
Browse files Browse the repository at this point in the history
  • Loading branch information
lzchen authored Sep 24, 2020
2 parents 7f892e8 + 52b2eb6 commit a66b8d9
Show file tree
Hide file tree
Showing 35 changed files with 350 additions and 397 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ dist: xenial
language: python

python:
- '3.4'
- '3.5'
- '3.6'
- '3.7'
Expand Down
8 changes: 8 additions & 0 deletions azure_monitor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## Unreleased

## 0.5b.0
Released 2020-09-24

- Change epoch for live metrics
([#115](https://github.com/microsoft/opentelemetry-azure-monitor-python/pull/115))
- Dropping support for Python 3.4
([#117](https://github.com/microsoft/opentelemetry-azure-monitor-python/pull/117))

## 0.4b.0
Released 2020-06-29

Expand Down
4 changes: 3 additions & 1 deletion azure_monitor/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# OpenTelemetry Azure Monitor SDKs and Exporters
# OpenTelemetry Azure Monitor SDKs and Exporters (Private preview)

[![PyPI version](https://badge.fury.io/py/opentelemetry-azure-monitor.svg)](https://badge.fury.io/py/opentelemetry-azure-monitor)

The OpenTelemetry Azure Monitor SDK and exporter are in private preview. They are not recommended for a production environment.

## Installation

```sh
Expand Down
11 changes: 2 additions & 9 deletions azure_monitor/examples/metrics/auto_collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,11 @@
from opentelemetry.sdk.trace import TracerProvider

from azure_monitor import AzureMonitorMetricsExporter
from azure_monitor.sdk.auto_collection import (
AutoCollection,
AzureMetricsSpanProcessor,
)
from azure_monitor.sdk.auto_collection import AutoCollection

# Add Span Processor to get metrics about traces
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer_provider().get_tracer(__name__)
span_processor = AzureMetricsSpanProcessor()
trace.get_tracer_provider().add_span_processor(span_processor)

metrics.set_meter_provider(MeterProvider())
meter = metrics.get_meter(__name__)
Expand All @@ -25,9 +20,7 @@
testing_label_set = {"environment": "testing"}

# Automatically collect standard metrics
auto_collection = AutoCollection(
meter=meter, labels=testing_label_set, span_processor=span_processor
)
auto_collection = AutoCollection(meter=meter, labels=testing_label_set)

metrics.get_meter_provider().start_pipeline(meter, exporter, 2)

Expand Down
2 changes: 1 addition & 1 deletion azure_monitor/examples/metrics/observer.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ def get_ram_usage_callback(observer):
label_keys=(),
)

input("Metrics will be printed soon. Press a key to finish...\n")
input("Press any key to exit...")
1 change: 0 additions & 1 deletion azure_monitor/examples/metrics/simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
unit="1",
value_type=int,
metric_type=Counter,
label_keys=("environment",),
)

testing_labels = {"environment": "testing"}
Expand Down
4 changes: 2 additions & 2 deletions azure_monitor/examples/traces/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# pylint: disable=no-name-in-module
import requests
from opentelemetry import trace
from opentelemetry.ext.requests import RequestsInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchExportSpanProcessor

Expand All @@ -21,6 +21,6 @@
)
trace.get_tracer_provider().add_span_processor(span_processor)

response = requests.get(url="http://127.0.0.1:8080/")
response = requests.get(url="http://example.com/")

input("Press any key to exit...")
28 changes: 0 additions & 28 deletions azure_monitor/examples/traces/request.py

This file was deleted.

4 changes: 2 additions & 2 deletions azure_monitor/examples/traces/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
# pylint: disable=no-name-in-module
import requests
from opentelemetry import trace
from opentelemetry.ext.flask import FlaskInstrumentor
from opentelemetry.ext.requests import RequestsInstrumentor
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchExportSpanProcessor

Expand Down
7 changes: 3 additions & 4 deletions azure_monitor/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,19 @@ classifiers =
License :: OSI Approved :: MIT License
Programming Language :: Python
Programming Language :: Python :: 3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8

[options]
python_requires = >=3.4
python_requires = >=3.5
package_dir=
=src
packages=find_namespace:
install_requires =
opentelemetry-api == 0.10b0
opentelemetry-sdk == 0.10b0
opentelemetry-api == 0.13b0
opentelemetry-sdk == 0.13b0
psutil >= 5.6.3
requests ~= 2.0

Expand Down
1 change: 1 addition & 0 deletions azure_monitor/src/azure_monitor/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ def parse_connection_string(connection_string) -> typing.Dict:
# Convert keys to lower-case due to case type-insensitive checking
result = {key.lower(): value for key, value in result.items()}
except Exception:
# pylint: disable=raise-missing-from
raise ValueError("Invalid connection string")
# Validate authorization
auth = result.get("authorization")
Expand Down
17 changes: 3 additions & 14 deletions azure_monitor/src/azure_monitor/sdk/auto_collection/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@

from opentelemetry.metrics import Meter

from azure_monitor.sdk.auto_collection.dependency_metrics import (
DependencyMetrics,
)
from azure_monitor.sdk.auto_collection.metrics_span_processor import (
AzureMetricsSpanProcessor,
)
Expand All @@ -21,7 +18,6 @@
"AutoCollection",
"AutoCollectionType",
"AzureMetricsSpanProcessor",
"DependencyMetrics",
"RequestMetrics",
"PerformanceMetrics",
]
Expand All @@ -36,14 +32,7 @@ class AutoCollection:
labels: Dictionary of labels
"""

def __init__(
self,
meter: Meter,
labels: Dict[str, str],
span_processor: AzureMetricsSpanProcessor,
):
col_type = AutoCollectionType.STANDARD_METRICS
def __init__(self, meter: Meter, labels: Dict[str, str]):
col_type = AutoCollectionType.PERF_COUNTER
self._performance_metrics = PerformanceMetrics(meter, labels, col_type)
self._request_metrics = RequestMetrics(
meter, labels, span_processor, col_type
)
self._request_metrics = RequestMetrics(meter, labels, col_type)
Original file line number Diff line number Diff line change
@@ -1,16 +1,53 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import logging
import threading
import time
from typing import Dict

import requests
from opentelemetry import context
from opentelemetry.metrics import Meter, Observer
from opentelemetry.sdk.metrics import UpDownSumObserver

from azure_monitor.sdk.auto_collection.metrics_span_processor import (
AzureMetricsSpanProcessor,
)

_dependency_lock = threading.Lock()
logger = logging.getLogger(__name__)
dependency_map = dict()
ORIGINAL_REQUEST = requests.Session.request


def dependency_patch(*args, **kwargs) -> None:
start_time = time.time()

try:
result = ORIGINAL_REQUEST(*args, **kwargs)
except Exception as exc: # pylint: disable=broad-except
exception = exc
result = getattr(exc, "response", None)
end_time = time.time()

# Only collect request metric if sent from non-exporter thread
if context.get_value("suppress_instrumentation") is None:
# We don't want multiple threads updating this at once
with _dependency_lock:
try:
# Update duration
duration = dependency_map.get("duration", 0)
dependency_map["duration"] = duration + (end_time - start_time)
# Update count
count = dependency_map.get("count", 0)
dependency_map["count"] = count + 1
# Update failed count
if (
result is not None
and result.status_code < 200
and result.status_code >= 300
) or exception is not None:
failed_count = dependency_map.get("failed_count", 0)
dependency_map["failed_count"] = failed_count + 1
except Exception: # pylint: disable=broad-except
logger.warning("Error handling failed dependency metrics.")
return result


class DependencyMetrics:
Expand All @@ -20,26 +57,20 @@ class DependencyMetrics:
Args:
meter: OpenTelemetry Meter
labels: Dictionary of labels
span_processor: Azure Metrics Span Processor
collection_type: Standard or Live Metrics
"""

def __init__(
self,
meter: Meter,
labels: Dict[str, str],
span_processor: AzureMetricsSpanProcessor,
):
def __init__(self, meter: Meter, labels: Dict[str, str]):
self._meter = meter
self._labels = labels
self._span_processor = span_processor
# Patch requests
requests.Session.request = dependency_patch

meter.register_observer(
callback=self._track_dependency_duration,
name="\\ApplicationInsights\\Dependency Call Duration",
description="Average Outgoing Requests duration",
unit="milliseconds",
value_type=int,
value_type=float,
observer_type=UpDownSumObserver,
)
meter.register_observer(
Expand All @@ -66,11 +97,11 @@ def _track_dependency_rate(self, observer: Observer) -> None:
using the requests library within an elapsed time and dividing
that value over the elapsed time.
"""
current_count = self._span_processor.dependency_count
current_count = dependency_map.get("count", 0)
current_time = time.time()
last_count = dependency_map.get("last_count", 0)
last_time = dependency_map.get("last_time")
last_result = dependency_map.get("last_result", 0)
last_result = dependency_map.get("last_result", 0.0)

try:
# last_time is None the very first time this function is called
Expand All @@ -95,29 +126,27 @@ def _track_dependency_duration(self, observer: Observer) -> None:
Calculated by getting the time it takes to make an outgoing request
and dividing over the amount of outgoing requests over an elapsed time.
"""
last_average_duration = dependency_map.get("last_average_duration", 0)
interval_duration = (
self._span_processor.dependency_duration
- dependency_map.get("last_duration", 0)
last_average_duration = dependency_map.get(
"last_average_duration", 0.0
)
interval_count = (
self._span_processor.dependency_count
- dependency_map.get("last_count", 0)
interval_duration = dependency_map.get(
"duration", 0.0
) - dependency_map.get("last_duration", 0.0)
interval_count = dependency_map.get("count", 0) - dependency_map.get(
"last_count", 0
)
try:
result = interval_duration / interval_count
dependency_map[
"last_count"
] = self._span_processor.dependency_count
dependency_map["last_count"] = dependency_map.get("count", 0)
dependency_map["last_average_duration"] = result
dependency_map[
"last_duration"
] = self._span_processor.dependency_duration
observer.observe(int(result), self._labels)
dependency_map["last_duration"] = dependency_map.get(
"duration", 0.0
)
observer.observe(result, self._labels)
except ZeroDivisionError:
# If interval_count is 0, exporter call made too close to previous
# Return the previous result if this is the case
observer.observe(int(last_average_duration), self._labels)
observer.observe(last_average_duration, self._labels)

def _track_failure_rate(self, observer: Observer) -> None:
""" Track Failed Dependency rate
Expand All @@ -126,11 +155,11 @@ def _track_failure_rate(self, observer: Observer) -> None:
using the requests library within an elapsed time and dividing
that value over the elapsed time.
"""
current_failed_count = self._span_processor.failed_dependency_count
current_failed_count = dependency_map.get("failed_count", 0)
current_time = time.time()
last_failed_count = dependency_map.get("last_failed_count", 0)
last_time = dependency_map.get("last_time")
last_result = dependency_map.get("last_result", 0)
last_result = dependency_map.get("last_result", 0.0)

try:
# last_time is None the very first time this function is called
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,8 @@ def __init__(
):
col_type = AutoCollectionType.LIVE_METRICS
self._performance_metrics = PerformanceMetrics(meter, labels, col_type)
self._dependency_metrics = DependencyMetrics(
meter, labels, span_processor
)
self._request_metrics = RequestMetrics(
meter, labels, span_processor, col_type
)
self._dependency_metrics = DependencyMetrics(meter, labels)
self._request_metrics = RequestMetrics(meter, labels, col_type)
self._manager = LiveMetricsManager(
meter, instrumentation_key, span_processor
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def export(
return MetricsExportResult.SUCCESS

except Exception: # pylint: disable=broad-except
logger.exception("Exception occurred while exporting the data.")
logger.warning("Exception occurred while exporting the data.")

return MetricsExportResult.FAILURE

Expand Down
Loading

0 comments on commit a66b8d9

Please sign in to comment.