Getting Started
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:
Obtain the Oat++ source code by cloning from the oatpp/oatpp GitHub repository.
git clone https://github.com/oatpp/oatpp.git
Navigate to the
oatpp
directory.cd oatpp
Create a
build
subdirectory and navigate into it.mkdir build cd build
Build oatpp using the
cmake
andmake
commands. This command will trigger the build process specified in theCMakeLists.txt
included in the oatpp source code.cmake .. make
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:
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
Change your working directory to the OpenTelemetry C++ SDK directory.
cd opentelemetry-cpp
Create a build directory and navigate into it.
mkdir build cd build
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 ..
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.