Skip to content
This repository was archived by the owner on Sep 17, 2025. It is now read-only.

Commit c809229

Browse files
lzchenreyang
authored andcommitted
Implement Standard Metrics - Free Memory (#708)
1 parent 356ca23 commit c809229

File tree

14 files changed

+288
-55
lines changed

14 files changed

+288
-55
lines changed

contrib/opencensus-ext-azure/README.rst

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,34 @@ The **Azure Monitor Metrics Exporter** allows you to export metrics to `Azure Mo
125125
if __name__ == "__main__":
126126
main()
127127
128+
The exporter also includes a set of standard metrics that are exported to Azure Monitor by default.
129+
130+
.. code:: python
131+
132+
import psutil
133+
import time
134+
135+
from opencensus.ext.azure import metrics_exporter
136+
137+
def main():
138+
# All you need is the next line. You can disable standard metrics by
139+
# passing in enable_standard_metrics=False into the constructor of
140+
# new_metrics_exporter()
141+
_exporter = metrics_exporter.new_metrics_exporter()
142+
143+
for i in range(100):
144+
print(psutil.virtual_memory())
145+
time.sleep(5)
146+
147+
print("Done recording metrics")
148+
149+
if __name__ == "__main__":
150+
main()
151+
152+
Below is a list of standard metrics that are currently available:
153+
154+
- Available memory (bytes)
155+
128156
Trace
129157
~~~~~
130158

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright 2019, OpenCensus Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import psutil
16+
import time
17+
18+
from opencensus.ext.azure import metrics_exporter
19+
20+
21+
def main():
22+
# All you need is the next line. You can disable standard metrics by
23+
# passing in enable_standard_metrics=False into the constructor of
24+
# new_metrics_exporter()
25+
_exporter = metrics_exporter.new_metrics_exporter()
26+
27+
print(_exporter.max_batch_size)
28+
for i in range(100):
29+
print(psutil.virtual_memory())
30+
time.sleep(5)
31+
32+
print("Done recording metrics")
33+
34+
35+
if __name__ == "__main__":
36+
main()

contrib/opencensus-ext-azure/opencensus/ext/azure/common/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
class Options(BaseObject):
2222
_default = BaseObject(
23+
enable_standard_metrics=True,
2324
endpoint='https://dc.services.visualstudio.com/v2/track',
2425
export_interval=15.0,
2526
grace_period=5.0,

contrib/opencensus-ext-azure/opencensus/ext/azure/metrics_exporter/__init__.py

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@
2323
from opencensus.ext.azure.common.protocol import DataPoint
2424
from opencensus.ext.azure.common.protocol import Envelope
2525
from opencensus.ext.azure.common.protocol import MetricData
26+
from opencensus.ext.azure.metrics_exporter import standard_metrics
2627
from opencensus.metrics import transport
2728
from opencensus.metrics.export.metric_descriptor import MetricDescriptorType
28-
from opencensus.stats import stats
29+
from opencensus.stats import stats as stats_module
2930
from opencensus.trace import execution_context
3031

3132
__all__ = ['MetricsExporter', 'new_metrics_exporter']
@@ -54,18 +55,17 @@ def export_metrics(self, metrics):
5455
type_ = metric.descriptor.type
5556
if type_ != MetricDescriptorType.CUMULATIVE_DISTRIBUTION:
5657
md = metric.descriptor
57-
# Each time series will be uniquely
58-
# identified by it's label values
58+
# Each time series will be uniquely identified by its
59+
# label values
5960
for time_series in metric.time_series:
60-
# Using stats, time_series should
61-
# only have one point which contains
62-
# the aggregated value
61+
# Using stats, time_series should only have one point
62+
# which contains the aggregated value
6363
data_point = self.create_data_points(
6464
time_series, md)[0]
6565
# The timestamp is when the metric was recorded
6666
time_stamp = time_series.points[0].timestamp
67-
# Get the properties using label keys from metric
68-
# and label values of the time series
67+
# Get the properties using label keys from metric and
68+
# label values of the time series
6969
properties = self.create_properties(time_series, md)
7070
envelopes.append(self.create_envelope(data_point,
7171
time_stamp,
@@ -90,9 +90,8 @@ def create_data_points(self, time_series, metric_descriptor):
9090

9191
def create_properties(self, time_series, metric_descriptor):
9292
properties = {}
93-
# We construct a properties map from the
94-
# label keys and values
95-
# We assume the ordering is already correct
93+
# We construct a properties map from the label keys and values. We
94+
# assume the ordering is already correct
9695
for i in range(len(metric_descriptor.label_keys)):
9796
if time_series.label_values[i].value is None:
9897
value = "null"
@@ -117,11 +116,10 @@ def create_envelope(self, data_point, time_stamp, properties):
117116

118117
def _transmit_without_retry(self, envelopes):
119118
# Contains logic from transport._transmit
120-
# TODO: Remove this function from exporter and
121-
# consolidate with transport._transmit to cover
122-
# all exporter use cases.
123-
# Uses cases pertain to properly handling failures
124-
# and implementing a retry policy for this exporter
119+
# TODO: Remove this function from exporter and consolidate with
120+
# transport._transmit to cover all exporter use cases. Uses cases
121+
# pertain to properly handling failures and implementing a retry
122+
# policy for this exporter.
125123
# TODO: implement retry policy
126124
"""
127125
Transmit the data envelopes to the ingestion service.
@@ -201,8 +199,8 @@ def _transmit_without_retry(self, envelopes):
201199
error['message'],
202200
envelopes[error['index']],
203201
)
204-
# show the envelopes that can be
205-
# retried manually for visibility
202+
# show the envelopes that can be retried manually for
203+
# visibility
206204
if retryable_envelopes:
207205
logger.warning(
208206
'Error while processing data. Data dropped. ' +
@@ -241,9 +239,12 @@ def _transmit_without_retry(self, envelopes):
241239

242240

243241
def new_metrics_exporter(**options):
244-
options = Options(**options)
245-
exporter = MetricsExporter(options=options)
246-
transport.get_exporter_thread(stats.stats,
242+
options_ = Options(**options)
243+
exporter = MetricsExporter(options=options_)
244+
producers = [stats_module.stats]
245+
if options_.enable_standard_metrics:
246+
producers.append(standard_metrics.producer)
247+
transport.get_exporter_thread(producers,
247248
exporter,
248-
interval=options.export_interval)
249+
interval=options_.export_interval)
249250
return exporter
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Copyright 2019, OpenCensus Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import psutil
16+
17+
from opencensus.metrics.export.gauge import DerivedLongGauge
18+
from opencensus.metrics.export.gauge import Registry
19+
from opencensus.metrics.export.metric_producer import MetricProducer
20+
21+
22+
# Namespaces used in Azure Monitor
23+
AVAILABLE_MEMORY = "\\Memory\\Available Bytes"
24+
25+
26+
def get_available_memory():
27+
return psutil.virtual_memory().available
28+
29+
30+
def get_available_memory_metric():
31+
""" Returns a derived gauge for available memory
32+
33+
Available memory is defined as memory that can be given instantly to
34+
processes without the system going into swap
35+
36+
:rtype: :class:`opencensus.metrics.export.gauge.DerivedLongGauge`
37+
:return: The gauge representing the available memory metric
38+
"""
39+
gauge = DerivedLongGauge(
40+
AVAILABLE_MEMORY,
41+
'Amount of available memory in bytes',
42+
'byte',
43+
[])
44+
gauge.create_default_time_series(get_available_memory)
45+
return gauge
46+
47+
48+
class AzureStandardMetricsProducer(MetricProducer):
49+
"""Implementation of the producer of standard metrics.
50+
51+
Includes Azure specific standard metrics, implemented
52+
using gauges.
53+
"""
54+
def __init__(self):
55+
self.registry = Registry()
56+
self.registry.add_gauge(get_available_memory_metric())
57+
58+
def get_metrics(self):
59+
return self.registry.get_metrics()
60+
61+
62+
producer = AzureStandardMetricsProducer()

contrib/opencensus-ext-azure/setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
long_description=open('README.rst').read(),
4141
install_requires=[
4242
'opencensus >= 0.7.dev0, < 1.0.0',
43+
'psutil >= 5.6.3',
4344
'requests >= 2.19.0',
4445
],
4546
extras_require={},

contrib/opencensus-ext-azure/tests/test_azure_metrics_exporter.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from opencensus.ext.azure.common import Options
2222
from opencensus.ext.azure.common.protocol import DataPoint
2323
from opencensus.ext.azure.common.protocol import Envelope
24+
from opencensus.ext.azure.metrics_exporter import standard_metrics
2425
from opencensus.metrics import label_key
2526
from opencensus.metrics import label_value
2627
from opencensus.metrics.export import metric
@@ -450,11 +451,32 @@ def test_create_envelope(self):
450451
self.assertTrue('properties' in envelope.data.baseData)
451452
self.assertEqual(envelope.data.baseData.properties, properties)
452453

453-
@mock.patch('opencensus.ext.azure.metrics_exporter' +
454-
'.transport.get_exporter_thread', return_value=mock.Mock())
454+
@mock.patch('opencensus.ext.azure.metrics_exporter'
455+
'.transport.get_exporter_thread')
455456
def test_new_metrics_exporter(self, exporter_mock):
456457
iKey = '12345678-1234-5678-abcd-12345678abcd'
457458
exporter = metrics_exporter.new_metrics_exporter(
458459
instrumentation_key=iKey)
459460

460461
self.assertEqual(exporter.options.instrumentation_key, iKey)
462+
self.assertEqual(len(exporter_mock.call_args_list), 1)
463+
self.assertEqual(len(exporter_mock.call_args[0][0]), 2)
464+
producer_class = standard_metrics.AzureStandardMetricsProducer
465+
self.assertFalse(isinstance(exporter_mock.call_args[0][0][0],
466+
producer_class))
467+
self.assertTrue(isinstance(exporter_mock.call_args[0][0][1],
468+
producer_class))
469+
470+
@mock.patch('opencensus.ext.azure.metrics_exporter'
471+
'.transport.get_exporter_thread')
472+
def test_new_metrics_exporter_no_standard_metrics(self, exporter_mock):
473+
iKey = '12345678-1234-5678-abcd-12345678abcd'
474+
exporter = metrics_exporter.new_metrics_exporter(
475+
instrumentation_key=iKey, enable_standard_metrics=False)
476+
477+
self.assertEqual(exporter.options.instrumentation_key, iKey)
478+
self.assertEqual(len(exporter_mock.call_args_list), 1)
479+
self.assertEqual(len(exporter_mock.call_args[0][0]), 1)
480+
producer_class = standard_metrics.AzureStandardMetricsProducer
481+
self.assertFalse(isinstance(exporter_mock.call_args[0][0][0],
482+
producer_class))
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Copyright 2019, OpenCensus Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import collections
16+
import mock
17+
import unittest
18+
19+
from opencensus.ext.azure.metrics_exporter import standard_metrics
20+
21+
22+
class TestStandardMetrics(unittest.TestCase):
23+
@mock.patch('opencensus.ext.azure.metrics_exporter'
24+
'.standard_metrics.get_available_memory_metric')
25+
def test_producer_ctor(self, avail_mock):
26+
standard_metrics.AzureStandardMetricsProducer()
27+
28+
self.assertEqual(len(avail_mock.call_args_list), 1)
29+
30+
def test_producer_get_metrics(self):
31+
producer = standard_metrics.AzureStandardMetricsProducer()
32+
metrics = producer.get_metrics()
33+
34+
self.assertEqual(len(metrics), 1)
35+
36+
def test_get_available_memory_metric(self):
37+
gauge = standard_metrics.get_available_memory_metric()
38+
39+
self.assertEqual(gauge.descriptor.name, '\\Memory\\Available Bytes')
40+
41+
@mock.patch('psutil.virtual_memory')
42+
def test_get_available_memory(self, psutil_mock):
43+
memory = collections.namedtuple('memory', 'available')
44+
vmem = memory(available=100)
45+
psutil_mock.return_value = vmem
46+
mem = standard_metrics.get_available_memory()
47+
48+
self.assertEqual(mem, 100)

contrib/opencensus-ext-ocagent/opencensus/ext/ocagent/stats_exporter/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,5 +257,5 @@ def new_stats_exporter(service_name,
257257
exporter = StatsExporter(
258258
ExportRpcHandler(_create_stub(endpoint), service_name, hostname))
259259

260-
transport.get_exporter_thread(stats.stats, exporter, interval)
260+
transport.get_exporter_thread([stats.stats], exporter, interval)
261261
return exporter

contrib/opencensus-ext-ocagent/tests/test_stats_exporter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ def _helper(request_iterator, context):
342342
self._port,
343343
interval=0.1)
344344

345-
self.assertEqual(mock_transport.call_args[0][0], stats_module.stats)
345+
self.assertEqual(mock_transport.call_args[0][0][0], stats_module.stats)
346346
self.assertEqual(mock_transport.call_args[0][1], exporter)
347347
self.assertEqual(mock_transport.call_args[0][2], 0.1)
348348

0 commit comments

Comments
 (0)