Look Inside Your C++ Code: Why Observability Is Not a Luxury, But a Necessity
\n\nImagine: you've launched a high-load service in C++. An hour passes, two, a day — everything is flying. And then — bam! — users complain about slowdowns, and you sit there guessing: where is the bottleneck? In memory? In the network? Or did someone just forget to release a resource? Without an observability system, you are like a ship captain with sealed portholes. You feel something is wrong, but you can't see exactly what.
\n\nObservability in C++ is your set of periscopes and radars. It's not just logging; it's three pillars: tracing, metrics, and logs. In this article, we'll break down each one with real code examples, show you how to integrate C++ with OpenTelemetry and Prometheus, and provide ready-made recipes for production. Let's go!
\n\nThe Three Pillars of Observability: Tracing, Metrics, Logs
\n\nBefore diving into the code, let's clearly define how these concepts differ and complement each other. Many people confuse observability with simple logging — and that's a mistake. It's like comparing a flashlight to a floodlight system.
\n\n- \n
- Logs are events. "What happened?" (e.g., "User logged in" or "Database connection error"). They are detailed, but without context, they can be useless. \n
- Metrics are numbers. "How many?" (requests per second, CPU usage, response time). They are aggregated and show trends. \n
- Tracing is the path of a request. "How exactly did this happen?" (from service entry to database call and client response). It's the link between logs and metrics. \n
Ideal observability in C++ means that by looking at a dashboard, you can understand in 10 seconds: where, when, and why an anomaly occurred. Without this, you'll be playing a guessing game with production.
\n\nTracing in C++: How to Implement OpenTelemetry
\n\nLet's start with the most powerful, yet most complex tool — tracing. OpenTelemetry is the de facto standard for collecting telemetry. In C++, it's not as richly supported as in Go or Java, but it works reliably.
\n\nSuppose we have a simple HTTP server in C++ (using the concept, not a specific library). We need to track how long each request takes to process and what internal calls occur.
\n\nSetting Up OpenTelemetry in C++
\n\nFirst, install the dependencies. In CMakeLists.txt, add:
\n\nfind_package(opentelemetry-cpp REQUIRED)\ntarget_link_libraries(my_app PRIVATE opentelemetry::api opentelemetry::exporter_ostream)\n\nNow, let's create a tracer and spans (a span is a unit of work). Here's a minimal example:
\n\n#include <opentelemetry/trace/provider.h>\n#include <opentelemetry/exporters/ostream/span_exporter.h>\n#include <opentelemetry/sdk/trace/tracer_provider.h>\n#include <opentelemetry/sdk/trace/simple_processor.h>\n\nnamespace trace_api = opentelemetry::trace;\nnamespace trace_sdk = opentelemetry::sdk::trace;\nnamespace trace_exporter = opentelemetry::exporter::trace;\n\nvoid init_tracer() {\n auto exporter = std::unique_ptr<trace_sdk::SpanExporter>(\n new trace_exporter::OStreamSpanExporter(std::cout));\n auto processor = std::unique_ptr<trace_sdk::SpanProcessor>(\n new trace_sdk::SimpleSpanProcessor(std::move(exporter)));\n auto provider = std::make_shared<trace_sdk::TracerProvider>(std::move(processor));\n trace_api::Provider::SetTracerProvider(provider);\n}\n\nvoid process_request(const std::string& user_id) {\n auto tracer = trace_api::Provider::GetTracerProvider()->GetTracer("my_service");\n auto span = tracer->StartSpan("process_request");\n span->SetAttribute("user.id", user_id);\n\n // Simulating work\n std::this_thread::sleep_for(std::chrono::milliseconds(100));\n\n span->End();\n}\n\nint main