-
Notifications
You must be signed in to change notification settings - Fork 0
Support multiple otlp providers #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -763,10 +763,23 @@ pub struct UsageConfig { | |
| #[serde(default = "default_true")] | ||
| pub database: bool, | ||
|
|
||
| /// OTLP exporter for usage data. | ||
| /// Sends usage records as OTLP log records to any OpenTelemetry-compatible backend. | ||
| /// OTLP exporters for usage data. | ||
| /// Sends usage records as OTLP log records to one or more OpenTelemetry-compatible backends. | ||
| /// Each entry creates an independent exporter, allowing fan-out to multiple destinations. | ||
| /// | ||
| /// ```toml | ||
| /// [[observability.usage.otlp]] | ||
| /// name = "grafana" | ||
| /// endpoint = "https://otlp-gateway.grafana.net/otlp" | ||
| /// headers = { Authorization = "Basic xxx" } | ||
| /// | ||
| /// [[observability.usage.otlp]] | ||
| /// name = "datadog" | ||
| /// endpoint = "https://otel.datadoghq.com" | ||
| /// headers = { "DD-API-KEY" = "xxx" } | ||
| /// ``` | ||
| #[serde(default)] | ||
| pub otlp: Option<UsageOtlpConfig>, | ||
| pub otlp: Vec<UsageOtlpConfig>, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Changing [observability.usage.otlp]
enabled = true
endpoint = "http://localhost:4317"will now fail to deserialize at startup with a type mismatch, because a TOML inline table ( [[observability.usage.otlp]]
enabled = true
endpoint = "http://localhost:4317"Since Prompt To Fix With AIThis is a comment left during a code review.
Path: src/config/observability.rs
Line: 782
Comment:
**Breaking config change — no migration path**
Changing `otlp` from `Option<UsageOtlpConfig>` to `Vec<UsageOtlpConfig>` alters the expected TOML syntax. Any existing deployment that has:
```toml
[observability.usage.otlp]
enabled = true
endpoint = "http://localhost:4317"
```
will now fail to deserialize at startup with a type mismatch, because a TOML inline table (`[section]`) cannot be coerced into a `Vec` — the new syntax requires:
```toml
[[observability.usage.otlp]]
enabled = true
endpoint = "http://localhost:4317"
```
Since `serde` will return an error rather than silently ignoring the old key, this is a hard startup failure for anyone who already has OTLP configured. Consider adding a note in the changelog/PR description, and ideally a custom deserializer or a serde `alias` / migration note so operators aren't surprised.
How can I resolve this? If you propose a fix, please make it concise. |
||
|
|
||
| /// Buffer configuration for batched writes. | ||
| #[serde(default)] | ||
|
|
@@ -777,7 +790,7 @@ impl Default for UsageConfig { | |
| fn default() -> Self { | ||
| Self { | ||
| database: true, | ||
| otlp: None, | ||
| otlp: Vec::new(), | ||
| buffer: UsageBufferConfig::default(), | ||
| } | ||
| } | ||
|
|
@@ -792,6 +805,11 @@ pub struct UsageOtlpConfig { | |
| #[serde(default = "default_true")] | ||
| pub enabled: bool, | ||
|
|
||
| /// Human-readable name for this endpoint (used in logs/metrics). | ||
| /// Defaults to the endpoint URL if not specified. | ||
| #[serde(default)] | ||
| pub name: Option<String>, | ||
|
|
||
| /// OTLP endpoint URL. | ||
| /// If not specified, uses the tracing OTLP endpoint. | ||
| #[serde(default)] | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Placing a
usestatement inside the body of a function (rather than at the top of the module or at the start of the enclosing block) is unusual in Rust and can be surprising to readers. The trait import is needed so thatotlp_sink.name()resolves, but a cleaner place would be at the top of the module alongside the other#[cfg(feature = "otlp")]imports (e.g. near wherecrate::config::UsageOtlpConfigandTracingConfigare imported inusage_sink.rs), or at the very start of theif let Some(buffer)block.Alternatively, expose
name()as an inherent method onOtlpSink(in addition to the trait impl) so no trait import is needed at the call site.Prompt To Fix With AI
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!