OpenTracing Plugins for C++

Ryan Burn
OpenTracing
Published in
2 min readJun 25, 2018

OpenTracing’s APIs provide a portable mechanism to instrument software for distributed tracing. The same code can be reused with any tracing vendor providing implementations of the OpenTracing APIs. The APIs cover the creation of traces, spans, and the propagation of span context across processes; but one area that has required interacting with tracing libraries in a vendor-specific manner is the construction and initialization of a tracer.

For OSS deployed as binary applications such as proxies and load balancers like NGINX, Envoy, and Varnish; or databases like PostgreSQL and MySQL; this has posed a challenge to providing turnkey tracing. Supporting a particular tracer requires writing vendor-specific code to expose that tracer’s configuration options and map those options to its construction. Yet end-users of these applications rarely have the ability to tweak the source code, so being able to work out-of-the-box with whatever tracer deployed is a desirable goal.

The Plugin Approach

As a solution to this problem, OpenTracing’s C++ API now supports a plugin approach where vendor tracing libraries are dynamically loaded at runtime and a tracer is created from a JSON representation of its configuration options.

The example application ot-chat shows how this can work in practice. It’s a chat room server written with Boost.Beast that communicates with clients over a WebSocket and broadcasts messages to all its connections. By using the plugin approach, the same binary for the server can be deployed with any tracer.

Configuring pluggable tracing

Any application, such as ot-chat, that uses the plugin approach needs to take options for specifying a vendor tracing library and the configuration to use when creating a tracer. By convention, the tracing libraries use JSON to represent the tracer’s options. This makes it possible for applications to embed a tracer’s configuration within their own configuration. Here’s what an example configuration for ot-chat looks like to set up Jaeger’s tracer:

Figure 1: An example configuration for the application to use Jaeger

Here, tracer_libraryprovides the path to Jaeger’s plugin and tracer_configuration is an embedding of Jaeger’s configuration.

Constructing a tracer

The snippet below shows how we can dynamically load tracer_library and construct an opentracing::Tracer from it using DynamicallyLoadTracingLibrary.

Figure 2: Creating a tracer from a dynamically loaded library

Once constructed, the standard OpenTracing API methods of opentracing::Tracer are available to instrument the rest of our application.

Figure 3: Example instrumentation using the dynamically loaded tracer

Conclusion

By using OpenTracing’s plugin approach, applications can support distributed tracing in a way that minimizes their dependencies and offers vendor-neutrality. This opens the door to adding tracing support to a broad range of common infrastructure components. Pluggable tracing is already supported in NGINX and Envoy. Additionally, we have plans to offer an OpenTracing C API with similar plugin support for C applications.

--

--