OpenTelemetry C++ SDK¶
이 문서는 SDK 계측 방식을 사용합니다. 애플리케이션 코드에서 SDK를 초기화하고 스팬을 생성한 후 Exporter를 구성하여 DataKit을 통해 트레이스를 Guance로 전송합니다. 이는 제로 코드 주입이 아니며, 의존성을 설치하거나 환경 변수를 설정해도 모든 프레임워크 호출이 자동으로 수집되지는 않습니다. 이 문서는 Trace만 활성화하며 Kubernetes는 다루지 않습니다.
사전 요구 사항¶
- 아래 빌드 명령은 Debian/Ubuntu에 적용되며 개발 의존성을 설치할 권한이 필요합니다.
- C++17을 지원하는 컴파일러, CMake 3.16 이상, Git, libcurl, Protobuf 개발 라이브러리와
protoc, nlohmann-json을 사용합니다. - 예제는 OpenTelemetry C++
v1.23.0을 고정 사용합니다. 업그레이드할 때 SDK, 컴파일러, 의존 라이브러리 및 ABI 호환성을 함께 확인해야 합니다. - DataKit이 설치되어 있으며 대상 워크스페이스의 설치 명령으로 전송 주소와 Token이 설정되어 있어야 합니다. 애플리케이션은 DataKit HTTP 포트
9529에 접근할 수 있어야 합니다.
1. OpenTelemetry 수집기 활성화¶
DataKit 호스트에서 설정 디렉터리로 이동합니다. 설정 파일이 없을 때만 샘플을 복사하고, 이미 파일이 있다면 직접 수정하십시오:
opentelemetry.conf에 다음 설정이 포함되어 있는지 확인합니다. 사용자 정의 태그는 customer_tags를 통해 유지됩니다:
[[inputs.opentelemetry]]
customer_tags = ["team", "app.operation"]
[inputs.opentelemetry.http]
http_status_ok = 200
trace_api = "/otel/v1/traces"
metric_api = "/otel/v1/metrics"
logs_api = "/otel/v1/logs"
로컬 연동은 127.0.0.1:9529를 사용합니다. 호스트 간 연동 시 DataKit 기본 설정 datakit.conf의 [http_api].listen에 애플리케이션이 접근할 수 있는 수신 대기 주소를 설정하고 네트워크 접근 범위를 제한하십시오. HTTP 수신 대기 주소는 수집기 파일에서 설정하지 않습니다.
DataKit을 재시작하고 확인합니다:
/v1/ping은 HTTP 서비스에 연결 가능한지만 확인하며, 트레이스가 저장되었음을 의미하지는 않습니다. 자세한 내용은 OpenTelemetry 수집기를 참조하십시오. DataKit이 워크스페이스 인증을 담당하므로 예제 애플리케이션에서 워크스페이스 Token을 직접 설정하지 않습니다.
2. 애플리케이션에 OpenTelemetry 연동¶
의존성 설치¶
개발 머신에 의존성을 설치하고 공식 소스 코드를 받습니다. 다음 디렉터리 이름은 아직 존재하지 않아야 합니다:
sudo apt-get update
sudo apt-get install -y build-essential cmake git libcurl4-openssl-dev \
libprotobuf-dev protobuf-compiler nlohmann-json3-dev
mkdir otel-cpp-demo
cd otel-cpp-demo
git clone --branch v1.23.0 --depth 1 --recurse-submodules --shallow-submodules \
https://github.com/open-telemetry/opentelemetry-cpp.git
otel-cpp-demo 루트 디렉터리에 CMakeLists.txt를 생성합니다. WITH_OTLP_HTTP는 HTTP Exporter를 활성화하며, 테스트와 예제를 비활성화하면 빌드 시간을 줄일 수 있습니다:
cmake_minimum_required(VERSION 3.16)
project(otel_cpp_demo LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(WITH_BENCHMARK OFF CACHE BOOL "" FORCE)
set(WITH_EXAMPLES OFF CACHE BOOL "" FORCE)
set(WITH_OTLP_GRPC OFF CACHE BOOL "" FORCE)
set(WITH_OTLP_HTTP ON CACHE BOOL "" FORCE)
add_subdirectory(opentelemetry-cpp)
add_executable(otel-cpp-demo main.cpp)
target_link_libraries(otel-cpp-demo PRIVATE
opentelemetry_trace
opentelemetry_exporter_otlp_http
)
SDK 초기화 및 스팬 생성¶
같은 디렉터리에 main.cpp를 생성합니다. Resource 감지기로 환경 변수를 읽고 Provider를 등록한 다음, 스팬을 명시적으로 종료하고 Provider를 플러시·종료합니다:
#include <chrono>
#include <memory>
#include <utility>
#include "opentelemetry/exporters/otlp/otlp_http_exporter_factory.h"
#include "opentelemetry/exporters/otlp/otlp_http_exporter_options.h"
#include "opentelemetry/sdk/resource/resource.h"
#include "opentelemetry/sdk/trace/batch_span_processor_factory.h"
#include "opentelemetry/sdk/trace/batch_span_processor_options.h"
#include "opentelemetry/sdk/trace/provider.h"
#include "opentelemetry/sdk/trace/samplers/parent.h"
#include "opentelemetry/sdk/trace/samplers/trace_id_ratio.h"
#include "opentelemetry/sdk/trace/tracer_provider.h"
namespace otlp = opentelemetry::exporter::otlp;
namespace sdktrace = opentelemetry::sdk::trace;
int main()
{
otlp::OtlpHttpExporterOptions options;
options.content_type = otlp::HttpRequestContentType::kBinary;
auto exporter = otlp::OtlpHttpExporterFactory::Create(options);
sdktrace::BatchSpanProcessorOptions batch_options;
auto processor = sdktrace::BatchSpanProcessorFactory::Create(
std::move(exporter), batch_options);
auto resource = opentelemetry::sdk::resource::Resource::Create({});
auto sampler = std::make_unique<sdktrace::ParentBasedSampler>(
std::make_shared<sdktrace::TraceIdRatioBasedSampler>(1.0));
auto provider = std::make_shared<sdktrace::TracerProvider>(
std::move(processor), resource, std::move(sampler));
std::shared_ptr<opentelemetry::trace::TracerProvider> api_provider = provider;
sdktrace::Provider::SetTracerProvider(api_provider);
auto tracer = provider->GetTracer("otel-cpp-demo");
auto parent = tracer->StartSpan("checkout");
{
auto scope = tracer->WithActiveSpan(parent);
auto child = tracer->StartSpan("db.lookup");
child->SetAttribute("app.operation", "lookup");
child->End();
}
parent->End();
const bool flushed = provider->ForceFlush(std::chrono::seconds(10));
const bool stopped = provider->Shutdown(std::chrono::seconds(10));
return flushed && stopped ? 0 : 1;
}
빌드 및 실행¶
CMakeLists.txt가 포함된 프로젝트 루트 디렉터리에서 빌드합니다:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --target otel-cpp-demo --parallel 2
애플리케이션을 시작하는 동일한 터미널에서 다음 파라미터를 설정합니다. 호스트 간 연동 시 127.0.0.1을 실제 DataKit 주소로 바꾸십시오:
export OTEL_SERVICE_NAME="order-service"
export OTEL_RESOURCE_ATTRIBUTES="deployment.environment.name=prod,service.version=1.0.0,team=backend"
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="http://127.0.0.1:9529/otel/v1/traces"
./build/otel-cpp-demo
장기 실행 서비스는 Provider를 한 번만 초기화합니다. 종료할 때는 먼저 요청 처리를 중지하고 스팬을 종료한 다음 ForceFlush()와 Shutdown()을 실행하십시오. 요청이 끝날 때마다 Provider를 종료하지 마십시오.
3. 데이터 전송 파라미터¶
| 파라미터 또는 설정 | 설명 |
|---|---|
OTEL_SERVICE_NAME |
service.name. 예제에서는 order-service를 사용하며, 안정적인 서비스 이름으로 설정해야 합니다. |
OTEL_RESOURCE_ATTRIBUTES |
쉼표로 구분된 리소스 속성입니다. 예제에서는 환경, 버전, team을 설정합니다. 사용자 정의 필드는 DataKit customer_tags에 추가해야 합니다. |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT |
Trace 전용 전체 주소: http://127.0.0.1:9529/otel/v1/traces. 일반 기본 주소보다 우선합니다. |
OTEL_EXPORTER_OTLP_ENDPOINT |
선택적 기본 주소: http://127.0.0.1:9529/otel. Trace 전용 주소가 설정되지 않은 경우 Exporter가 /v1/traces를 추가합니다. |
OTEL_EXPORTER_OTLP_HEADERS |
선택적 OTLP 요청 헤더로, 형식은 key=value,key2=value2입니다. 수신 측 또는 프록시가 인증을 요구할 때만 설정합니다. |
이 예제는 OtlpHttpExporter를 빌드·생성하며 content_type = kBinary를 설정합니다. OTEL_EXPORTER_OTLP_PROTOCOL을 변경해도 gRPC Exporter로 바뀌지 않습니다. gRPC를 사용하려면 빌드 옵션을 별도로 활성화하고 해당 Exporter를 링크한 다음 초기화 코드를 수정해야 합니다.
샘플링은 코드에서 ParentBased + 루트 트레이스 비율 1.0으로 구성되며, 부모 스팬의 샘플링 결정을 따릅니다. 프로덕션 환경에서는 예제의 비율을 0.1로 변경하여 루트 트레이스의 약 10%를 샘플링할 수 있습니다. 이 예제는 샘플러를 명시적으로 설정하므로 OTEL_TRACES_SAMPLER 또는 OTEL_TRACES_SAMPLER_ARG에 의존하지 않습니다.
예제는 Trace 내보내기 파이프라인을 명시적으로 생성하므로 OTEL_TRACES_EXPORTER가 이를 선택하거나 종료하지 않습니다. Metric·Log Provider를 생성하지 않았으므로 OTEL_METRICS_EXPORTER나 OTEL_LOGS_EXPORTER를 설정해도 해당 신호가 활성화되지 않습니다. 로그는 DataKit의 로그 파일 수집 기능을 별도로 사용할 수 있습니다.
컨텍스트 전파 및 비즈니스 연동¶
WithActiveSpan의 Scope는 현재 컨텍스트에만 영향을 미치며, 부모를 다른 스레드나 서비스로 자동 전달하지 않습니다. 스레드를 전환할 때는 Context를 명시적으로 전달하고 복원해야 합니다. HTTP/RPC에서는 HttpTraceContext와 TextMapCarrier를 함께 사용하여 traceparent와 tracestate를 추출·주입하고, 서버 진입점에 원격 부모를 설정해야 합니다. 실제 비즈니스에서는 오류 경로에서도 스팬 상태를 설정하고 모든 스팬이 종료되도록 보장해야 합니다.
검증 및 문제 해결¶
- 예제를 실행한 후 Guance의 애플리케이션 성능 모니터링(APM)에서
order-service로 트레이스를 조회하여checkout과 하위 스팬db.lookup이 있는지 확인합니다. - 데이터가 없으면 수집기가 활성화되었는지, 애플리케이션 환경 변수가 적용되었는지, HTTP 경로에
/otel/v1/traces가 포함되어 있는지, DataKit과 애플리케이션의 내보내기 오류를 확인합니다. - 스팬이 종료되었고 Provider가 종료 전에 플러시를 완료했는지 확인합니다. 강제 종료하거나 루트 트레이스 비율을
0으로 설정하면 예상한 데이터를 확인할 수 없습니다. 네트워크 연결이 되어 있어도 내보내기가 성공한 것은 아닙니다.