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

# C++ SDK

> Track events and logs from C++ applications with sub-microsecond overhead and zero dependencies.

The C++ SDK sends events and structured logs from your services to Tell. It's a server SDK — you create a client, pass a user ID on every call, and a background worker thread handles batching and delivery over TCP.

Your thread does about 85 ns of work per call and never touches the network. The SDK has zero external dependencies beyond C++17 and pthreads.

## Installation

Add the SDK as a CMake subdirectory:

```cmake theme={null}
add_subdirectory(tell-cpp)
target_link_libraries(your_target PRIVATE tell)
```

Or build from source:

```bash theme={null}
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
```

Requires a C++17 compiler (GCC 7+, Clang 5+, MSVC 2017+) and CMake 3.14+.

## Quick start

```cpp theme={null}
#include "tell/tell.hpp"

int main() {
    auto client = tell::Tell::create(
        tell::TellConfig::production("a1b2c3d4e5f60718293a4b5c6d7e8f90")
    );

    client->track("user_123", "Page Viewed",
        tell::Props().add("url", "/home").add("referrer", "google"));

    client->close();
}
```

The client connects to `collect.tell.rs:50000`, batches up to 100 events, and flushes every 10 seconds or when you call `close()`.

## Configuration

Use one of the two presets, or build a custom config.

```cpp theme={null}
// Production — collect.tell.rs:50000, batch 100, flush 10s
auto config = tell::TellConfig::production("a1b2c3d4e5f60718293a4b5c6d7e8f90");

// Development — localhost:50000, batch 10, flush 2s
auto config = tell::TellConfig::development("a1b2c3d4e5f60718293a4b5c6d7e8f90");
```

For custom settings, use the builder:

```cpp theme={null}
auto config = tell::TellConfig::builder("a1b2c3d4e5f60718293a4b5c6d7e8f90")
    .endpoint("collect.internal:50000")
    .batch_size(200)
    .flush_interval(std::chrono::milliseconds(5000))
    .max_retries(5)
    .close_timeout(std::chrono::milliseconds(10000))
    .network_timeout(std::chrono::milliseconds(15000))
    .on_error([](const tell::TellError& e) {
        std::cerr << "[Tell] " << e.what() << std::endl;
    })
    .build();
```

The API key must be a 32-character hex string. The SDK validates it at build time and throws `TellError` if it's invalid.

## Tracking events

Every tracking method takes `user_id` as its first parameter. Calls never block or throw — errors go to the optional `on_error` callback.

```cpp theme={null}
// Track a custom event
client->track("user_123", "Feature Used",
    tell::Props().add("feature", "dark_mode").add("enabled", true));

// Identify a user
client->identify("user_123",
    tell::Props().add("name", "Jane").add("plan", "pro"));

// Associate user with a group
client->group("user_123", "company_456",
    tell::Props().add("plan", "enterprise").add("seats", 50));

// Track revenue
client->revenue("user_123", 49.99, "USD", "order_789",
    tell::Props().add("product", "annual_plan"));

// Link two user identities
client->alias("anon_abc", "user_123");
```

Properties are always optional — you can omit them entirely:

```cpp theme={null}
client->track("user_123", "Click");
```

### Standard event names

The SDK provides typed constants for common events:

```cpp theme={null}
client->track("user_123", tell::Events::PAGE_VIEWED,
    tell::Props().add("url", "/pricing"));
client->track("user_123", tell::Events::USER_SIGNED_UP,
    tell::Props().add("source", "organic"));
client->track("user_123", tell::Events::ORDER_COMPLETED,
    tell::Props().add("total", 99.00));
```

Constants cover user lifecycle, revenue, subscriptions, trials, shopping, engagement, and communication events. Custom string names are always accepted too.

### Super properties

Register properties that get merged into every `track`, `group`, and `revenue` call:

```cpp theme={null}
client->register_props(
    tell::Props().add("app_version", "2.1.0").add("env", "production"));

// This track call automatically includes app_version and env
client->track("user_123", "Click",
    tell::Props().add("button", "submit"));

// Remove a super property
client->unregister("env");
```

Event-specific properties override super properties when keys conflict.

## Structured logging

Send logs alongside events through the same pipeline. Each log has an RFC 5424 severity level.

```cpp theme={null}
client->log_error("DB connection failed", "api",
    tell::Props().add("host", "db.internal").add("retries", 3));

client->log_info("Request processed", "api",
    tell::Props().add("status", 200).add("duration_ms", 45));

client->log_warning("Rate limit approaching", "gateway");
```

The `service` parameter defaults to `"app"` if omitted. Convenience methods are available for all nine levels: `log_emergency`, `log_alert`, `log_critical`, `log_error`, `log_warning`, `log_notice`, `log_info`, `log_debug`, and `log_trace`.

You can also use the generic `log` method with an explicit level:

```cpp theme={null}
client->log(tell::LogLevel::Notice, "Deployment completed", "deploy",
    tell::Props().add("version", "3.1.0").add("commit", "abc123f"));
```

## Properties

The `Props` class builds key-value pairs with a chainable API. It writes JSON bytes directly to a buffer — no intermediate allocations.

```cpp theme={null}
tell::Props()
    .add("url", "/home")       // string
    .add("count", 42)          // int
    .add("active", true)       // bool
    .add("score", 3.14);       // double
```

Supported types: `std::string`, `const char*`, `int`, `int64_t`, `double`, and `bool`.

## Lifecycle

```cpp theme={null}
// Force-send all queued events and logs
client->flush();

// Rotate session ID
client->reset_session();

// Flush + shut down the background worker
client->close();
```

Always call `close()` before your process exits to avoid losing buffered events. It blocks up to `close_timeout` (default 5 seconds).

## Thread safety

The SDK is fully thread-safe. Internal state is protected by `std::shared_mutex`:

```cpp theme={null}
auto client = tell::Tell::create(config);

std::thread t1([&] { client->track("user_1", "Event A"); });
std::thread t2([&] { client->track("user_2", "Event B"); });
std::thread t3([&] { client->log_info("Processing", "worker"); });

t1.join(); t2.join(); t3.join();
client->close();
```

## Error handling

Construction throws on invalid config:

```cpp theme={null}
try {
    auto client = tell::Tell::create(config);
} catch (const tell::TellError& e) {
    std::cerr << e.what() << std::endl;
}
```

Tracking and logging calls (`track`, `identify`, `log_*`, etc.) never throw. Invalid input (empty user ID, event name too long) is reported through the `on_error` callback:

```cpp theme={null}
auto config = tell::TellConfig::builder("a1b2c3d4e5f60718293a4b5c6d7e8f90")
    .on_error([](const tell::TellError& e) {
        if (e.kind() == tell::ErrorKind::Validation) {
            std::cerr << "Invalid " << e.field()
                      << ": " << e.reason() << std::endl;
        }
    })
    .build();
```

## Advanced

### Configuration reference

| Parameter         | Default (production)    | Default (development) | Description                        |
| ----------------- | ----------------------- | --------------------- | ---------------------------------- |
| `endpoint`        | `collect.tell.rs:50000` | `localhost:50000`     | TCP collector address              |
| `batch_size`      | 100                     | 10                    | Events per batch before auto-flush |
| `flush_interval`  | 10s                     | 2s                    | Time between auto-flushes          |
| `max_retries`     | 3                       | 3                     | Retry attempts on send failure     |
| `close_timeout`   | 5s                      | 5s                    | Max wait on `close()`              |
| `network_timeout` | 30s                     | 30s                   | TCP connect/send timeout           |
| `on_error`        | silent                  | silent                | Error callback                     |

### Validation rules

| Field          | Rule                    |
| -------------- | ----------------------- |
| API key        | 32-character hex string |
| User ID        | Non-empty               |
| Event name     | 1–256 characters        |
| Log message    | 1–65,536 characters     |
| Group ID       | Non-empty               |
| Revenue amount | Positive number         |
| Service        | Max 256 characters      |

### Performance

Caller latency — serialize, encode, and enqueue (Apple M4 Pro):

| Operation            | Latency |
| -------------------- | ------- |
| `track` (no props)   | 85 ns   |
| `track` (with props) | 224 ns  |
| `identify`           | 272 ns  |
| `log`                | 273 ns  |
| `revenue`            | 326 ns  |

### Retry behavior

On TCP send failure, the SDK retries with exponential backoff up to `max_retries` attempts. After exhausting retries, the batch is dropped and the error reported via `on_error`.
