• Stars
    star
    270
  • Rank 148,629 (Top 3 %)
  • Language
    PHP
  • License
    Apache License 2.0
  • Created almost 7 years ago
  • Updated 9 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Zipkin instrumentation for PHP

Zipkin PHP

CI Latest Stable Version Coverage Status Minimum PHP Version Total Downloads License

Zipkin PHP is the official PHP Tracer implementation for Zipkin, supported by the OpenZipkin community.

Installation

composer require openzipkin/zipkin

Setup

use Zipkin\Annotation;
use Zipkin\Endpoint;
use Zipkin\Samplers\BinarySampler;
use Zipkin\TracingBuilder;
use Zipkin\Reporters\Http;

// First we create the endpoint that describes our service
$endpoint = Endpoint::create('my_service');

$reporter = new Http(['endpoint_url' => 'http://myzipkin:9411/api/v2/spans']);
$sampler = BinarySampler::createAsAlwaysSample();
$tracing = TracingBuilder::create()
    ->havingLocalEndpoint($endpoint)
    ->havingSampler($sampler)
    ->havingReporter($reporter)
    ->build();

$tracer = $tracing->getTracer();

...

$tracer->flush();

Obs. for a more complete frontend/backend example, check this repository.

Tracing

The tracer creates and joins spans that model the latency of potentially distributed work. It can employ sampling to reduce overhead in process or to reduce the amount of data sent to Zipkin.

Spans returned by a tracer report data to Zipkin when finished, or do nothing if unsampled. After starting a span, you can annotate events of interest or add tags containing details or lookup keys.

Spans have a context which includes trace identifiers that place it at the correct spot in the tree representing the distributed operation.

Local Tracing

When tracing local code, just run it inside a span

$span = $tracer->newTrace();
$span->setName('encode');
$span->start();

try {
  doSomethingExpensive();
} finally {
  $span->finish();
}

In the above example, the span is the root of the trace. In many cases, you will be a part of an existing trace. When this is the case, call newChild instead of newTrace

$span = $tracer->newChild($root->getContext());
$span->setName('encode');
$span->start();
try {
  doSomethingExpensive();
} finally {
  $span->finish();
}

Customizing spans

Once you have a span, you can add tags to it, which can be used as lookup keys or details. For example, you might add a tag with your runtime version.

$span->tag('http.status_code', '200');

RPC tracing

RPC tracing is often done automatically by interceptors. Under the scenes, they add tags and events that relate to their role in an RPC operation.

Here's an example of a client span:

// before you send a request, add metadata that describes the operation
$span = $tracer->newTrace();
$span->setName('get');
$span->setKind(Kind\CLIENT);
$span->tag('http.status_code', '200');
$span->tag(Tags\HTTP_PATH, '/api');
$span->setRemoteEndpoint(Endpoint::create('backend', 127 << 24 | 1, null, 8080));

// when the request is scheduled, start the span
$span->start();

// if you have callbacks for when data is on the wire, note those events
$span->annotate(Annotation\WIRE_SEND);
$span->annotate(Annotation\WIRE_RECV);

// when the response is complete, finish the span
$span->finish();

Sampling

Sampling may be employed to reduce the data collected and reported out of process. When a span isn't sampled, it adds no overhead (noop).

Sampling is an up-front decision, meaning that the decision to report data is made at the first operation in a trace, and that decision is propagated downstream.

By default, there's a global sampler that applies a single rate to all traced operations. Sampler is how you indicate this, and it defaults to trace every request.

Custom sampling

You may want to apply different policies depending on what the operation is. For example, you might not want to trace requests to static resources such as images, or you might want to trace all requests to a new api.

Most users will use a framework interceptor which automates this sort of policy. Here's how they might work internally.

private function newTrace(Request $request) {
  $flags = SamplingFlags::createAsEmpty();
  if (strpos($request->getUri(), '/experimental') === 0) {
    $flags = DefaultSamplingFlags::createAsSampled();
  } else if (strpos($request->getUri(), '/static') === 0) {
    $flags = DefaultSamplingFlags::createAsSampled();
  }
  return $tracer->newTrace($flags);
}

Propagation

Propagation is needed to ensure activity originating from the same root are collected together in the same trace. The most common propagation approach is to copy a trace context from a client sending an RPC request to a server receiving it.

For example, when an downstream Http call is made, its trace context is sent along with it, encoded as request headers:

   Client Span                                                Server Span
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  β”‚                                       β”‚                  β”‚
β”‚   TraceContext   β”‚           Http Request Headers        β”‚   TraceContext   β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ TraceId      β”‚ β”‚          β”‚ X-B3-TraceId      β”‚        β”‚ β”‚ TraceId      β”‚ β”‚
β”‚ β”‚              β”‚ β”‚          β”‚                   β”‚        β”‚ β”‚              β”‚ β”‚
β”‚ β”‚ ParentSpanId β”‚ β”‚ Extract  β”‚ X-B3-ParentSpanId β”‚ Inject β”‚ β”‚ ParentSpanId β”‚ β”‚
β”‚ β”‚              β”œβ”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€>β”‚                   β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”Ό>β”‚              β”‚ β”‚
β”‚ β”‚ SpanId       β”‚ β”‚          β”‚ X-B3-SpanId       β”‚        β”‚ β”‚ SpanId       β”‚ β”‚
β”‚ β”‚              β”‚ β”‚          β”‚                   β”‚        β”‚ β”‚              β”‚ β”‚
β”‚ β”‚ Sampled      β”‚ β”‚          β”‚ X-B3-Sampled      β”‚        β”‚ β”‚ Sampled      β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚                  β”‚                                       β”‚                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The names above are from B3 Propagation, which is built-in to Brave and has implementations in many languages and frameworks.

Most users will use a framework interceptor which automates propagation. Here's how they might work internally.

Here's what client-side propagation might look like

// configure a function that injects a trace context into a request
$injector = $tracing->getPropagation()->getInjector(new RequestHeaders);

// before a request is sent, add the current span's context to it
$injector($span->getContext(), $request);

Here's what server-side propagation might look like

// configure a function that extracts the trace context from a request
$extractor = $tracing->getPropagation()->getExtractor(new RequestHeaders);
$extracted = $extractor($request);

$span = $tracer->newChild($extracted);
$span->setKind(Kind\SERVER);

If you aren't using a framework or don't have access to the Request object, you can extract the context from the $_SERVER variable

$extractor = $tracing->getPropagation()->getExtractor(new ServerHeaders);
$extracted = $extractor($_SERVER);

Extracting a propagated context

The Extractor reads trace identifiers and sampling status from an incoming request or message. The carrier is usually a request object or headers.

SamplingFlags|TraceContext is usually only used with $tracer->newChild(extracted), unless you are sharing span IDs between a client and a server.

Implementing Propagation

Extractor will output a SamplingFlags|TraceContext with one of the following:

  • TraceContext if trace and span IDs were present.
  • SamplingFlags if no identifiers were present

Current Span

Zipkin supports a "current span" concept which represents the in-flight operation. Tracer::currentSpan() can be used to add custom tags to a span and Tracer::nextSpan() can be used to create a child of whatever is in-flight.

A common use case for the current span is to instrument RPC clients. For example:

/**
  * This http clients composes an http client using PSR7
  */
class TraceClient implements ClientInterface
{
    public function request($method, $uri = '', array $options = [])
    {
        /* Gets the child Span of the current one */
        $span = $this->tracer->nextSpan();
        $span->setKind(Zipkin\Kind\CLIENT);
        $span->tag(Tags\HTTP_PATH, $uri);

        try {
            $response = $this->client->request($method, $uri, $options);
            $span->tag(Tags\HTTP_STATUS_CODE, (string) $response->getStatusCode());

            return $response;
        catch (Throwable $e) {
            $span->setError($e);
            throw $e;
        } finally {
            $span->finish();
        }
    }
}

Setting a span in scope manually

When writing new instrumentation, it is important to place a span you created in scope as the current span.

In edge cases, you may need to clear the current span temporarily. For example, launching a task that should not be associated with the current request. To do this, simply pass null to openScope.

Instrumentation

Tests

Tests can be run by

composer test

Whereas static checks can be run by:

composer static-check

Reference

More Repositories

1

zipkin

Zipkin is a distributed tracing system
Java
16,769
star
2

brave

Java distributed tracing implementation compatible with Zipkin backend services.
Java
2,327
star
3

zipkin-go

Zipkin distributed tracing library for go.
Go
604
star
4

zipkin-js

Zipkin instrumentation for Node.js and browsers
JavaScript
561
star
5

b3-propagation

Repository that describes and sometimes implements B3 propagation
518
star
6

zipkin4net

A .NET client library for Zipkin
C#
341
star
7

brave-example

A collection of examples how to use brave instrumentation in various frameworks and libraries.
Java
210
star
8

zipkin-dependencies

Spark job that aggregates zipkin spans for use in the UI
Java
174
star
9

zipkin-reporter-java

Shared library for reporting zipkin spans on transports such as http or kafka
Java
122
star
10

zipkin-ruby

zipkin-tracer ruby gem
Ruby
98
star
11

zipkin-gcp

Reporters and collectors for use in Google Cloud Platform
Java
89
star
12

zipkin-aws

Reporters and collectors for use in Amazon's cloud
Java
69
star
13

zipkin-php-example

See how much time php services spend on an http request
PHP
59
star
14

zipkin-api

Zipkin's language independent model and HTTP Api Definitions
Thrift
59
star
15

zipkin-js-example

Example project that shows how to use zipkin with javascript
JavaScript
58
star
16

zipkin-finagle

Integration between Finagle tracing to Zipkin transports such as http and kafka
Java
40
star
17

openzipkin.github.io

content for https://zipkin.io
HTML
39
star
18

zipkin-browser-extension

Chrome and Firefox browser extensions for Zipkin
JavaScript
25
star
19

pyramid_zipkin-example

See how much time python services spend on an http request
Python
14
star
20

brave-cassandra

Tracing instrumentation for Cassandra and the DataStax Java Driver
Java
12
star
21

docker-java

A small Docker image based on azul/zulu-openjdk-alpine
Shell
11
star
22

zipkin-api-example

Example of how to use the OpenApi/Swagger api spec
Go
9
star
23

brave-karaf

Karaf integration and tests for Brave (java Zipkin tracer)
Java
6
star
24

zipkin-layout-factory

Spring Boot Layout Factory for Zipkin Server derivatives
Shell
6
star
25

zipkin-support

repository for support questions raised as issues
4
star
26

zipkin-ruby-example

Ruby
4
star
27

docker-alpine

Alpine Linux base layer for Zipkin Docker images
Shell
3
star
28

zipkin-release

Documentation and templates used for projects released the same way as OpenZipkin
Python
1
star