This page covers instrumenting a C++ application with the OpenTelemetry SDK to send logs and traces to Bronto over OTLP/HTTP via a local OTel Collector.
If you don’t have a Collector and want to export directly from your application to Bronto, see Direct export to Bronto at the bottom of this page.
Prerequisites
Install dependencies
vcpkg install opentelemetry-cpp
Link the required components in your CMakeLists.txt:
find_package(opentelemetry-cpp CONFIG REQUIRED)
target_link_libraries(myapp PRIVATE
opentelemetry-cpp::logs
opentelemetry-cpp::otlp_http_log_record_exporter
opentelemetry-cpp::resources
)
Set up an OtlpHttpLogRecordExporter, a BatchLogRecordProcessor, and a LoggerProvider. Register it as the global provider so any part of your application can obtain a logger.
#include "opentelemetry/exporters/otlp/otlp_http_log_record_exporter_factory.h"
#include "opentelemetry/exporters/otlp/otlp_http_log_record_exporter_options.h"
#include "opentelemetry/logs/provider.h"
#include "opentelemetry/sdk/logs/logger_provider_factory.h"
#include "opentelemetry/sdk/logs/processor/batch_log_record_processor_factory.h"
#include "opentelemetry/sdk/resource/resource.h"
namespace logs_sdk = opentelemetry::sdk::logs;
namespace logs_api = opentelemetry::logs;
namespace otlp = opentelemetry::exporter::otlp;
namespace resource = opentelemetry::sdk::resource;
void InitLogging()
{
otlp::OtlpHttpLogRecordExporterOptions opts;
opts.url = "http://localhost:4318/v1/logs";
auto exporter = otlp::OtlpHttpLogRecordExporterFactory::Create(opts);
auto processor = logs_sdk::BatchLogRecordProcessorFactory::Create(
std::move(exporter), {});
auto res = resource::Resource::Create({
{"service.name", "my-service"},
{"service.namespace", "my-team"},
{"deployment.environment","production"},
});
auto provider = logs_sdk::LoggerProviderFactory::Create(
std::move(processor), res);
logs_api::Provider::SetLoggerProvider(
opentelemetry::nostd::shared_ptr<logs_api::LoggerProvider>(provider.release()));
}
Call InitLogging() once at application startup, before any log emission.
The opts.url field in the snippet above connects to the OTel Collector on localhost:4318. If your Collector runs on a different host or port, update the URL accordingly.
Set resource attributes
Resource attributes are attached to every log record exported from this process. Two attributes drive how Bronto organises incoming logs:
| OTel attribute | Bronto concept | Description |
|---|
service.name | Dataset | Groups logs from one service |
service.namespace | Collection | Groups related services or a team’s services |
These are set via Resource::Create in the setup above.
Complete example
#include "otel_logging.h"
#include "opentelemetry/logs/provider.h"
namespace logs_api = opentelemetry::logs;
int main()
{
InitLogging();
auto logger = logs_api::Provider::GetLoggerProvider()
->GetLogger("my-logger", "my-service");
logger->EmitLogRecord(logs_api::Severity::kInfo, "Application started");
logger->EmitLogRecord(logs_api::Severity::kWarn, "Low disk space");
logger->EmitLogRecord(logs_api::Severity::kError, "Database connection failed");
// Flush buffered records before exit
logs_api::Provider::GetLoggerProvider()->ForceFlush();
return 0;
}
Unlike higher-level language SDKs, there is no automatic bridge from a popular C++ logging library (spdlog, glog) in the official OTel SDK yet. Community bridges exist — search the OpenTelemetry registry for your preferred library.
Verify log collection
After running your application, open the Search page in Bronto. Filter by the dataset name you set in service.name — your log records should appear within a few seconds.
If no logs appear, check:
- The OTel Collector is running and reachable at the configured endpoint.
- The Collector’s pipeline includes a
logs pipeline with an otlp receiver and the Bronto exporter — see Connect Open Telemetry to Bronto.
InitLogging() is called before the first log emission.
ForceFlush() is called before process exit to flush buffered records.
Traces
Auto-instrumentation for C++ is more limited than other languages. Check the OpenTelemetry C++ registry for available community instrumentation packages for HTTP servers, gRPC, and database clients.
Link tracing components
Add the tracing libraries to your CMakeLists.txt alongside the logging ones:
target_link_libraries(myapp PRIVATE
opentelemetry-cpp::logs
opentelemetry-cpp::otlp_http_log_record_exporter
opentelemetry-cpp::trace
opentelemetry-cpp::otlp_http_exporter
)
otel_logging.cpp (extended)
#include "opentelemetry/exporters/otlp/otlp_http_exporter_factory.h"
#include "opentelemetry/exporters/otlp/otlp_http_exporter_options.h"
#include "opentelemetry/trace/provider.h"
#include "opentelemetry/sdk/trace/tracer_provider_factory.h"
#include "opentelemetry/sdk/trace/batch_span_processor_factory.h"
namespace trace_sdk = opentelemetry::sdk::trace;
namespace trace_api = opentelemetry::trace;
void InitTracing(const resource::Resource& res)
{
otlp::OtlpHttpExporterOptions traceOpts;
traceOpts.url = "http://localhost:4318/v1/traces";
auto exporter = otlp::OtlpHttpExporterFactory::Create(traceOpts);
auto processor = trace_sdk::BatchSpanProcessorFactory::Create(std::move(exporter), {});
auto provider = trace_sdk::TracerProviderFactory::Create(std::move(processor), res);
trace_api::Provider::SetTracerProvider(
opentelemetry::nostd::shared_ptr<trace_api::TracerProvider>(provider.release()));
}
Pass the same res used in InitLogging so both signals share service.name and service.namespace.
Creating spans
auto tracer = trace_api::Provider::GetTracerProvider()->GetTracer("my-service");
auto span = tracer->StartSpan("process-payment");
auto scope = tracer->WithActiveSpan(span);
span->SetAttribute("payment.amount", 99.99);
logger->EmitLogRecord(logs_api::Severity::kInfo, "Processing payment");
span->End();
Direct export to Bronto
If you are not using an OTel Collector, export directly to Bronto by replacing the exporter configurations with the Bronto OTLP endpoints and your API key:
// Logs
otlp::OtlpHttpLogRecordExporterOptions logOpts;
logOpts.url = "https://ingestion.eu.bronto.io/v1/logs"; // or ingestion.us.bronto.io
logOpts.http_headers.push_back({"x-bronto-api-key", "<YOUR_API_KEY>"});
// Traces
otlp::OtlpHttpExporterOptions traceOpts;
traceOpts.url = "https://ingestion.eu.bronto.io/v1/traces"; // or ingestion.us.bronto.io
traceOpts.http_headers.push_back({"x-bronto-api-key", "<YOUR_API_KEY>"});
| Region | Logs endpoint | Traces endpoint |
|---|
| EU | https://ingestion.eu.bronto.io/v1/logs | https://ingestion.eu.bronto.io/v1/traces |
| US | https://ingestion.us.bronto.io/v1/logs | https://ingestion.us.bronto.io/v1/traces |
See API Keys for how to create a key with ingestion permissions. No other changes to the rest of the setup are required.