Skip to content

Commit 0a3cbba

Browse files
committed
chore: Add OTLP docs and general arch
1 parent 34d6d50 commit 0a3cbba

File tree

3 files changed

+1488
-0
lines changed

3 files changed

+1488
-0
lines changed

docs/design/architecture.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# OpenTelemetry Rust – Architecture Overview
2+
3+
> Last Updated: 2025-07-16
4+
5+
## 1 Purpose
6+
This document provides a stable, high-level description of how the OpenTelemetry Rust implementation is put together. Detailed per-signal design notes live in their own files; this overview ties the pieces together.
7+
8+
> Reference: the [OpenTelemetry Specification](https://opentelemetry.io/docs/specs/otel/) and its [GitHub repository](https://github.com/open-telemetry/opentelemetry-specification/)
9+
10+
## 2 Layering
11+
```mermaid
12+
graph TD
13+
A[Application code] --> B[opentelemetry API]
14+
B --> C[opentelemetry-sdk]
15+
C --> D[Processors / Readers]
16+
D --> E[Exporters]
17+
E -->|"protocols (OTLP, Zipkin, Prometheus, stdout)"| F[Back-end / Collector]
18+
```
19+
Key points:
20+
1. The **API** crate is a lightweight facade that library and application code instrument against.
21+
2. The **SDK** crate supplies concrete implementations, batching, aggregation, and lifecycle management.
22+
**Processors / readers** live inside the SDK and adapt buffering and temporality.
23+
3. **Exporters** translate OTel data into wire formats (OTLP, Prometheus, etc.) and handle transport.
24+
25+
## 3 Cross-cutting Components
26+
* **Resource** – a representation of the entity producing telemetry - see the [resource spec](https://opentelemetry.io/docs/specs/otel/resource/).
27+
* **Attributes** - a key-value pair used to annotate telemetry data - see the [common spec](https://opentelemetry.io/docs/specs/otel/common/#attributes).
28+
* **Context & Propagation** – context management and carrier injection; see the [context propagation spec](https://opentelemetry.io/docs/specs/otel/context/).
29+
* **Runtime model** — public APIs are agnostic to execution model (synchronous or asynchronous); implementations may support optional async exporters (e.g., Tokio) or blocking I/O, depending on configuration. See the [SDK design guidelines](https://opentelemetry.io/docs/specs/otel/).
30+
* **Error taxonomy** – see [ADR 001 Error Handling](../adr/001_error_handling.md).
31+
32+
## Detailed Design
33+
34+
### Signals
35+
36+
* **Traces**[design doc](./traces.md)
37+
* **Metrics**[design doc](./metrics.md)
38+
* **Logs**[design doc](./logs.md)
39+
40+
### Exporters
41+
42+
* **OTLP**[design doc](./otlp.md) — the OTLP exporter.
43+
44+
## 5 Extensibility Hooks
45+
| Layer | Customisation points |
46+
|-------|---------------------|
47+
| SDK | Provide complete alternative SDK |
48+
| SDK | Plug-in **samplers**, **metric readers**, **log processors**, and **providers** |
49+
| Exporters | Support additional wire-protocols |
50+
| Processors | Provide alternative `LogProcessor` or `SpanProcessor` |

docs/design/otlp.md

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# OpenTelemetry Rust – OTLP Architecture
2+
3+
> Last Updated: 2025-07-16
4+
5+
This document describes how the **OTLP exporters** are organised and how they integrate with the wider OpenTelemetry Rust SDK.
6+
7+
> Reference the [OTLP specification](https://opentelemetry.io/docs/specs/otlp/) and [OTLP Protocol Exporter specification](https://opentelemetry.io/docs/specs/otel/protocol/exporter/).
8+
9+
## Overview
10+
OpenTelemetry Protocol (OTLP) is the vendor-agnostic wire format used by OpenTelemetry to transport telemetry signals (traces, metrics, logs) from instrumented code to a backend (OTel Collector or compatible vendor). It supports:
11+
12+
* **Encodings**
13+
* *Protobuf* – canonical format
14+
* *JSON* (optional)
15+
* **Transports**
16+
* *gRPC* (default port `4317`)
17+
* *HTTP/1.1*, *HTTP/2* (default port `4318`, binary protobuf or JSON payloads)
18+
* **Signals**
19+
* Traces, Metrics, Logs – transported independently but share common envelope (`Resource`, `Scope` etc.)
20+
21+
## 2. Overall Structure of `opentelemetry-rust` and OTLP
22+
```mermaid
23+
graph TD
24+
A[Application code] --> B[opentelemetry-api]
25+
B --> C[opentelemetry-sdk processors]
26+
C --> D[opentelemetry-otlp exporters]
27+
D -->|gRPC / HTTP| E[OTel Collector / Backend]
28+
%% Auxiliary exporters
29+
C --> F[custom exporters]
30+
```
31+
32+
Important details:
33+
34+
* `opentelemetry-sdk` owns batching, aggregation, lifecycle; **exporters handle serialization, transport, and may implement retry policy**.
35+
* Each signal (trace/metric/log) has its own exporter type but they share common builder + transport modules.
36+
37+
---
38+
39+
## 3. Crate Layout
40+
```text
41+
src/
42+
lib.rs # Re-exports + feature flags + protocol enum
43+
span.rs | metric.rs | logs.rs # Signal-specific builders/exporters
44+
exporter/
45+
├── mod.rs # Common builder traits, env var parsing, compression
46+
├── http/ # Reqwest blocking/async clients, body encoder
47+
└── tonic/ # Tonic clients, TLS/compression helpers
48+
```
49+
### 3.1 Common Interfaces
50+
* **`HasExportConfig` / `WithExportConfig`** – traits mixed into builders to expose shared config (`endpoint`, `timeout`, `protocol`).
51+
* **Builder marker types** (`NoExporterBuilderSet`, `HttpExporterBuilderSet`, `TonicExporterBuilderSet`) enforce at compile time that exactly *one* transport is chosen.
52+
* **`SupportedTransportClient` enum** – run-time dispatch inside the exporter when sending.
53+
54+
---
55+
56+
## 4. Feature-Flag Matrix
57+
| Cargo feature | Purpose | Conditional modules |
58+
|---------------|---------|---------------------|
59+
| `trace` / `metrics` / `logs` | Enable signal exporters | `span.rs`, `metric.rs`, `logs.rs` |
60+
| `grpc-tonic` | Use `tonic` gRPC transport | `exporter/tonic` |
61+
| `http-proto` *(default)* | HTTP + protobuf body | `exporter/http` |
62+
| `http-json` | HTTP + JSON body | `exporter/http` |
63+
| `gzip-tonic` `zstd-tonic` | gRPC message compression | `exporter/tonic` |
64+
| `reqwest-client` / `reqwest-blocking-client` *(default)* | Choose async vs blocking Reqwest HTTP client |
65+
| `hyper-client` | Use Hyper HTTP transport *(requires both Reqwest features disabled)* |
66+
| TLS helpers (`tls-roots`, `tls-webpki-roots`, `reqwest-rustls`) | Supply trust roots for TLS |
67+
68+
Because **only one transport is valid per exporter**, `protocol` is currently a *hint* – unsupported values are ignored by the concrete builder.
69+
70+
### 4.1 Transport-specific Client Options
71+
72+
Exporters must function both in binaries that start no async runtime and in services already running Tokio. Consequently the crate provides **blocking** and **non-blocking** HTTP clients behind mutually-exclusive feature-flags. The blocking variant is the default to ensure out-of-the-box operability; applications that already depend on Tokio can opt into an async client to avoid spawning extra threads.
73+
74+
| Implementation | Feature flag | Blocking? | Underlying crate | Transports | Typical use case |
75+
|---------------|--------------|-----------|------------------|-----------|------------------|
76+
| Reqwest (blocking) **(default)** | `reqwest-blocking-client` | Yes | `reqwest::blocking` | HTTP/1.1 → auto-upgrades to HTTP/2 over TLS | CLI tools, synchronous binaries, or hybrid apps where introducing Tokio is undesirable. A helper thread is spawned once at builder time.
77+
| Reqwest (async) | `reqwest-client` | No (Tokio) | `reqwest` | HTTP/1.1 → auto-upgrades to HTTP/2 over TLS | Services that already run a Tokio runtime and prefer fully non-blocking exports.
78+
| Hyper | `hyper-client` | No (Tokio) | `hyper` | HTTP/1.1 → auto-upgrades to HTTP/2 over TLS | Lean dependency footprint when both Reqwest features are disabled.
79+
| Tonic (gRPC) | `grpc-tonic` | No (Tokio) | `tonic` | HTTP/2 (gRPC) | Environments standardising on gRPC or collectors exposing only port 4317.
80+
| Custom | — | Depends | user-supplied | depends on implementation | Embedding a bespoke `HttpClient` via `.with_http_client()`.
81+
82+
---
83+
84+
## 5. Runtime Flow (Trace Export)
85+
```mermaid
86+
sequenceDiagram
87+
participant App as Application code
88+
participant SDK as opentelemetry-sdk
89+
participant Exp as SpanExporter
90+
participant Tx as Transport Client (HTTP/Reqwest or gRPC/Tonic)
91+
participant Collector as OTLP Collector
92+
App->>SDK: span.start / span.end
93+
SDK-->>SDK: BatchSpanProcessor collects SpanData
94+
SDK->>Exp: export(batch)
95+
Exp->>Tx: serialize + send
96+
Tx--)Collector: OTLP request
97+
note right of Collector: 2xx = success,<br/>non-2xx → error handling
98+
Tx--)Exp: Future resolves
99+
Exp--)SDK: OTelSdkResult
100+
```
101+
Notes:
102+
103+
* Serialization happens **inside the transport client** module to keep exporters thin.
104+
* `export` is `async` for `tonic` / `reqwest` clients, surfaced via `SpanExporter` implementing `opentelemetry_sdk::trace::SpanExporter`.
105+
* Resource attributes are injected once per exporter via `set_resource()` before first export.
106+
107+
---
108+
109+
## 6. Configuration & Environment Variable Resolution
110+
`exporter::mod.rs` implements helper fns:
111+
* `default_protocol()` – chosen from compile-time defaults.
112+
* `resolve_timeout()` – precedence: signal-specific env → generic env → builder → default 10 s.
113+
* `parse_header_string()` – parses comma-separated `k=v` pairs with URL-decoding.
114+
115+
Signal builders read **signal-specific env vars** (e.g. `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`) _before_ generic ones, matching the spec.
116+
117+
---
118+
119+
## 7. Error Handling Strategy
120+
`ExporterBuildError` is a non-exhaustive enum covering:
121+
* Builder-time validation (URI parse, missing client)
122+
* Feature gating errors (compression requested w/o feature)
123+
* Runtime errors are wrapped in `OTelSdkError` from the SDK.
124+
125+
Builder fails fast, runtime exporter surfaces errors through `export()` futures so processors (or Retry logic) can decide whether to back-off, drop, or escalate.
126+
127+
---
128+
129+
## 8. Extension & Customisation Points
130+
1. **Custom headers / metadata**`.with_metadata(map)` (gRPC) or `.with_headers()` (HTTP).
131+
2. **Compression**`.with_compression(Compression::Gzip | Compression::Zstd)` behind feature-flags.
132+
3. **TLS** – via `TonicConfig` or `HttpConfig`; root-store helper features embed common CA bundles.
133+
4. **Alternate HTTP client** – inject any `HttpClient` implementation via `.with_http_client()`.
134+
5. **Protocol JSON** – switch to JSON payloads at build-time with the `http-json` feature.
135+
136+
---
137+
138+
## 9. Interactions with Other Exporters
139+
* **Prometheus exporter** (pull-based) bypasses OTLP entirely.
140+
* **Jaeger / Zipkin** exporters are alternative push paths; users typically pick *one* per signal.
141+
* **stdout exporter** may be enabled alongside OTLP in development.
142+
143+
Example mixed configuration:
144+
```rust
145+
let otlp = SpanExporter::builder().with_tonic().build()?;
146+
let jaeger = opentelemetry_jaeger::new_agent_pipeline().install_simple()?;
147+
let provider = SdkTracerProvider::builder()
148+
.with_batch_exporter(otlp)
149+
.with_simple_exporter(jaeger)
150+
.build();
151+
```
152+
153+
---
154+
155+
## 10. Key Architectural Decisions
156+
| Decision | Rationale |
157+
|----------|-----------|
158+
| *Builder pattern with marker types* | Compile-time guarantee that exactly one transport is chosen. |
159+
| *Transport-specific modules* | Keep heavy deps (`tonic`, `reqwest`) behind feature-flags to minimise compile times. |
160+
| *Separate crate* | Avoids pulling heavy deps into projects that don’t need OTLP. |
161+
| *Non-exhaustive error enums* | Allows adding new failure modes without breaking semver. |
162+
163+
---
164+
165+
### Source References
166+
* Builder traits – [`exporter/mod.rs`](../../opentelemetry-otlp/src/exporter/mod.rs)
167+
* HTTP transport – [`exporter/http/mod.rs`](../../opentelemetry-otlp/src/exporter/http/mod.rs)
168+
* gRPC transport – [`exporter/tonic/mod.rs`](../../opentelemetry-otlp/src/exporter/tonic/mod.rs)
169+
* Span exporter – [`span.rs`](../../opentelemetry-otlp/src/span.rs)
170+
* Metric exporter – [`metric.rs`](../../opentelemetry-otlp/src/metric.rs)
171+
* Logs exporter – [`logs.rs`](../../opentelemetry-otlp/src/logs.rs)

0 commit comments

Comments
 (0)