> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bronto.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Send C++ logs and traces to Bronto via OpenTelemetry

> Instrument C++ applications with the OpenTelemetry C++ SDK to send logs and traces to Bronto over OTLP/HTTP via a local Collector or direct export.

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.

<Tip>
  If you don't have a Collector and want to export directly from your application to Bronto, see [Direct export to Bronto](#direct-export-to-bronto) at the bottom of this page.
</Tip>

## Prerequisites

* C++14 or later
* CMake 3.12 or later
* vcpkg or Conan (recommended), or a manual build of `opentelemetry-cpp`
* A running OTel Collector configured to forward logs and traces to Bronto — see [Connect Open Telemetry to Bronto](/agent-setup/open-telemetry)
* [OpenTelemetry C++ SDK documentation](https://opentelemetry.io/docs/languages/cpp/)

## Install dependencies

<CodeGroup>
  ```bash vcpkg theme={"dark"}
  vcpkg install opentelemetry-cpp
  ```

  ```bash Conan theme={"dark"}
  conan install opentelemetry-cpp/1.17.0@ --build=missing
  ```
</CodeGroup>

Link the required components in your `CMakeLists.txt`:

```cmake CMakeLists.txt theme={"dark"}
find_package(opentelemetry-cpp CONFIG REQUIRED)

target_link_libraries(myapp PRIVATE
    opentelemetry-cpp::logs
    opentelemetry-cpp::otlp_http_log_record_exporter
    opentelemetry-cpp::resources
)
```

## Configure the log bridge

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.

```cpp otel_logging.cpp theme={"dark"}
#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.

## Configure the OTLP exporter

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

```cpp main.cpp theme={"dark"}
#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;
}
```

<Note>
  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.
</Note>

## Verify delivery

After running your application, check both signals in Bronto:

* **Logs**: open the [Search](https://app.bronto.io/search) page and filter by the dataset name you set in `service.name` — your log records should appear within a few seconds.
* **Traces**: open the Explore Traces page and filter by the same `service.name` — your spans 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](/agent-setup/open-telemetry).
* `InitLogging()` is called before the first log emission.
* `ForceFlush()` is called before process exit to flush buffered records.

## Traces

<Tip>
  Auto-instrumentation for C++ is more limited than other languages. Check the [OpenTelemetry C++ registry](https://opentelemetry.io/ecosystem/registry/?language=cpp\&component=instrumentation) for available community instrumentation packages for HTTP servers, gRPC, and database clients.
</Tip>

### Link tracing components

Add the tracing libraries to your `CMakeLists.txt` alongside the logging ones:

```cmake CMakeLists.txt theme={"dark"}
target_link_libraries(myapp PRIVATE
    opentelemetry-cpp::logs
    opentelemetry-cpp::otlp_http_log_record_exporter
    opentelemetry-cpp::trace
    opentelemetry-cpp::otlp_http_exporter
)
```

### Configure the tracer provider

```cpp otel_logging.cpp (extended) theme={"dark"}
#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

```cpp theme={"dark"}
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();
```

## GenAI semantic conventions

If your application calls an LLM, OpenTelemetry defines [GenAI semantic conventions](https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/gen-ai-spans.md). There is no first-party GenAI auto-instrumentation package for C++ yet — set the attributes yourself around each LLM call:

<Note>
  This page was verified on **July 17, 2026**. GenAI libraries and semantic conventions are evolving rapidly, so package configuration and emitted attributes can change between releases. Keep your instrumentation current and verify the fields emitted by the version you deploy.
</Note>

```cpp theme={"dark"}
auto tracer = trace_api::Provider::GetTracerProvider()->GetTracer("my-service");

auto span = tracer->StartSpan("chat gpt-4o-mini");
auto scope = tracer->WithActiveSpan(span);

span->SetAttribute("gen_ai.provider.name", "openai");
span->SetAttribute("gen_ai.request.model", "gpt-4o-mini");
span->SetAttribute("gen_ai.usage.input_tokens", 33);
span->SetAttribute("gen_ai.usage.output_tokens", 74);

span->End();
```

<Note>
  Message content (`gen_ai.input.messages` / `gen_ai.output.messages`) is opt-in by convention in languages with auto-instrumentation, off by default. Since you're setting attributes by hand here, apply the same discipline — gate prompt/response content behind your own config flag rather than always sending it.
</Note>

See [LLM Observability](/ai-features/llm-observability) for the full recommended attribute set and how to search and aggregate GenAI fields in Bronto.

## 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:

```cpp theme={"dark"}
// 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](/Account-Management/API-Keys) for how to create a key with ingestion permissions. No other changes to the rest of the setup are required.
