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

Commit 77cd734

Browse files
jpoehneltliyanhui1228
authored andcommitted
flask init_app and config pattern (#160)
1 parent 13321df commit 77cd734

3 files changed

Lines changed: 180 additions & 17 deletions

File tree

docs/trace/usage.rst

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,41 @@ requests will be automatically traced.
209209
# `GoogleCloudFormatPropagator` as propagator.
210210
middleware = FlaskMiddleware(app)
211211
212+
The middleware also supports the factory pattern.
213+
214+
.. code:: python
215+
216+
from opencensus.trace.ext.flask.flask_middleware import FlaskMiddleware
217+
middleware = FlaskMiddleware()
218+
app = flask.Flask(__name__)
219+
middleware.init_app(app)
220+
221+
The Flask application configuration can also be used to specify the sampler,
222+
exporter, propagator in the middleware.
223+
224+
.. code:: python
225+
226+
OPENCENSUS_TRACE = {
227+
'SAMPLER': opencensus.trace.samplers.probability.ProbabilitySampler,
228+
'REPORTER': opencensus.trace.exporters.print_exporter.PrintExporter,
229+
'PROPAGATOR': opencensus.trace.propagation.google_cloud_format.
230+
GoogleCloudFormatPropagator,
231+
}
232+
233+
You can configure the sampling rate and other parameters using the ``OPENCENSUS_TRACE_PARAMS``
234+
key in the app configuration:
235+
236+
.. code:: python
237+
238+
OPENCENSUS_TRACE_PARAMS = {
239+
'BLACKLIST_PATHS': ['/_ah/health'],
240+
'GCP_EXPORTER_PROJECT': None,
241+
'SAMPLING_RATE': 0.5,
242+
'ZIPKIN_EXPORTER_SERVICE_NAME': 'my_service',
243+
'ZIPKIN_EXPORTER_HOST_NAME': 'localhost',
244+
'ZIPKIN_EXPORTER_PORT': 9411,
245+
}
246+
212247
Django
213248
~~~~~~
214249

opencensus/trace/ext/flask/flask_middleware.py

Lines changed: 83 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import inspect
1516
import logging
1617
import sys
1718

@@ -24,20 +25,34 @@
2425
from opencensus.trace import status
2526
from opencensus.trace import tracer as tracer_module
2627
from opencensus.trace.exporters import print_exporter
28+
from opencensus.trace.exporters.transports import sync
2729
from opencensus.trace.ext import utils
2830
from opencensus.trace.propagation import google_cloud_format
29-
from opencensus.trace.samplers import always_on
31+
from opencensus.trace.samplers import always_on, probability
32+
3033

3134
_FLASK_TRACE_HEADER = 'X_CLOUD_TRACE_CONTEXT'
3235

3336
HTTP_METHOD = attributes_helper.COMMON_ATTRIBUTES['HTTP_METHOD']
3437
HTTP_URL = attributes_helper.COMMON_ATTRIBUTES['HTTP_URL']
3538
HTTP_STATUS_CODE = attributes_helper.COMMON_ATTRIBUTES['HTTP_STATUS_CODE']
3639

40+
BLACKLIST_PATHS = 'BLACKLIST_PATHS'
41+
GCP_EXPORTER_PROJECT = 'GCP_EXPORTER_PROJECT'
42+
SAMPLING_RATE = 'SAMPLING_RATE'
43+
TRANSPORT = 'TRANSPORT'
44+
ZIPKIN_EXPORTER_SERVICE_NAME = 'ZIPKIN_EXPORTER_SERVICE_NAME'
45+
ZIPKIN_EXPORTER_HOST_NAME = 'ZIPKIN_EXPORTER_HOST_NAME'
46+
ZIPKIN_EXPORTER_PORT = 'ZIPKIN_EXPORTER_PORT'
47+
3748
log = logging.getLogger(__name__)
3849

3950

4051
class FlaskMiddleware(object):
52+
DEFAULT_SAMPLER = always_on.AlwaysOnSampler
53+
DEFAULT_EXPORTER = print_exporter.PrintExporter
54+
DEFAULT_PROPAGATOR = google_cloud_format.GoogleCloudFormatPropagator
55+
4156
"""Flask middleware to automatically trace requests.
4257
4358
:type app: :class: `~flask.Flask`
@@ -65,22 +80,78 @@ class FlaskMiddleware(object):
6580
:class:`.TextFormatPropagator` and
6681
:class:`.TraceContextPropagator`.
6782
"""
68-
def __init__(self, app, blacklist_paths=None, sampler=None, exporter=None,
69-
propagator=None):
70-
if sampler is None:
71-
sampler = always_on.AlwaysOnSampler()
72-
73-
if exporter is None:
74-
exporter = print_exporter.PrintExporter()
75-
76-
if propagator is None:
77-
propagator = google_cloud_format.GoogleCloudFormatPropagator()
78-
83+
def __init__(self, app=None, blacklist_paths=None, sampler=None,
84+
exporter=None, propagator=None):
7985
self.app = app
8086
self.blacklist_paths = blacklist_paths
8187
self.sampler = sampler
8288
self.exporter = exporter
8389
self.propagator = propagator
90+
91+
if self.app is not None:
92+
self.init_app(app)
93+
94+
def init_app(self, app):
95+
self.app = app
96+
97+
# get settings from app config
98+
settings = self.app.config.get('OPENCENSUS_TRACE', {})
99+
100+
self.sampler = (self.sampler
101+
or settings.get('SAMPLER',
102+
self.DEFAULT_SAMPLER))
103+
self.exporter = (self.exporter
104+
or settings.get('EXPORTER',
105+
self.DEFAULT_EXPORTER))
106+
self.propagator = (self.propagator
107+
or settings.get('PROPAGATOR',
108+
self.DEFAULT_PROPAGATOR))
109+
110+
# get params from app config
111+
params = self.app.config.get('OPENCENSUS_TRACE_PARAMS', {})
112+
113+
self.blacklist_paths = params.get(BLACKLIST_PATHS,
114+
self.blacklist_paths)
115+
116+
# Initialize the sampler
117+
if not inspect.isclass(self.sampler):
118+
pass # handling of instantiated sampler
119+
elif self.sampler.__name__ == 'ProbabilitySampler':
120+
_rate = params.get(SAMPLING_RATE,
121+
probability.DEFAULT_SAMPLING_RATE)
122+
self.sampler = self.sampler(_rate)
123+
else:
124+
self.sampler = self.sampler()
125+
126+
transport = params.get(TRANSPORT, sync.SyncTransport)
127+
128+
# Initialize the exporter
129+
if not inspect.isclass(self.exporter):
130+
pass # handling of instantiated exporter
131+
elif self.exporter.__name__ == 'StackdriverExporter':
132+
_project_id = params.get(GCP_EXPORTER_PROJECT, None)
133+
self.exporter = self.exporter(
134+
project_id=_project_id,
135+
transport=transport)
136+
elif self.exporter.__name__ == 'ZipkinExporter':
137+
_zipkin_service_name = params.get(
138+
ZIPKIN_EXPORTER_SERVICE_NAME, 'my_service')
139+
_zipkin_host_name = params.get(
140+
ZIPKIN_EXPORTER_HOST_NAME, 'localhost')
141+
_zipkin_port = params.get(
142+
ZIPKIN_EXPORTER_PORT, 9411)
143+
self.exporter = self.exporter(
144+
service_name=_zipkin_service_name,
145+
host_name=_zipkin_host_name,
146+
port=_zipkin_port,
147+
transport=transport)
148+
else:
149+
self.exporter = self.exporter(transport=transport)
150+
151+
# Initialize the propagator
152+
if inspect.isclass(self.propagator):
153+
self.propagator = self.propagator()
154+
84155
self.setup_trace()
85156

86157
def setup_trace(self):

tests/unit/trace/ext/flask/test_flask_middleware.py

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@
2525
from opencensus.trace import span_data
2626
from opencensus.trace import stack_trace
2727
from opencensus.trace import status
28-
from opencensus.trace.exporters import print_exporter
28+
from opencensus.trace.exporters import print_exporter, stackdriver_exporter, \
29+
zipkin_exporter
2930
from opencensus.trace.ext.flask import flask_middleware
3031
from opencensus.trace.propagation import google_cloud_format
31-
from opencensus.trace.samplers import always_off
32-
from opencensus.trace.samplers import always_on
32+
from opencensus.trace.samplers import always_off, always_on, ProbabilitySampler
3333
from opencensus.trace.tracers import base
3434
from opencensus.trace.tracers import noop_tracer
3535

@@ -60,7 +60,7 @@ def tearDown(self):
6060
execution_context.clear()
6161

6262
def test_constructor_default(self):
63-
app = mock.Mock()
63+
app = mock.Mock(config={})
6464
middleware = flask_middleware.FlaskMiddleware(app=app)
6565

6666
self.assertIs(app, middleware.app)
@@ -73,7 +73,7 @@ def test_constructor_default(self):
7373
google_cloud_format.GoogleCloudFormatPropagator)
7474

7575
def test_constructor_explicit(self):
76-
app = mock.Mock()
76+
app = mock.Mock(config={})
7777
sampler = mock.Mock()
7878
exporter = mock.Mock()
7979
propagator = mock.Mock()
@@ -91,6 +91,63 @@ def test_constructor_explicit(self):
9191
self.assertTrue(app.before_request.called)
9292
self.assertTrue(app.after_request.called)
9393

94+
def test_init_app(self):
95+
app = mock.Mock()
96+
97+
middleware = flask_middleware.FlaskMiddleware()
98+
middleware.init_app(app)
99+
100+
self.assertIs(middleware.app, app)
101+
self.assertTrue(app.before_request.called)
102+
self.assertTrue(app.after_request.called)
103+
104+
def test_init_app_config_stackdriver_exporter(self):
105+
app = mock.Mock()
106+
app.config = {
107+
'OPENCENSUS_TRACE': {
108+
'SAMPLER': ProbabilitySampler,
109+
'EXPORTER': stackdriver_exporter.StackdriverExporter,
110+
'PROPAGATOR': google_cloud_format.GoogleCloudFormatPropagator,
111+
},
112+
'OPENCENSUS_TRACE_PARAMS': {
113+
'BLACKLIST_PATHS': ['/_ah/health'],
114+
'GCP_EXPORTER_PROJECT': None,
115+
'SAMPLING_RATE': 0.5,
116+
'ZIPKIN_EXPORTER_SERVICE_NAME': 'my_service',
117+
'ZIPKIN_EXPORTER_HOST_NAME': 'localhost',
118+
'ZIPKIN_EXPORTER_PORT': 9411,
119+
},
120+
}
121+
122+
middleware = flask_middleware.FlaskMiddleware()
123+
middleware.init_app(app)
124+
125+
self.assertIs(middleware.app, app)
126+
self.assertTrue(app.before_request.called)
127+
self.assertTrue(app.after_request.called)
128+
129+
def test_init_app_config_zipkin_exporter(self):
130+
app = mock.Mock()
131+
app.config = {
132+
'OPENCENSUS_TRACE': {
133+
'SAMPLER': ProbabilitySampler,
134+
'EXPORTER': zipkin_exporter.ZipkinExporter,
135+
'PROPAGATOR': google_cloud_format.GoogleCloudFormatPropagator,
136+
},
137+
'OPENCENSUS_TRACE_PARAMS': {
138+
'ZIPKIN_EXPORTER_SERVICE_NAME': 'my_service',
139+
'ZIPKIN_EXPORTER_HOST_NAME': 'localhost',
140+
'ZIPKIN_EXPORTER_PORT': 9411,
141+
},
142+
}
143+
144+
middleware = flask_middleware.FlaskMiddleware()
145+
middleware.init_app(app)
146+
147+
self.assertIs(middleware.app, app)
148+
self.assertTrue(app.before_request.called)
149+
self.assertTrue(app.after_request.called)
150+
94151
def test__before_request(self):
95152
from opencensus.trace import execution_context
96153

0 commit comments

Comments
 (0)