Looking for a good Flask example using opentracing/jaeger-client lib


#1

Hi Folks!

I am trying to create a small example migrating a “non-istio” app that is already using jaeger to istio.

from flask import Flask
from jaeger_client import Config
import logging
from flask_opentracing import FlaskTracer
from opentracing_instrumentation.client_hooks import install_all_patches
from opentracing_instrumentation.request_context import RequestContextManager
import requests

def init_tracer(service):
    logging.getLogger('').handlers = []
    logging.basicConfig(format='%(message)s', level=logging.DEBUG)

    config = Config(
        config={ # usually read from some yaml config
            'sampler': {'type': 'const', 'param': 1, },
            'logging': True,
            'reporter_batch_size': 1,
        },
        service_name=service,
    )
    return config.initialize_tracer()

app = Flask(__name__)
tracer = init_tracer('frontend') 
flask_tracer = FlaskTracer(tracer, True, app, ['url','url_rule','method','path','environ.HTTP_X_REAL_IP'])

@app.route("/")
def frontend():
    with RequestContextManager(span=flask_tracer.get_span()):
        requests.get('http://backend:8083/backend')
        requests.get('http://meaning:8084/meaning')
        return 'frontend'

install_all_patches()
if __name__ == "__main__":
    app.run(port=8082, debug=True)

The example above works using Jaeger, but if I do use Istio it doesn’t get my span tags and it is not grouping these calls.

I understand that the “productpage” example uses only the B3 headers to generate the grouping… If I try to update my example following the “productpage” recommendation it does group my requests, but it is not linking to istio-ingressgateway creatinga “trace-without-root-span”:

The code updated:

from flask import Flask, request
import requests
#Tracing Istio
from flask import _request_ctx_stack as stack
from jaeger_client import Tracer, ConstSampler
from opentracing.propagation import Format
from jaeger_client.reporter import NullReporter
from jaeger_client.codecs import B3Codec
from opentracing.ext import tags
from opentracing_instrumentation.request_context import get_current_span, span_in_context

try:
    import http.client as http_client
except ImportError:
    # Python 2
    import httplib as http_client
http_client.HTTPConnection.debuglevel = 1

app = Flask(__name__)

tracer = Tracer(
    one_span_per_rpc=True,
    service_name='frontend',
    reporter=NullReporter(),
    sampler=ConstSampler(decision=True),
    extra_codecs={Format.HTTP_HEADERS: B3Codec()}
)

def trace():
    '''
    Function decorator that creates opentracing span from incoming b3 headers
    '''
    def decorator(f):
        def wrapper(*args, **kwargs):
            request = stack.top.request
            try:
                # Create a new span context, reading in values (traceid,
                # spanid, etc) from the incoming x-b3-*** headers.
                span_ctx = tracer.extract(
                    Format.HTTP_HEADERS,
                    dict(request.headers)
                )
                # Note: this tag means that the span will *not* be
                # a child span. It will use the incoming traceid and
                # spanid. We do this to propagate the headers verbatim.
                rpc_tag = {tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER}
                span = tracer.start_span(
                    operation_name='op', child_of=span_ctx, tags=rpc_tag
                )
            except Exception as e:
                # We failed to create a context, possibly due to no
                # incoming x-b3-*** headers. Start a fresh span.
                # Note: This is a fallback only, and will create fresh headers,
                # not propagate headers.
                span = tracer.start_span('op')
            with span_in_context(span):
                r = f(*args, **kwargs)
                return r
        wrapper.__name__ = f.__name__
        return wrapper
    return decorator

def getForwardHeaders(request):
    headers = {}

    # x-b3-*** headers can be populated using the opentracing span
    span = get_current_span()
    carrier = {}
    tracer.inject(
        span_context=span.context,
        format=Format.HTTP_HEADERS,
        carrier=carrier)

    headers.update(carrier)

    incoming_headers = ['x-request-id']

    for ihdr in incoming_headers:
        val = request.headers.get(ihdr)
        if val is not None:
            headers[ihdr] = val
            #print "incoming: "+ihdr+":"+val

    return headers

@app.route("/")
@trace()
def frontend():
        headers = getForwardHeaders(request)
        requests.get('http://backend:8083/backend', headers=headers)
        requests.get('http://meaning:8084/meaning', headers=headers)
        return 'frontend v2 basic'

#install_all_patches()
if __name__ == "__main__":
    print('frontend v2')
    app.run(port=8082, debug=True)

Anybody could help me to convert my jaeger code to istio compatible?

Thanks in advance!

Tito


#2

Hi! I am just updating here with a code that now works for me:

import sys

from flask import Flask, abort, request
import requests

app = Flask(__name__)

def getForwardHeaders(request):
    headers = {}

    incoming_headers = [ 'x-request-id',
                         'x-b3-traceid',
                         'x-b3-spanid',
                         'x-b3-parentspanid',
                         'x-b3-sampled',
                         'x-b3-flags',
                         'x-ot-span-context'
    ]

    for ihdr in incoming_headers:
        val = request.headers.get(ihdr)
        if val is not None:
            headers[ihdr] = val
            print("incoming: "+ihdr+":"+val, file=sys.stderr)
    return headers


@app.route("/")
def f1():
    tracking_headers = getForwardHeaders(request)
    outputs = requests.get('http://backend:8083/backend', headers=tracking_headers).content
    outputs += requests.get('http://meaning:8084/meaning', headers=tracking_headers).content
    return outputs

if __name__ == "__main__":
    print('frontend v2')
    app.run(host='0.0.0.0', port=8082, debug=True)

Anyway I didn’t figure out how to include my own spans, for example using jaeger_client library on the same context as Istio. If anybody has a code that includes jaeger_client adding spans to Istio spans let me know.