Skip to content

Commit b6044b2

Browse files
authored
chore(monitoring): add opencensus samples (GoogleCloudPlatform#5344)
1 parent 6b0e633 commit b6044b2

File tree

6 files changed

+218
-0
lines changed

6 files changed

+218
-0
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
/memorystore/**/*.py @GoogleCloudPlatform/python-samples-owners
4949
/ml_engine/**/*.py @alecglassford @GoogleCloudPlatform/python-samples-owners
5050
/monitoring/**/*.py @GoogleCloudPlatform/python-samples-owners
51+
/monitoring/opencensus @yuriatgoogle @GoogleCloudPlatform/python-samples-owners
5152
/notebooks/**/*.py @alixhami @sirtorry @GoogleCloudPlatform/python-samples-owners
5253
/opencensus/**/*.py @GoogleCloudPlatform/python-samples-owners
5354
/profiler/**/*.py @kalyanac @GoogleCloudPlatform/python-samples-owners

monitoring/opencensus/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
This section contains a sample of using [OpenCensus](https://opencensus.io) and the [Prometheus](https://prometheus.io) exporter to instrument a Flask application to emit Service Level Indicator metrics.
2+
3+
## Running the samples locally
4+
5+
1. Many samples require extra libraries to be installed. If there is a `requirements.txt`, you will need to install the dependencies with [`pip`](pip.readthedocs.org).
6+
7+
pip install -t lib -r requirements.txt
8+
9+
2. Use `main.py` to run the sample:
10+
11+
python main.py
12+
13+
3. Visit `http://localhost:8080` to view your application.
14+
15+
4. Visit `http://localhost:8080/metrics` to view your metrics.
16+
17+
## Additional resources
18+
19+
For more information on OpenCensus:
20+
21+
> https://opencensus.io
22+
23+
For more information on Prometheus:
24+
25+
> https://prometheus.io

monitoring/opencensus/main.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
"""
2+
# Copyright 2021 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
"""
16+
import random
17+
import time
18+
19+
# [START monitoring_sli_metrics_opencensus_setup]
20+
from flask import Flask
21+
from opencensus.ext.prometheus import stats_exporter as prometheus
22+
from opencensus.stats import aggregation as aggregation_module
23+
from opencensus.stats import measure as measure_module
24+
from opencensus.stats import stats as stats_module
25+
from opencensus.stats import view as view_module
26+
from opencensus.tags import tag_map as tag_map_module
27+
28+
from prometheus_flask_exporter import PrometheusMetrics
29+
30+
# [END monitoring_sli_metrics_opencensus_setup]
31+
32+
# set up measures
33+
# [START monitoring_sli_metrics_opencensus_measure]
34+
m_request_count = measure_module.MeasureInt(
35+
"python_request_count", "total requests", "requests"
36+
)
37+
m_failed_request_count = measure_module.MeasureInt(
38+
"python_failed_request_count", "failed requests", "requests"
39+
)
40+
m_response_latency = measure_module.MeasureFloat(
41+
"python_response_latency", "response latency", "s"
42+
)
43+
# [END monitoring_sli_metrics_opencensus_measure]
44+
45+
# set up stats recorder
46+
stats_recorder = stats_module.stats.stats_recorder
47+
# [START monitoring_sli_metrics_opencensus_view]
48+
# set up views
49+
latency_view = view_module.View(
50+
"python_response_latency",
51+
"The distribution of the latencies",
52+
[],
53+
m_response_latency,
54+
aggregation_module.DistributionAggregation(
55+
[0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]
56+
),
57+
)
58+
59+
request_count_view = view_module.View(
60+
"python_request_count",
61+
"total requests",
62+
[],
63+
m_request_count,
64+
aggregation_module.CountAggregation(),
65+
)
66+
67+
failed_request_count_view = view_module.View(
68+
"python_failed_request_count",
69+
"failed requests",
70+
[],
71+
m_failed_request_count,
72+
aggregation_module.CountAggregation(),
73+
)
74+
75+
76+
# register views
77+
def register_all_views(view_manager: stats_module.stats.view_manager) -> None:
78+
view_manager.register_view(latency_view)
79+
view_manager.register_view(request_count_view)
80+
view_manager.register_view(failed_request_count_view)
81+
# [END monitoring_sli_metrics_opencensus_view]
82+
83+
84+
# set up exporter
85+
# [START monitoring_sli_metrics_opencensus_exporter]
86+
def setup_openCensus_and_prometheus_exporter() -> None:
87+
stats = stats_module.stats
88+
view_manager = stats.view_manager
89+
exporter = prometheus.new_stats_exporter(prometheus.Options(namespace="oc_python"))
90+
view_manager.register_exporter(exporter)
91+
register_all_views(view_manager)
92+
# [END monitoring_sli_metrics_opencensus_exporter]
93+
94+
95+
app = Flask(__name__)
96+
metrics = PrometheusMetrics(app)
97+
98+
99+
@app.route("/")
100+
def homePage() -> (str, int):
101+
# start timer
102+
# [START monitoring_sli_metrics_opencensus_latency]
103+
start_time = time.perf_counter()
104+
# [START monitoring_sli_metrics_opencensus_counts]
105+
mmap = stats_recorder.new_measurement_map()
106+
# [END monitoring_sli_metrics_opencensus_latency]
107+
# count request
108+
mmap.measure_int_put(m_request_count, 1)
109+
# fail 10% of the time
110+
# [START monitoring_sli_metrics_opencensus_latency]
111+
if random.randint(0, 100) > 90:
112+
# [END monitoring_sli_metrics_opencensus_latency]
113+
mmap.measure_int_put(m_failed_request_count, 1)
114+
# [END monitoring_sli_metrics_opencensus_counts]
115+
# [START monitoring_sli_metrics_opencensus_latency]
116+
response_latency = time.perf_counter() - start_time
117+
mmap.measure_float_put(m_response_latency, response_latency)
118+
# [START monitoring_sli_metrics_opencensus_counts]
119+
tmap = tag_map_module.TagMap()
120+
mmap.record(tmap)
121+
# [END monitoring_sli_metrics_opencensus_latency]
122+
return ("error!", 500)
123+
# [END monitoring_sli_metrics_opencensus_counts]
124+
else:
125+
random_delay = random.randint(0, 5000) / 1000
126+
# delay for a bit to vary latency measurement
127+
time.sleep(random_delay)
128+
# record latency
129+
response_latency = time.perf_counter() - start_time
130+
mmap.measure_float_put(m_response_latency, response_latency)
131+
tmap = tag_map_module.TagMap()
132+
mmap.record(tmap)
133+
return ("home page", 200)
134+
135+
136+
if __name__ == "__main__":
137+
setup_openCensus_and_prometheus_exporter()
138+
app.run(port=8080)

monitoring/opencensus/main_test.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""
2+
# Copyright 2021 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
"""
16+
import main
17+
18+
19+
def test_index() -> None:
20+
"""
21+
GIVEN the app
22+
WHEN multiple requests to the / endpoint are made
23+
THEN check that most of them succeed
24+
"""
25+
client = main.app.test_client()
26+
success_counter = 0
27+
for x in range(10):
28+
r = client.get("/")
29+
if r.status_code == 200:
30+
success_counter = success_counter + 1
31+
assert success_counter >= 5
32+
33+
34+
def test_metrics() -> None:
35+
"""
36+
GIVEN the app
37+
WHEN a request to the /metrics endpoint is made
38+
THEN check that it's a valid Prometheus endpoint
39+
"""
40+
client = main.app.test_client()
41+
res = client.get("/metrics")
42+
assert res.status_code == 200
43+
assert "HELP" in res.data.decode("utf-8")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pytest==6.2.1
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Flask==1.1.2
2+
google-api-core==1.25.1
3+
google-auth==1.25.0
4+
googleapis-common-protos==1.52.0
5+
opencensus==0.7.12
6+
opencensus-context==0.1.2
7+
opencensus-ext-prometheus==0.2.1
8+
prometheus-client==0.9.0
9+
prometheus-flask-exporter==0.18.1
10+
requests==2.25.1

0 commit comments

Comments
 (0)