Skip to content

Commit ab1458e

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

File tree

2 files changed

+217
-0
lines changed

2 files changed

+217
-0
lines changed

docs/design/architecture.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# OpenTelemetry Rust – Architecture Overview
2+
3+
## 1 Purpose
4+
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.
5+
6+
> Reference: the [OpenTelemetry Specification](https://opentelemetry.io/docs/specs/otel/) and its [GitHub repository](https://github.com/open-telemetry/opentelemetry-specification/)
7+
8+
## 2 Layering
9+
```mermaid
10+
graph TD
11+
A[Application code] --> B[opentelemetry API]
12+
B --> C[opentelemetry-sdk core]
13+
C --> D[Processors / Readers]
14+
D --> E[Exporters]
15+
E -->|"protocols (OTLP, Jaeger, Zipkin, Prometheus, stdout)"| F[Back-end / Collector]
16+
```
17+
Key points:
18+
1. The **API** crates are dependency-free facades that application code instruments against.
19+
2. The **SDK** crate supplies concrete implementations, batching, aggregation, and lifecycle management.
20+
**Processors / readers** live inside the SDK and adapt buffering, temporality, and semantics.
21+
3. **Exporters** translate OTel data into wire formats (OTLP, Prometheus, etc.) and handle transport.
22+
23+
## 3 Cross-cutting Components
24+
* **Resource & Attributes** – common schema for describing process, host, and service; see the [resource spec](https://opentelemetry.io/docs/specs/otel/resource/).
25+
* **Context & Propagation** – context management and carrier injection; see the [context propagation spec](https://opentelemetry.io/docs/specs/otel/context/).
26+
* **Runtime model** – public APIs are runtime-agnostic; heavy I/O lives behind optional Tokio-based exporters; see the [SDK design guidelines](https://opentelemetry.io/docs/specs/otel/).
27+
* **Error taxonomy** – see [ADR 001 Error Handling](../adr/001_error_handling.md).
28+
29+
## Detailed Design
30+
31+
### Signals
32+
33+
* **Traces**[design doc](./traces.md) — trees of _spans_ capturing distributed work.
34+
* **Metrics**[design doc](./metrics.md) — numerical time-series data.
35+
* **Logs**[design doc](./logs.md) — timestamped events bridged from existing logging frameworks (`log`, `tracing`).
36+
37+
### Everything Else
38+
39+
* **OTLP**[design doc](./otlp.md) — the OTLP exporter subsystem.
40+
41+
## 5 Extensibility Hooks
42+
| Layer | Customisation points |
43+
|-------|---------------------|
44+
| API | Provide alternative `TracerProvider`, `MeterProvider`, `LoggerProvider` |
45+
| SDK | Plug-in **samplers**, **metric readers**, **log processors** |
46+
| Exporters | Support additional wire-protocols |

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