Skip to content
This repository was archived by the owner on Sep 17, 2025. It is now read-only.
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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

## Unreleased

## 0.7.10
Released 2020-06-29

- Updated `azure` module
([#903](https://github.com/census-instrumentation/opencensus-python/pull/903),
[#925](https://github.com/census-instrumentation/opencensus-python/pull/925))

- Updated `stackdriver` module
([#919](https://github.com/census-instrumentation/opencensus-python/pull/919))

## 0.7.9
Released 2020-06-17

Expand Down
7 changes: 7 additions & 0 deletions context/opencensus-context/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## Unreleased

## opencensus-ext-context 0.1.2

## 0.1.2
Released 2020-06-29

- Release source distribution

## 0.1.1
Released 2019-05-31

Expand Down
11 changes: 10 additions & 1 deletion contrib/opencensus-ext-azure/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,18 @@

## Unreleased

- Attach rate metrics via Heartbeat for Web and Function apps
([#930](https://github.com/census-instrumentation/opencensus-python/pull/930))
- Attach rate metrics for VM
([#935](https://github.com/census-instrumentation/opencensus-python/pull/935))

## 1.0.4
Released 2020-06-29

- Remove dependency rate from standard metrics
([#903](https://github.com/census-instrumentation/opencensus-python/pull/903))
- Implement customEvents using AzureEventHandler
([#925](https://github.com/census-instrumentation/opencensus-python/pull/925))

## 1.0.3
Released 2020-06-17
Expand All @@ -13,7 +23,6 @@ Released 2020-06-17
- Add support to initialize azure exporters with proxies
([#902](https://github.com/census-instrumentation/opencensus-python/pull/902))


## 1.0.2
Released 2020-02-04

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
HeartbeatMetric,
)
from opencensus.metrics import transport
from opencensus.metrics.export.gauge import Registry
from opencensus.metrics.export.metric_producer import MetricProducer

_HEARTBEAT_METRICS = None
Expand All @@ -43,20 +42,13 @@ def enable_heartbeat_metrics(connection_string, ikey):
exporter.options.export_interval)


def register_metrics():
registry = Registry()
metric = HeartbeatMetric()
registry.add_gauge(metric())
return registry


class AzureHeartbeatMetricsProducer(MetricProducer):
"""Implementation of the producer of heartbeat metrics.

Includes Azure attach rate metrics, implemented using gauges.
"""
def __init__(self):
self.registry = register_metrics()
self._heartbeat = HeartbeatMetric()

def get_metrics(self):
return self.registry.get_metrics()
return self._heartbeat.get_metrics()
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,60 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import datetime
import json
import os
import platform
from collections import OrderedDict

import requests

from opencensus.common.version import __version__ as opencensus_version
from opencensus.ext.azure.common.version import __version__ as ext_version
from opencensus.metrics.export.gauge import LongGauge
from opencensus.metrics.label_key import LabelKey
from opencensus.metrics.label_value import LabelValue

_AIMS_URI = "http://169.254.169.254/metadata/instance/compute"
_AIMS_API_VERSION = "api-version=2017-12-01"
_AIMS_FORMAT = "format=json"


class HeartbeatMetric:
NAME = "Heartbeat"

def __init__(self):
self.vm_data = {}
self.is_vm = False
self.properties = OrderedDict()
self.update_properties()
self.heartbeat = LongGauge(
HeartbeatMetric.NAME,
'Heartbeat metric with custom dimensions',
'count',
list(self.properties.keys()),
)
self.heartbeat.get_or_create_time_series(
list(self.properties.values())
)

def get_metrics(self):
if self.is_vm:
# Only need to update if in vm (properties could change)
self.properties.clear()
self.update_properties()
self.heartbeat = LongGauge(
HeartbeatMetric.NAME,
'Heartbeat metric with custom dimensions',
'count',
list(self.properties.keys()),
)
self.heartbeat.get_or_create_time_series(
list(self.properties.values())
)
return [self.heartbeat.get_metric(datetime.datetime.utcnow())]

def update_properties(self):
self.properties[LabelKey("sdk", '')] = LabelValue(
'py{}:oc{}:ext{}'.format(
platform.python_version(),
Expand All @@ -48,17 +86,34 @@ def __init__(self):
# Function apps
self.properties[LabelKey("azfunction_appId", '')] = \
LabelValue(os.environ.get("WEBSITE_HOSTNAME"))
elif self.get_azure_compute_metadata():
# VM
if self.vm_data:
self.properties[LabelKey("azInst_vmId", '')] = \
LabelValue(self.vm_data.get("vmId", ''))
self.properties[LabelKey("azInst_subscriptionId", '')] = \
LabelValue(self.vm_data.get("subscriptionId", ''))
self.properties[LabelKey("azInst_osType", '')] = \
LabelValue(self.vm_data.get("osType", ''))

def __call__(self):
""" Returns a derived gauge for the heartbeat metric.
def get_azure_compute_metadata(self):
try:
request_url = "{0}?{1}&{2}".format(
_AIMS_URI, _AIMS_API_VERSION, _AIMS_FORMAT)
response = requests.get(request_url, headers={"MetaData": "True"})
except requests.exceptions.ConnectionError:
# Not in VM
self.is_vm = False

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is connection error enough to determine is not a VM?, maybe adding generic except as well to be sure doesn't think is a VM

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct about the second except. I'll add it to catch anything else.

return False
except requests.exceptions.RequestException:
pass # retry

:rtype: :class:`opencensus.metrics.export.gauge.LongGauge`
:return: The gauge representing the heartbeat metric
"""
gauge = LongGauge(
HeartbeatMetric.NAME,
'Heartbeat metric with custom dimensions',
'count',
list(self.properties.keys()))
gauge.get_or_create_time_series(list(self.properties.values()))
return gauge
self.is_vm = True
try:
text = response.text
self.vm_data = json.loads(text)
except Exception: # pylint: disable=broad-except
# Error in reading response body, retry
pass

return True
145 changes: 113 additions & 32 deletions contrib/opencensus-ext-azure/tests/test_azure_heartbeat_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,40 +12,53 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import json
import os
import platform
import unittest

import mock
import requests

from opencensus.common.version import __version__ as opencensus_version
from opencensus.ext.azure.common.version import __version__ as ext_version
from opencensus.ext.azure.metrics_exporter import heartbeat_metrics


class MockResponse(object):
def __init__(self, status_code, text):
self.status_code = status_code
self.text = text


def throw(exc_type, *args, **kwargs):
def func(*_args, **_kwargs):
raise exc_type(*args, **kwargs)
return func


class TestHeartbeatMetrics(unittest.TestCase):
def setUp(self):
# pylint: disable=protected-access
heartbeat_metrics._HEARTBEAT_METRICS = None

@mock.patch('opencensus.ext.azure.metrics_exporter'
'.heartbeat_metrics.register_metrics')
def test_producer_ctor(self, avail_mock):
heartbeat_metrics.AzureHeartbeatMetricsProducer()

self.assertEqual(len(avail_mock.call_args_list), 1)
def test_producer_ctor(self):
producer = heartbeat_metrics.AzureHeartbeatMetricsProducer()
# pylint: disable=protected-access
metric = producer._heartbeat
self.assertTrue(
isinstance(
metric,
heartbeat_metrics.heartbeat.HeartbeatMetric
)
)

def test_producer_get_metrics(self):
producer = heartbeat_metrics.AzureHeartbeatMetricsProducer()
metrics = producer.get_metrics()

self.assertEqual(len(metrics), 1)

def test_register_metrics(self):
registry = heartbeat_metrics.register_metrics()

self.assertEqual(len(registry.get_metrics()), 1)

@mock.patch('opencensus.metrics.transport.get_exporter_thread')
def test_enable_heartbeat_metrics(self, transport_mock):
ikey = '12345678-1234-5678-abcd-12345678abcd'
Expand All @@ -61,7 +74,7 @@ def test_enable_heartbeat_metrics(self, transport_mock):
transport_mock.assert_called()

@mock.patch('opencensus.metrics.transport.get_exporter_thread')
def test_enable_heartbeat_metrics_exits(self, transport_mock):
def test_enable_heartbeat_metrics_exists(self, transport_mock):
# pylint: disable=protected-access
producer = heartbeat_metrics.AzureHeartbeatMetricsProducer()
heartbeat_metrics._HEARTBEAT_METRICS = producer
Expand All @@ -85,6 +98,23 @@ def test_heartbeat_metric_init(self):
ext_version,
))
self.assertEqual(values[1].value, platform.system())
gauge = metric.heartbeat

self.assertEqual(gauge.descriptor.name, 'Heartbeat')
self.assertEqual(
gauge.descriptor.description,
'Heartbeat metric with custom dimensions'
)
self.assertEqual(gauge.descriptor.unit, 'count')
self.assertEqual(gauge.descriptor._type, 1)
self.assertEqual(
gauge.descriptor.label_keys,
list(metric.properties.keys())
)
self.assertEqual(
gauge._len_label_keys,
len(metric.properties.keys())
)

@mock.patch.dict(
os.environ,
Expand Down Expand Up @@ -143,23 +173,74 @@ def test_heartbeat_metric_init_functionapp(self):
self.assertEqual(keys[2].key, "azfunction_appId")
self.assertEqual(values[2].value, "host_name")

def test_heartbeat_metric(self):
# pylint: disable=protected-access
metric = heartbeat_metrics.HeartbeatMetric()
gauge = metric()

self.assertEqual(gauge.descriptor.name, 'Heartbeat')
self.assertEqual(
gauge.descriptor.description,
'Heartbeat metric with custom dimensions'
)
self.assertEqual(gauge.descriptor.unit, 'count')
self.assertEqual(gauge.descriptor._type, 1)
self.assertEqual(
gauge.descriptor.label_keys,
list(metric.properties.keys())
)
self.assertEqual(
gauge._len_label_keys,
len(metric.properties.keys())
)
def test_heartbeat_metric_init_vm(self):
with mock.patch('requests.get') as get:
get.return_value = MockResponse(
200,
json.dumps(
{
'vmId': 5,
'subscriptionId': 3,
'osType': 'Linux'
}
)
)
metric = heartbeat_metrics.HeartbeatMetric()
self.assertTrue(metric.is_vm)
self.assertEqual(metric.NAME, 'Heartbeat')
keys = list(metric.properties.keys())
values = list(metric.properties.values())
self.assertEqual(len(keys), 5)
self.assertEqual(len(keys), len(values))
self.assertEqual(keys[0].key, "sdk")
self.assertEqual(keys[1].key, "osType")
self.assertEqual(values[0].value, 'py{}:oc{}:ext{}'.format(
platform.python_version(),
opencensus_version,
ext_version,
))
self.assertEqual(values[1].value, platform.system())
self.assertEqual(keys[2].key, "azInst_vmId")
self.assertEqual(values[2].value, 5)
self.assertEqual(keys[3].key, "azInst_subscriptionId")
self.assertEqual(values[3].value, 3)
self.assertEqual(keys[4].key, "azInst_osType")
self.assertEqual(values[4].value, "Linux")

def test_heartbeat_metric_not_vm(self):
with mock.patch(
'requests.get',
throw(requests.exceptions.ConnectionError)
):
metric = heartbeat_metrics.HeartbeatMetric()
self.assertFalse(metric.is_vm)
self.assertEqual(metric.NAME, 'Heartbeat')
keys = list(metric.properties.keys())
self.assertEqual(len(keys), 2)

def test_heartbeat_metric_vm_error_response(self):
with mock.patch('requests.get') as get:
get.return_value = MockResponse(
200,
json.dumps(
{
'vmId': 5,
'subscriptionId': 3,
'osType': 'Linux'
}
)
)
metric = heartbeat_metrics.HeartbeatMetric()
self.assertTrue(metric.is_vm)
keys = list(metric.properties.keys())
self.assertEqual(len(keys), 5)
with mock.patch(
'requests.get',
throw(Exception)
):
metric.vm_data.clear()
self.assertTrue(metric.is_vm)
self.assertEqual(len(metric.vm_data), 0)
self.assertTrue(metric.is_vm)
keys = list(metric.properties.keys())
self.assertEqual(len(keys), 5)
3 changes: 3 additions & 0 deletions contrib/opencensus-ext-stackdriver/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

## 0.7.3
Released 2020-06-29

- Add mean property for distribution values
([#919](https://github.com/census-instrumentation/opencensus-python/pull/919))

Expand Down