Getting Started

Get telemetry for your app in less than 5 minutes!

This page will show you how to get started with OpenTelemetry in C++.

You will learn how to instrument a simple C++ application, such that traces are emitted to the terminal.

Prerequisites

Ensure that you have the following installed locally:

  • Git
  • C++ compiler supporting C++ version >= 14
  • Make
  • CMake version >= 3.20

Example Application

The following example uses a basic Oat++ application. If you are not using Oat++, that’s OK - you can use OpenTelemetry C++ with any other web framework as well.

Setup

  • Create a folder named otel-cpp-starter.

  • move into the newly created folder. This will serve as your working directory.

  • After setting up dependencies, your directory structure should resemble this:

    otel-cpp-starter
    ├── oatpp
    ├── opentelemetry-cpp
    └── roll-dice
    

Dependencies

To begin, install Oat++ locally using the source code and make, following these steps:

  1. Obtain the Oat++ source code by cloning from the oatpp/oatpp GitHub repository.

    git clone https://github.com/oatpp/oatpp.git
    
  2. Navigate to the oatpp directory.

    cd oatpp
    
  3. Create a build subdirectory and navigate into it.

    mkdir build
    cd build
    
  4. Build oatpp using the cmake and make commands. This command will trigger the build process specified in the CMakeLists.txt included in the oatpp source code.

    cmake ..
    make
    
  5. Install oatpp.

This command will install the built oatpp library and headers on your system, making it accessible for development in your project.

sudo make install

To uninstall the built oatpp library and headers from your system.

sudo make uninstall

Next, install and build OpenTelemetry C++ locally using CMake, following these steps:

  1. In your terminal, navigate back to the otel-cpp-starter directory. Then, clone the OpenTelemetry C++ GitHub repository to your local machine.

    git clone https://github.com/open-telemetry/opentelemetry-cpp.git
    
  2. Change your working directory to the OpenTelemetry C++ SDK directory.

    cd opentelemetry-cpp
    
  3. Create a build directory and navigate into it.

    mkdir build
    cd build
    
  4. In the build directory run CMake, to configure and generate the build system.

    cmake ..
    

    Or, if the cmake --build fails, you can also try:

    cmake -DWITH_ABSEIL=ON ..
    
  5. Execute the build process.

    cmake --build .
    

With Oat++ and OpenTelemetry C++ ready, you can continue with creating the HTTP Server, that we want to instrument eventually.

Create and launch an HTTP Server

In your otel-cpp-starter folder, create a subfolder roll-dice, where the Oat++ library will be used by referencing the oatpp headers and linking them when compiling your project.

Create a file called CMakeLists.txt to define the Oat++ library directories, include paths, and link against Oat++ during the compilation process.

project(RollDiceServer)
cmake_minimum_required(VERSION 3.1)
# Set C++ standard (e.g., C++17)
set(CMAKE_CXX_STANDARD 17)
set(project_name roll-dice-server)

# Define your project's source files
set(SOURCES
    main.cpp  # Add your source files here
)

# Create an executable target
add_executable(dice-server ${SOURCES})

set(OATPP_ROOT ../oatpp)
find_library(OATPP_LIB NAMES liboatpp.a HINTS "${OATPP_ROOT}/build/src/" NO_DEFAULT_PATH)

if (NOT OATPP_LIB)
  message(SEND_ERROR "Did not find oatpp library ${OATPP_ROOT}/build/src")
endif()
#set the path to the directory containing "oatpp" package configuration files
include_directories(${OATPP_ROOT}/src)
target_link_libraries(dice-server PRIVATE ${OATPP_LIB})

Next, the sample HTTP server source code is needed. It will do the following:

  • Initialize an HTTP router and set up a request handler to generate a random number as the response when a GET request is made to the /rolldice endpoint.
  • Next, create a connection handler, a connection provider, and start the server on localhost:8080.
  • Lastly, initialize and run the application within the main function.

In that roll-dice folder, create a file called main.cpp and add the following code to the file.

#include "oatpp/web/server/HttpConnectionHandler.hpp"
#include "oatpp/network/Server.hpp"
#include "oatpp/network/tcp/server/ConnectionProvider.hpp"
#include <cstdlib>
#include <ctime>
#include <string>

using namespace std;

class Handler : public oatpp::web::server::HttpRequestHandler {
public:
  shared_ptr<OutgoingResponse> handle(const shared_ptr<IncomingRequest>& request) override {
    int low = 1;
    int high = 7;
    int random = rand() % (high - low) + low;
    // Convert a std::string to oatpp::String
    const string response = to_string(random);
    return ResponseFactory::createResponse(Status::CODE_200, response.c_str());
  }
};

void run() {
  auto router = oatpp::web::server::HttpRouter::createShared();
  router->route("GET", "/rolldice", std::make_shared<Handler>());
  auto connectionHandler = oatpp::web::server::HttpConnectionHandler::createShared(router);
  auto connectionProvider = oatpp::network::tcp::server::ConnectionProvider::createShared({"localhost", 8080, oatpp::network::Address::IP_4});
  oatpp::network::Server server(connectionProvider, connectionHandler);
  OATPP_LOGI("Dice Server", "Server running on port %s", connectionProvider->getProperty("port").getData());
  server.run();
}

int main() {
  oatpp::base::Environment::init();
  srand((int)time(0));
  run();
  oatpp::base::Environment::destroy();
  return 0;
}

Build and run the application with the following CMake commands.

mkdir build
cd build
cmake ..
cmake --build .

After successfully building your project, you can run the generated executable.

./dice-server

Then, open http://localhost:8080/rolldice in your browser to ensure it is working.

Instrumentation

To add OpenTelemetry to your application, update the CMakeLists.txt file with the following additional dependencies.

project(RollDiceServer)
cmake_minimum_required(VERSION 3.1)
# Set C++ standard (e.g., C++17)
set(CMAKE_CXX_STANDARD 17)
set(project_name roll-dice-server)

# Define your project's source files
set(SOURCES
    main.cpp  # Add your source files here
)
# Create an executable target
add_executable(dice-server ${SOURCES})

set(OATPP_ROOT ../oatpp)
set(OPENTELEMETRY_ROOT ../opentelemetry-cpp)
find_library(OATPP_LIB NAMES liboatpp.a HINTS "${OATPP_ROOT}/build/src/" NO_DEFAULT_PATH)
if (NOT OATPP_LIB)
  message(SEND_ERROR "Did not find oatpp library ${OATPP_ROOT}/build/src")
endif()
# set the path to the directory containing "oatpp" package configuration files
include_directories(${OATPP_ROOT}/src)

include_directories(${OPENTELEMETRY_ROOT}/api/include)
include_directories(${OPENTELEMETRY_ROOT}/sdk/include)
include_directories(${OPENTELEMETRY_ROOT}/sdk/src)
include_directories(${OPENTELEMETRY_ROOT}/exporters/ostream/include)

find_library(OPENTELEMETRY_COMMON_LIB NAMES libopentelemetry_common.a HINTS "${OPENTELEMETRY_ROOT}/build/sdk/src/common" NO_DEFAULT_PATH)
find_library(OPENTELEMETRY_TRACE_LIB NAMES libopentelemetry_trace.a HINTS "${OPENTELEMETRY_ROOT}/build/sdk/src/trace" NO_DEFAULT_PATH)
find_library(OPENTELEMETRY_EXPORTER_LIB NAMES libopentelemetry_exporter_ostream_span.a HINTS "${OPENTELEMETRY_ROOT}/build/exporters/ostream" NO_DEFAULT_PATH)
find_library(OPENTELEMETRY_RESOURCE_LIB NAMES libopentelemetry_resources.a HINTS "${OPENTELEMETRY_ROOT}/build/sdk/src/resource" NO_DEFAULT_PATH)

if(OPENTELEMETRY_COMMON_LIB AND OPENTELEMETRY_TRACE_LIB AND OPENTELEMETRY_EXPORTER_LIB AND OPENTELEMETRY_RESOURCE_LIB)
  message(STATUS "Found opentelemetry libraries")
else()
  message(SEND_ERROR "Did not find opentelemetry libraries")
endif()

target_link_libraries(dice-server PRIVATE ${OATPP_LIB} ${OPENTELEMETRY_COMMON_LIB} ${OPENTELEMETRY_TRACE_LIB} ${OPENTELEMETRY_EXPORTER_LIB} ${OPENTELEMETRY_RESOURCE_LIB})

Update the main.cpp file with the following code to initialize a tracer and to emit spans when the /rolldice request handler is called.

#include "oatpp/web/server/HttpConnectionHandler.hpp"
#include "oatpp/network/Server.hpp"
#include "oatpp/network/tcp/server/ConnectionProvider.hpp"

#include "opentelemetry/exporters/ostream/span_exporter_factory.h"
#include "opentelemetry/sdk/trace/exporter.h"
#include "opentelemetry/sdk/trace/processor.h"
#include "opentelemetry/sdk/trace/simple_processor_factory.h"
#include "opentelemetry/sdk/trace/tracer_provider_factory.h"
#include "opentelemetry/trace/provider.h"

#include <cstdlib>
#include <ctime>
#include <string>

using namespace std;
namespace trace_api = opentelemetry::trace;
namespace trace_sdk = opentelemetry::sdk::trace;
namespace trace_exporter = opentelemetry::exporter::trace;

namespace {
  void InitTracer() {
    auto exporter  = trace_exporter::OStreamSpanExporterFactory::Create();
    auto processor = trace_sdk::SimpleSpanProcessorFactory::Create(std::move(exporter));
    std::shared_ptr<opentelemetry::trace::TracerProvider> provider =
      trace_sdk::TracerProviderFactory::Create(std::move(processor));
    //set the global trace provider
    trace_api::Provider::SetTracerProvider(provider);
  }
  void CleanupTracer() {
    std::shared_ptr<opentelemetry::trace::TracerProvider> none;
    trace_api::Provider::SetTracerProvider(none);
  }

}

class Handler : public oatpp::web::server::HttpRequestHandler {
public:
  shared_ptr<OutgoingResponse> handle(const shared_ptr<IncomingRequest>& request) override {
    auto tracer = opentelemetry::trace::Provider::GetTracerProvider()->GetTracer("my-app-tracer");
    auto span = tracer->StartSpan("RollDiceServer");
    int low = 1;
    int high = 7;
    int random = rand() % (high - low) + low;
    // Convert a std::string to oatpp::String
    const string response = to_string(random);
    span->End();
    return ResponseFactory::createResponse(Status::CODE_200, response.c_str());
  }
};

void run() {
  auto router = oatpp::web::server::HttpRouter::createShared();
  router->route("GET", "/rolldice", std::make_shared<Handler>());
  auto connectionHandler = oatpp::web::server::HttpConnectionHandler::createShared(router);
  auto connectionProvider = oatpp::network::tcp::server::ConnectionProvider::createShared({"localhost", 8080, oatpp::network::Address::IP_4});
  oatpp::network::Server server(connectionProvider, connectionHandler);
  OATPP_LOGI("Dice Server", "Server running on port %s", connectionProvider->getProperty("port").getData());
  server.run();
}

int main() {
  oatpp::base::Environment::init();
  InitTracer();
  srand((int)time(0));
  run();
  oatpp::base::Environment::destroy();
  CleanupTracer();
  return 0;
}

Build your project again.

cd build
cmake ..
cmake --build .

After successfully building your project, you can run the generated executable.

./dice-server

When you send a request to the server at http://localhost:8080/rolldice, you will see a span being emitted to the terminal.

{
  "name" : "RollDiceServer",
  "trace_id": "f47bea385dc55e4d17470d51f9d3130b",
  "span_id": "deed994b51f970fa",
  "tracestate" : ,
  "parent_span_id": "0000000000000000",
  "start": 1698991818716461000,
  "duration": 64697,
  "span kind": "Internal",
  "status": "Unset",
  "service.name": "unknown_service",
  "telemetry.sdk.language": "cpp",
  "telemetry.sdk.name": "opentelemetry",
  "telemetry.sdk.version": "1.11.0",
  "instr-lib": "my-app-tracer"
}

Next Steps

For more information about instrumenting your code, refer the instrumentation documentation.

You’ll also want to configure an appropriate exporter to export your telemetry data to one or more telemetry backends.