diff --git a/opencensus/README.md b/opencensus/README.md new file mode 100644 index 00000000000..4ffe0a5f510 --- /dev/null +++ b/opencensus/README.md @@ -0,0 +1,35 @@ +OpenCensus logo + +# OpenCensus Stackdriver Metrics Sample + +[OpenCensus](https://opencensus.io) is a toolkit for collecting application +performance and behavior data. OpenCensus includes utilities for distributed +tracing, metrics collection, and context propagation within and between +services. + +This example demonstrates using the OpenCensus client to send metrics data to +the [Stackdriver Monitoring](https://cloud.google.com/monitoring/docs/) +backend. + +## Prerequisites + +Install the OpenCensus core and Stackdriver exporter libraries: + +```sh +pip install -r opencensus/requirements.txt +``` + +Make sure that your environment is configured to [authenticate with +GCP](https://cloud.google.com/docs/authentication/getting-started). + +## Running the example + +```sh +python opencensus/metrics_quickstart.py +``` + +The example generates a histogram of simulated latencies, which is exported to +Stackdriver after 60 seconds. After it's exported, the histogram will be +visible on the [Stackdriver Metrics +Explorer](https://app.google.stackdriver.com/metrics-explorer) page as +`OpenCensus/task_latency_view`. diff --git a/opencensus/metrics_quickstart.py b/opencensus/metrics_quickstart.py new file mode 100755 index 00000000000..eefa20c5fd5 --- /dev/null +++ b/opencensus/metrics_quickstart.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python + +# Copyright 2019 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START monitoring_opencensus_metrics_quickstart] + +from random import random +import time + +from opencensus.ext.stackdriver import stats_exporter +from opencensus.stats import aggregation +from opencensus.stats import measure +from opencensus.stats import stats +from opencensus.stats import view + + +# A measure that represents task latency in ms. +LATENCY_MS = measure.MeasureFloat( + "task_latency", + "The task latency in milliseconds", + "ms") + +# A view of the task latency measure that aggregates measurements according to +# a histogram with predefined bucket boundaries. This aggregate is periodically +# exported to Stackdriver Monitoring. +LATENCY_VIEW = view.View( + "task_latency_distribution", + "The distribution of the task latencies", + [], + LATENCY_MS, + # Latency in buckets: [>=0ms, >=100ms, >=200ms, >=400ms, >=1s, >=2s, >=4s] + aggregation.DistributionAggregation( + [100.0, 200.0, 400.0, 1000.0, 2000.0, 4000.0])) + + +def main(): + # Register the view. Measurements are only aggregated and exported if + # they're associated with a registered view. + stats.stats.view_manager.register_view(LATENCY_VIEW) + + # Create the Stackdriver stats exporter and start exporting metrics in the + # background, once every 60 seconds by default. + exporter = stats_exporter.new_stats_exporter() + print('Exporting stats to project "{}"' + .format(exporter.options.project_id)) + + # Record 100 fake latency values between 0 and 5 seconds. + for num in range(100): + ms = random() * 5 * 1000 + print("Latency {}: {}".format(num, ms)) + + mmap = stats.stats.stats_recorder.new_measurement_map() + mmap.measure_float_put(LATENCY_MS, ms) + mmap.record() + + # Keep the thread alive long enough for the exporter to export at least + # once. + time.sleep(65) + + +if __name__ == '__main__': + main() + +# [END monitoring_opencensus_metrics_quickstart] diff --git a/opencensus/metrics_quickstart_test.py b/opencensus/metrics_quickstart_test.py new file mode 100644 index 00000000000..bd9f745edc3 --- /dev/null +++ b/opencensus/metrics_quickstart_test.py @@ -0,0 +1,80 @@ +# Copyright 2019 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from unittest import mock +import unittest + +from opencensus.ext.stackdriver import stats_exporter +from opencensus.stats import aggregation +from opencensus.stats import measure +from opencensus.stats import stats +from opencensus.stats import view + + +class TestMetricsQuickstart(unittest.TestCase): + """Sanity checks for the OpenCensus metrics quickstart examples. + + These tests check that a few basic features of the library work as expected + in order for the demo to run. See the opencensus and + opencensus-ext-stackdriver source for actual library tests. + """ + def test_measure_creation(self): + measure.MeasureFloat( + "task_latency", + "The task latency in milliseconds", + "ms") + + def test_view_creation(self): + test_view = view.View( + "task_latency_distribution", + "The distribution of the task latencies", + [], + mock.Mock(), + aggregation.DistributionAggregation([1.0, 2.0, 3.0])) + # Check that metric descriptor conversion doesn't crash + test_view.get_metric_descriptor() + + # Don't modify global stats + @mock.patch('opencensus.ext.stackdriver.stats_exporter.stats.stats', + stats._Stats()) + def test_measurement_map_record(self): + mock_measure = mock.Mock() + mock_measure_name = mock.Mock() + mock_measure.name = mock_measure_name + mock_view = mock.Mock() + mock_view.columns = [] + mock_view.measure = mock_measure + + stats.stats.view_manager.register_view(mock_view) + + mmap = stats.stats.stats_recorder.new_measurement_map() + mmap.measure_float_put(mock_measure, 1.0) + mmap.record() + + # Reaching into the stats internals here to check that recording the + # measurement map affects view data. + m2vd = (stats.stats.view_manager.measure_to_view_map + ._measure_to_view_data_list_map) + self.assertIn(mock_measure_name, m2vd) + [view_data] = m2vd[mock_measure_name] + agg_data = view_data.tag_value_aggregation_data_map[tuple()] + agg_data.add_sample.assert_called_once() + + @mock.patch('opencensus.ext.stackdriver.stats_exporter' + '.monitoring_v3.MetricServiceClient') + def test_new_stats_exporter(self, mock_client): + transport = stats_exporter.new_stats_exporter() + self.assertIsNotNone(transport) + self.assertIsNotNone(transport.options) + self.assertIsNotNone(transport.options.project_id) diff --git a/opencensus/requirements.txt b/opencensus/requirements.txt new file mode 100644 index 00000000000..0b9d34aa12f --- /dev/null +++ b/opencensus/requirements.txt @@ -0,0 +1,3 @@ +grpcio +opencensus-ext-stackdriver==0.2.1 +opencensus==0.4.1