OpenTracing instrumentation for the Flask microframework

Overview

Flask-OpenTracing

This package enables distributed tracing in Flask applications via The OpenTracing Project. Once a production system contends with real concurrency or splits into many services, crucial (and formerly easy) tasks become difficult: user-facing latency optimization, root-cause analysis of backend errors, communication about distinct pieces of a now-distributed system, etc. Distributed tracing follows a request on its journey from inception to completion from mobile/browser all the way to the microservices.

As core services and libraries adopt OpenTracing, the application builder is no longer burdened with the task of adding basic tracing instrumentation to their own code. In this way, developers can build their applications with the tools they prefer and benefit from built-in tracing instrumentation. OpenTracing implementations exist for major distributed tracing systems and can be bound or swapped with a one-line configuration change.

If you want to learn more about the underlying python API, visit the python source code.

If you are migrating from the 0.x series, you may want to read the list of breaking changes.

Installation

Run the following command:

$ pip install Flask-Opentracing

Usage

This Flask extension allows for tracing of Flask apps using the OpenTracing API. All that it requires is for a FlaskTracing tracer to be initialized using an instance of an OpenTracing tracer. You can either trace all requests to your site, or use function decorators to trace certain individual requests.

Note: optional_args in both cases are any number of attributes (as strings) of flask.Request that you wish to set as tags on the created span

Initialize

FlaskTracing wraps the tracer instance that's supported by opentracing. To create a FlaskTracing object, you can either pass in a tracer object directly or a callable that returns the tracer object. For example:

import opentracing
from flask_opentracing import FlaskTracing

opentracing_tracer = ## some OpenTracing tracer implementation
tracing = FlaskTracing(opentracing_tracer, ...)

or

import opentracing
from flask_opentracing import FlaskTracing

def initialize_tracer():
    ...
    return opentracing_tracer

tracing = FlaskTracing(initialize_tracer, ...)

Trace All Requests

import opentracing
from flask_opentracing import FlaskTracing

app = Flask(__name__)

opentracing_tracer = ## some OpenTracing tracer implementation
tracing = FlaskTracing(opentracing_tracer, True, app, [optional_args])

Trace Individual Requests

import opentracing
from flask_opentracing import FlaskTracing

app = Flask(__name__)

opentracing_tracer = ## some OpenTracing tracer implementation
tracing = FlaskTracing(opentracing_tracer)

@app.route('/some_url')
@tracing.trace(optional_args)
def some_view_func():
    ...
    return some_view

Accessing Spans Manually

In order to access the span for a request, we've provided an method FlaskTracing.get_span(request) that returns the span for the request, if it is exists and is not finished. This can be used to log important events to the span, set tags, or create child spans to trace non-RPC events. If no request is passed in, the current request will be used.

Tracing an RPC

If you want to make an RPC and continue an existing trace, you can inject the current span into the RPC. For example, if making an http request, the following code will continue your trace across the wire:

@tracing.trace()
def some_view_func(request):
    new_request = some_http_request
    current_span = tracing.get_span(request)
    text_carrier = {}
    opentracing_tracer.inject(span, opentracing.Format.TEXT_MAP, text_carrier)
    for k, v in text_carrier.iteritems():
        new_request.add_header(k,v)
    ... # make request

Examples

See examples to view and run an example of two Flask applications with integrated OpenTracing tracers.

This tutorial has a step-by-step guide for using Flask-Opentracing with Jaeger.

Breaking changes from 0.x

Starting with the 1.0 version, a few changes have taken place from previous versions:

  • FlaskTracer has been renamed to FlaskTracing, although FlaskTracing can be used still as a deprecated name.
  • When passing an Application object at FlaskTracing creation time, trace_all_requests defaults to True.
  • When no opentracing.Tracer is provided, FlaskTracing will rely on the global tracer.

Further Information

If you're interested in learning more about the OpenTracing standard, please visit opentracing.io or join the mailing list. If you would like to implement OpenTracing in your project and need help, feel free to send us a note at [email protected].

Comments
  • Allow the underlying tracer initialization to be deferred

    Allow the underlying tracer initialization to be deferred

    For some tracer implementation (like Jaeger), the initialization of the tracer should be deferred until the wsgi server forks. We cannot directly create a jaeger tracer instance and pass it to FlaskTracer constructor upon startup.

    This PR addresses this issue. The underlying tracer initialization can be deferred if we pass a callable as argument to the constructor.

    opened by kevinjqiu 9
  • Question: Release Status

    Question: Release Status

    What is the status of getting master released? I need the broader opentracing version pinning in order to incorporate this library into my project, and master seems to work in my project, but I would much prefer to install a released package.

    opened by zrayn 8
  • Add default to _after_request_fn

    Add default to _after_request_fn

    If the flask app has a before_request method that shortcuts request execution, the request will not be present in the _current_spans dict but the tracer will still try to finish the span. Adding this default prevents an exception on those calls.

    opened by gravelg 8
  • opentracing setup version

    opentracing setup version

    Hi,

    opentracing 1.3 can run on python 3, 1.2 and below he can't.

    In the setup, 'opentracing>=1.1,<1.2' can be upgrade or there is some issue?

    Regards,

    opened by ChristopheDaSilva 6
  • Add deploy section into .travis.yml

    Add deploy section into .travis.yml

    opened by Jamim 4
  • Recursive tracing

    Recursive tracing

    We have a recursive route we're using for testing, when using the python aws xray tracing here: https://github.com/aws/aws-xray-sdk-python

    It includes all depth in the traces. However with the python-flask opentracing module, it's only showing one level of recursion in the waterfall graph. Is there anyway to get more detail?

    This is my config:

        config = jaeger_client.Config(config={'sampler': {'type': 'const', 'param': 1}, 
                                'logging': True,
                                'local_agent':
                                {'reporting_host': JAEGER_HOST}},
                        service_name="jaeger_opentracing_example")
        jaeger_tracer = config.initialize_tracer()
        # This will trace all requests; routes can also be chosen individually instead
        # Traced attributes lists additional request attributes we want to capture
        tracing = FlaskTracing(jaeger_tracer, True, app, traced_atrributes=['remote_addr'])
    

    Here is the flask route:

    @app.route("/recurse/<number>", methods=['GET'])
    def recurse(number):
        '''Recursively call itself with sleep'''
        num = int(number)
        if num> 0:
            val = num - 1
            sleep((randint(200,600)/1000.0))
            r = requests.get("http://turquoise.default/recurse/%d" % val)
            string = "%sAt depth %d!<br>" % (r.text, num)
            return string, r.status_code
        else:
            return "Depth 0!", 200
    

    For instance calling /recurse/7 results in the following waterfalls:

    Screenshot from 2019-05-15 08-54-34 Screenshot from 2019-05-15 08-54-43

    opened by r0fls 2
  • 'Tracer' object has no attribute '_noop_scope'

    'Tracer' object has no attribute '_noop_scope'

    Following the standard example but calling Flask.run explicitly, like:

           def initialize_tracer():
                from jaeger_client import Config
                config = Config(
                    config={
                        'sampler': {'type': 'const', 'param': 1}
                    },
                    service_name='hello-world')
                return config.initialize_tracer() # also sets opentracing.tracer                                      
    
            def rest_prediction_server():
                app = seldon_microservice.get_rest_microservice(
                    user_object, debug=DEBUG)
    
    
                from flask_opentracing import FlaskTracer
    
                flask_tracer = FlaskTracer(initialize_tracer, True, app)
    
                app.run(host='0.0.0.0', port=port)
    
    

    I get on REST call:

    Traceback (most recent call last):
      File "/home/clive/anaconda3/lib/python3.6/site-packages/flask/app.py", line 2292, in wsgi_app
        response = self.full_dispatch_request()
      File "/home/clive/anaconda3/lib/python3.6/site-packages/flask/app.py", line 1815, in full_dispatch_request
        rv = self.handle_user_exception(e)
      File "/home/clive/anaconda3/lib/python3.6/site-packages/flask_cors/extension.py", line 161, in wrapped_function
        return cors_after_request(app.make_response(f(*args, **kwargs)))
      File "/home/clive/anaconda3/lib/python3.6/site-packages/flask/app.py", line 1718, in handle_user_exception
        reraise(exc_type, exc_value, tb)
      File "/home/clive/anaconda3/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise
        raise value
      File "/home/clive/anaconda3/lib/python3.6/site-packages/flask/app.py", line 1811, in full_dispatch_request
        rv = self.preprocess_request()
      File "/home/clive/anaconda3/lib/python3.6/site-packages/flask/app.py", line 2087, in preprocess_request
        rv = func()
      File "/home/clive/anaconda3/lib/python3.6/site-packages/flask_opentracing/tracing.py", line 39, in start_trace
        self._before_request_fn(traced_attributes)
      File "/home/clive/anaconda3/lib/python3.6/site-packages/flask_opentracing/tracing.py", line 121, in _before_request_fn
        child_of=span_ctx)
      File "/home/clive/anaconda3/lib/python3.6/site-packages/opentracing/tracer.py", line 126, in start_active_span
        return self._noop_scope
    AttributeError: 'Tracer' object has no attribute '_noop_scope'
    
    

    Any ideas?

    opened by cliveseldon 2
  • add working examples with Jaeger tracer.

    add working examples with Jaeger tracer.

    I've added examples that use Jaeger instead of Lightstep, introduce serialization to the wire, and are bare minimal for ease of understanding.

    Before running both files you need to set JAEGER_HOST and WEBSERVER_HOST environment variables.

    @bhs @yurishkuro - please let me know if the approach seems good for you. I'll be writing README after your thumbs up on general direction.

    opened by ror6ax 2
  • does not work when started via flask run

    does not work when started via flask run

    The following script never starts the webserver and just hangs forever:

    import opentracing
    from flask import Flask
    from flask_opentracing import FlaskTracer
    from jaeger_client import Config
    
    app = Flask(__name__)
    
    config = Config(
    config={ # usually read from some yaml config
                              'sampler': {
                              'type': 'const',
                              'param': 1,
                      },
                              'logging': True,
                      },
    service_name='flask_app_1',
    )
    
    
    opentracing_tracer = config.initialize_tracer()
    tracer = FlaskTracer(opentracing_tracer)
    
    @app.route('/some_url')
    @tracer.trace()
    def some_view_func():
        return "some_view"
    

    With debug logging turned on, I see this:

    2017-11-06 15:10:16,177 Initializing Jaeger Tracer with UDP reporter

    Same script with minimal changes started by python scriptname.py works as a charm.

    opened by ror6ax 2
  • Remove non-ascii character from README.rst

    Remove non-ascii character from README.rst

    The python packaging doesn't declare itself to prefer utf-8, but the README contains it, so the long_description=open('README.rst').read() is failing with: UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 3800: ordinal not in range(128)

    There's only this single character, though, so just replace it with ASCII and solve the issue the easy way.

    opened by asilversempirical 2
  • Running flasktracing in gunicorn ends up to no root span

    Running flasktracing in gunicorn ends up to no root span

    I have an flask app using flasktracing, all the requests are shown in my jaeger backend UI without WSGI server. But when I run with gunicorn, I saw my "no-root-span" messages in the UI. I am using threads only with only one sync worker, and I have tried lazy-init and normal init the global tracer . Any idea how to fix it?

    opened by zh0uquan 1
  • Status code tag is not added to spans when using @trace decorator

    Status code tag is not added to spans when using @trace decorator

    How to reproduce the issue

    Let's use whatever tracer (I used jaeger for this example) and use the trace decorator. Ex.:

    from flask import Flask
    from flask_opentracing import FlaskTracer
    
    app = Flask(__name__)
    
    
    def initialize_tracer():
        config = Config(
            config={
                'sampler': {'type': 'const', 'param': 1}
            },
            service_name='hello-world'
        )
        return config.initialize_tracer()
    
    
    flask_tracer = FlaskTracer(initialize_tracer)
    
    
    @app.route('/')
    @flask_tracer.trace()
    def hello():
        return 'Hello'
    

    In the resulting span, the HTTP_STATUS_CODE tag should not be present.

    Possible cause

    Looking at the code, it seems the response is not being passed to the _after_request_fn method when using the trace decorator.

    https://github.com/opentracing-contrib/python-flask/blob/master/flask_opentracing/tracing.py#L83

    Possible fix

    I attached a PR with one possible solution.

    opened by Fortiz2305 0
  • scope_manager not accessable via opentracing

    scope_manager not accessable via opentracing

    In the opentracing API the scope manager is stored in the property _scope_manager and can be accessed as scope_manager. Currently, the property _scope_manager is not set in the FlaskTracing class. This leads to issues with opentracing instrumentation that needs access to it, e.g. the boto3 part of the https://github.com/uber-common/opentracing-python-instrumentation. (When I have more time, I will try to add a minimal example where failures actually occur.)

    I could imagine two fixes to this a) adding

    @property 
    def _scope_manager(self):
        return self.tracer._scopemanager
    

    or b) explicitly doing:

    self._scope_manager = self.tracer.scope_manager
    

    in the initializer. Happy to write the PR for this, if you provide me your preferences :slightly_smiling_face:

    opened by jendrikjoe 1
  • Extended traced_attributes scope to after_request (response) too

    Extended traced_attributes scope to after_request (response) too

    Traced_attributes is given and scanned in _after_request_fn too. The motivation is that this way the attributes of the response object could be presented in the trace also. Our goal is to have more control over the traced attributes because our task requires also the input and the output data in one point.

    opened by lukacsg 4
  • On 404 operation_id is None which causes issues with some tracers

    On 404 operation_id is None which causes issues with some tracers

    The wavefront tracer specifically requires span names be non-None:

    ValueError
    ValueError: Span name cannot be blank
    
    ValueError
    ValueError: Span name cannot be blank
    
    Traceback (most recent call last)
    File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2463, in __call__
    return self.wsgi_app(environ, start_response)
    File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2449, in wsgi_app
    response = self.handle_exception(e)
    File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1866, in handle_exception
    reraise(exc_type, exc_value, tb)
    File "/usr/local/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
    raise value
    File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2446, in wsgi_app
    response = self.full_dispatch_request()
    File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1952, in full_dispatch_request
    return self.finalize_request(rv)
    File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1969, in finalize_request
    response = self.process_response(response)
    File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2266, in process_response
    response = handler(response)
    File "/usr/local/lib/python3.7/site-packages/flask_opentracing/tracing.py", line 43, in end_trace
    self._after_request_fn(response)
    File "/usr/local/lib/python3.7/site-packages/flask_opentracing/tracing.py", line 160, in _after_request_fn
    scope.close()
    File "/usr/local/lib/python3.7/site-packages/opentracing/scope_managers/__init__.py", line 77, in close
    self.span.finish()
    File "/usr/local/lib/python3.7/site-packages/wavefront_opentracing_sdk/span.py", line 165, in finish
    self._do_finish(time.time() - self.start_time)
    File "/usr/local/lib/python3.7/site-packages/wavefront_opentracing_sdk/span.py", line 191, in _do_finish
    self.tracer.report_span(self)
    File "/usr/local/lib/python3.7/site-packages/wavefront_opentracing_sdk/tracer.py", line 274, in report_span
    self._reporter.report(span)
    File "/usr/local/lib/python3.7/site-packages/wavefront_opentracing_sdk/reporting/composite.py", line 32, in report
    rep.report(wavefront_span)
    File "/usr/local/lib/python3.7/site-packages/wavefront_opentracing_sdk/reporting/console.py", line 35, in report
    default_source='unknown')
    File "/usr/local/lib/python3.7/site-packages/wavefront_sdk/common/utils.py", line 236, in tracing_span_to_line_data
    raise ValueError('Span name cannot be blank')
    ValueError: Span name cannot be blank
    

    Is there a mechanism to provide a default name for that case?

    opened by Alphasite 0
Releases(v1.0.0)
Owner
3rd-Party OpenTracing API Contributions
3rd-party contributions that use OpenTracing. **The repositories in this org are *not* affiliated with the CNCF.**
3rd-Party OpenTracing API Contributions
A flask template with Bootstrap 4, asset bundling+minification with webpack, starter templates, and registration/authentication. For use with cookiecutter.

cookiecutter-flask A Flask template for cookiecutter. (Supports Python ≥ 3.6) See this repo for an example project generated from the most recent vers

4.3k Dec 29, 2022
Flask webassets integration.

Integrates the webassets library with Flask, adding support for merging, minifying and compiling CSS and Javascript files. Documentation: https://flas

Michael Elsdörfer 433 Dec 29, 2022
Small and simple gravatar usage in Flask.

Flask Gravatar About This is small and simple integration gravatar into flask. Installation Flask-Gravatar is on PyPI so all you need is: pip install

Alexander Zelenyak 78 Sep 15, 2022
Boilerplate template formwork for a Python Flask application with Mysql,Build dynamic websites rapidly.

Overview English | 简体中文 How to Build dynamic web rapidly? We choose Formwork-Flask. Formwork is a highly packaged Flask Demo. It's intergrates various

aswallz 81 May 16, 2022
SeCl - A really easy to deploy and use made-on Flask API to manage your files remotely from Terminal

SeCl SeCl it's a really easy to deploy and use made-on Flask API to manage your

ZSendokame 3 Jan 15, 2022
Paid roles for discord using Stripe, Python, Flask & Docker

Welcome to Paycord Paid roles for discord using Stripe, Python, Flask & Docker. Setup Production On stripe dashboard, go Developers ➡️ Webhooks ➡️ Add

Ward 12 Dec 28, 2022
Intranet de la Rez Flask web app

IntraRez Application Flask de l'Intranet de la Rez. Exigences Python : Probablement = 3.10 à terme, pour l'instant = 3.8 suffit ; Autres packages Li

3 Jul 03, 2022
Python3🐍 webApp to display your current playing music on OBS Studio.

Spotify Overlay A Overlay to display on Obs Studio or any related video/stream recorder, the current music that is playing on your Spotify. Installati

carlitos 0 Oct 17, 2022
A simple FastAPI web service + Vue.js based UI over a rclip-style clip embedding database.

Explore CLIP Embeddings in a rclip database A simple FastAPI web service + Vue.js based UI over a rclip-style clip embedding database. A live demo of

18 Oct 15, 2022
A swagger 2.0 spec extractor for flask

flask-swagger A Swagger 2.0 spec extractor for Flask You can now specify base path for yml files: app = Flask(__name__) @app.route("/spec") def spec(

Sling 457 Dec 02, 2022
A caching extension for Flask

Flask-Caching Adds easy cache support to Flask. This is a fork of the Flask-Cache extension. Flask-Caching also includes the cache module from werkzeu

Peter Justin 774 Jan 02, 2023
Search users in Github. Created with Flask, PipEnv, Heroku and free time.

Search in Github Here search for users in Github and other stuff! This app is working with, Data Github API BackEnd Flask Language Python Package mana

AmirHossein Mohammadi 12 Jan 16, 2022
A Python, Flask login system

Python Login System This is a basic login + authenticason system for flask using Flask_Login and Flask_SQLAlchemy Get started on your own To use this

MrShoe 0 Feb 02, 2022
Freezes a Flask application into a set of static files.

Frozen-Flask Freezes a Flask application into a set of static files. The result can be hosted without any server-side software other than a traditiona

Frozen Flask 737 Dec 19, 2022
a flask zipkin extension based on py_zipkin.

flask-zipkin a flask zipkin extension based on py_zipkin. Installation pip install flask_zipkin usage you can simply use it as other flask extensions.

39 Jul 03, 2022
REST API with mongoDB and Flask.

Flask REST API with mongoDB py 3.10 First, to install all dependencies: python -m pip install -r requirements.txt Second, into the ./src/ folder, cop

Luis Quiñones Requelme 3 Mar 05, 2022
Glauth management ui created with python/flask

glauth-ui Glauth-UI is a small flask web app i created to manage the minimal glauth ldap server. I created this as i wanted to use glauth for authenti

Nils Thiele 67 Nov 29, 2022
Seamlessly serve your static assets of your Flask app from Amazon S3

flask-s3 Seamlessly serve the static assets of your Flask app from Amazon S3. Maintainers Flask-S3 is maintained by @e-dard, @eriktaubeneck and @SunDw

Edd Robinson 188 Aug 24, 2022
A boilerplate Flask API for a Fullstack Project :rocket:

Flask Boilerplate to quickly get started with production grade flask application with some additional packages and configuration prebuilt.

Yasser Tahiri 32 Dec 24, 2022
Criando um Bot com PYAUTOGUI e utilizando o Flask para Interface para Usuário

Criando um Bot com PYAUTOGUI e utilizando o Flask para Interface para Usuário O pyautogui foi escolhido pela possibilidade de fazer a identificação do

Rodrigo Vital 2 Oct 20, 2021