Skip to content
This repository has been archived by the owner on Oct 3, 2023. It is now read-only.

Cross Language Tracer :: Php => Py and Py => Php #205

Closed
dragoscirjan opened this issue Oct 2, 2018 · 2 comments
Closed

Cross Language Tracer :: Php => Py and Py => Php #205

dragoscirjan opened this issue Oct 2, 2018 · 2 comments

Comments

@dragoscirjan
Copy link

Hi guys,

I'm trying to write a cross-language (PHP and py for now) bridge to be able to start from, i.e., PHP and end in python or vice versa. We have an application which uses both languages. I chose to create a new propagator in PHP because the python formater for the propagator is easier to rewrite in PHP.

So far, I've got here:

<?php

namespace App\Opencensus\Trace\Propagator;

use App\Opencensus\Trace\Propagator\PythonFormater;

use OpenCensus\Trace\Propagator\PropagatorInterface;
use OpenCensus\Trace\SpanContext;

class HttpHeaderPropagator implements PropagatorInterface {

    const DEFAULT_HEADER = 'HTTP_TRACEPARENT';

    /**
     * @var FormatterInterface
     */
    private $formatter;

    /**
     * @var string
     */
    private $header;

    /**
     * @see BaseHttpHeaderPropagator::start()
     */
    public function __construct(FormatterInterface $formatter = null, $header = null)
    {
        $this->formatter = $formatter ?: new PythonFormater();
        $this->header = $header ?: self::DEFAULT_HEADER;
    }

    /**
     * Generate a SpanContext object from the all the HTTP headers
     *
     * @param array $headers
     * @return SpanContext
     */
    public function extract($headers)
    {
        if (array_key_exists($this->header, $headers)) {
            return $this->formatter->deserialize($headers[$this->header]);
        }
        return new SpanContext();
    }

    /**
     * Persists the current SpanContext back into the results of this request
     *
     * @param SpanContext $context
     * @param array $container
     * @return array
     */
    public function inject(SpanContext $context, $container)
    {
        $header = $this->key();
        $value = $this->formatter->serialize($context);
        if (!headers_sent()) {
            header("$header: $value");
        }
        return [
            $header => $value
        ];
    }

    /**
     * Returns the current formatter
     *
     * @return FormatterInterface
     */
    public function formatter()
    {
        return $this->formatter;
    }

    /**
     * Return the key used to propagate the SpanContext
     *
     * @return string
     */
    public function key()
    {
        return str_replace('_', '-', preg_replace('/^HTTP_/', '', $this->header));
    }

}
<?php

namespace App\Opencensus\Trace\Propagator;

use OpenCensus\Trace\Propagator\FormatterInterface;
use OpenCensus\Trace\SpanContext;

class PythonFormater implements FormatterInterface
{
    const CONTEXT_HEADER_FORMAT = '/00-(\w+)-(\w+)-0(\d)/';

    /**
     * Generate a SpanContext object from the Trace Context header
     *
     * @param string $header
     * @return SpanContext
     */
    public function deserialize($header)
    {
        if (preg_match(self::CONTEXT_HEADER_FORMAT, $header, $matches)) {
            return new SpanContext(
                strtolower($matches[1]),
                array_key_exists(2, $matches) && !empty($matches[2]) ? $matches[2] : null,
                array_key_exists(3, $matches) && $matches[3] === '01' ? $matches[3] == '1' : null,
                true
            );
        }
        return new SpanContext();
    }

    /**
     * Convert a SpanContext to header string
     *
     * @param SpanContext $context
     * @return string
     */
    public function serialize(SpanContext $context)
    {
        $ret = sprintf(
            '00-%s-%s-0%d',
            $context->traceId(),
            $context->spanId(),
            $context->enabled() ? '1' : '0'
        );
        return $ret;
    }
}
<?php

namespace App\Opencensus;

use App\Opencensus\Trace\Propagator\HttpHeaderPropagator;

use OpenCensus\Trace\Exporter\JaegerExporter;
use OpenCensus\Trace\Tracer;

class PyBundle extends Bundle
{
    public function tracerStart() {
        // Start the request tracing for this request
        $exporter = new JaegerExporter('my-jaeger', [
            'host' => 'distributed-tracing-test-jaeger'
        ]);
        Tracer::start($exporter, [
            'propagator' => new HttpHeaderPropagator()
        ]);
    }
}
<?php

namespace App\Opencensus;

use OpenCensus\Trace\Exporter\JaegerExporter;
use OpenCensus\Trace\Integrations\Doctrine;
use OpenCensus\Trace\Integrations\Symfony;
use OpenCensus\Trace\Tracer;

use Monolog\Logger;

use Symfony\Component\HttpKernel\Bundle\Bundle as BaseBundle;


class Bundle extends BaseBundle
{
    public function boot()
    {
        $this->setupOpenCensus();
    }

    public function tracerStart() {
        // Start the request tracing for this request
        $exporter = new JaegerExporter('my-jaeger', [
            'host' => 'distributed-tracing-test-jaeger'
        ]);
        Tracer::start($exporter);
    }

    private function setupOpenCensus()
    {
        if (php_sapi_name() == 'cli') {
            return;
        }

        $this->tracerStart();

        // Enable OpenCensus extension integrations
        // Doctrine::load();
        // Symfony::load();

    }
}

My 1st test was to start a trace in python, from python make a HTTP call to PHP and continue the trace in PHP. However, Jaeger is logging the PHP side as <trace-without-root-span>.

Could you give me a hint on what I am doing wrong?

@chingor13
Copy link
Member

The Jaeger exporter has a known issue (census-ecosystem/opencensus-php-exporter-jaeger#7) that the underlying Thrift library cannot support unsigned 64-bit integers (used for trace ids).

If python is generating a trace/span ids with 64-bit values, then PHP may not be able to handle it using the Jaeger exporter. As a workaround, you can try using the ZipkinExporter and point that to your Jaeger instance's zipkin api port.

@dragoscirjan
Copy link
Author

dragoscirjan commented Oct 3, 2018

@chingor13 the real bug is actually in PHP, in SpanConverter::convertTraceId method, as explained in this pull request.

Short story long, a 16 'digit' hex chunk can exceed php's INT sise, thus hexdec fails in converting the value correctly which affects the conversion of the traceId. Please see a solution in my PR, trying to cover both BC and GPM solutions for big numbers.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants