Skip to content

Commit 85505ee

Browse files
committed
feat: Add opentelemetry declarative configuration.
1 parent c955356 commit 85505ee

File tree

23 files changed

+1872
-0
lines changed

23 files changed

+1872
-0
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
[workspace]
22
members = [
33
"opentelemetry-aws",
4+
"opentelemetry-config",
5+
"opentelemetry-config-stdout",
46
"opentelemetry-contrib",
57
"opentelemetry-datadog",
68
"opentelemetry-etw-logs",
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Changelog
2+
3+
## vNext
4+
5+
## v0.1.0
6+
7+
### Added
8+
9+
- Initial declarative configuration for stdout (console)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Code owners file.
2+
# This file controls who is tagged for review for any given pull request.
3+
4+
# For anything not explicitly taken by someone else:
5+
* @open-telemetry/rust-approvers
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "opentelemetry-config-stdout"
3+
version = "0.1.0"
4+
description = "Declarative configuration for OpenTelemetry SDK using console (stdout) configuration"
5+
license = "Apache-2.0"
6+
edition = "2021"
7+
rust-version = "1.75.0"
8+
9+
[dependencies]
10+
opentelemetry-config = { path = "../opentelemetry-config" }
11+
opentelemetry_sdk = { version = "0.31.0" }
12+
opentelemetry-stdout = { version = "0.31.0" }
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# OpenTelemetry Declarative Configuration for stdout (console)
2+
3+
![OpenTelemetry — An observability framework for cloud-native software.][splash]
4+
5+
[splash]: https://raw.githubusercontent.com/open-telemetry/opentelemetry-rust/main/assets/logo-text.png
6+
7+
Declarative configuration for applications instrumented with [`OpenTelemetry`].
8+
9+
[`OpenTelemetry`]: https://crates.io/crates/opentelemetry
10+
11+
## Overview
12+
13+
This crate provides a declarative configuration extension for OpenTelemetry that enables stdout (console) metric exports. It integrates with the `opentelemetry-config` crate to allow YAML-based configuration of the console exporter.
14+
15+
### Features
16+
17+
- Console/stdout metrics exporter configuration via YAML
18+
- Support for both Delta and Cumulative temporality
19+
- Integration with OpenTelemetry declarative configuration
20+
- Simple registration API for the configurator
21+
22+
## Installation
23+
24+
Add this to your `Cargo.toml`:
25+
26+
```toml
27+
[dependencies]
28+
opentelemetry-config-stdout = "0.1.0"
29+
```
30+
31+
## Quick Start
32+
33+
### 1. Create a YAML Configuration File
34+
35+
Create a file named `otel-config.yaml`:
36+
37+
```yaml
38+
metrics:
39+
readers:
40+
- periodic:
41+
exporter:
42+
console:
43+
temporality: delta
44+
45+
resource:
46+
service.name: "my-service"
47+
service.version: "1.0.0"
48+
```
49+
50+
### 2. Load and Apply Configuration
51+
52+
```rust
53+
use opentelemetry_config::{ConfiguratorManager, TelemetryConfigurator};
54+
use opentelemetry_config_stdout::ConsolePeriodicExporterConfigurator;
55+
56+
fn main() -> Result<(), Box<dyn std::error::Error>> {
57+
// Create configurator manager and register stdout exporter
58+
let mut configurator_manager = ConfiguratorManager::new();
59+
ConsolePeriodicExporterConfigurator::register_into(&mut configurator_manager);
60+
61+
// Load configuration from YAML file
62+
let telemetry_configurator = TelemetryConfigurator::new();
63+
let providers = telemetry_configurator
64+
.configure_from_yaml_file(&configurator_manager, "otel-config.yaml")?;
65+
66+
// Use the configured providers
67+
if let Some(meter_provider) = providers.meter_provider() {
68+
// Your application code here
69+
}
70+
71+
// Shutdown all providers
72+
providers.shutdown()?;
73+
Ok(())
74+
}
75+
```
76+
77+
## Examples
78+
79+
### Console Exporter Example
80+
81+
See the [examples/console](examples/console) directory for a complete working example that demonstrates:
82+
83+
- Setting up a console exporter configurator
84+
- Loading configuration from a YAML file
85+
- Configuring a meter provider
86+
- Proper shutdown handling
87+
88+
To run the example:
89+
90+
```bash
91+
cd examples/console
92+
cargo run -- --file ../metrics_console.yaml
93+
```
94+
95+
## Configuration Schema
96+
97+
### Metrics Configuration
98+
99+
```yaml
100+
metrics:
101+
readers:
102+
- periodic:
103+
exporter:
104+
console:
105+
temporality: delta # or cumulative
106+
```
107+
108+
## Contributing
109+
110+
Contributions are welcome! Please feel free to submit issues or pull requests.
111+
112+
## License
113+
114+
This project is licensed under the Apache-2.0 license.
115+
116+
## Release Notes
117+
118+
You can find the release notes (changelog) [here](https://github.com/open-telemetry/opentelemetry-rust-contrib/tree/main/opentelemetry-config-stdout/CHANGELOG.md).
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "opentelemetry-config-console-example"
3+
version = "0.1.0"
4+
description = "Declarative configuration for OpenTelemetry SDK example using console configuration"
5+
license = "Apache-2.0"
6+
edition = "2021"
7+
rust-version = "1.75.0"
8+
9+
[workspace]
10+
11+
[dependencies]
12+
opentelemetry-config-stdout = { path = "../../" }
13+
opentelemetry-config = { path = " ../../../../../opentelemetry-config" }
14+
opentelemetry_sdk = { version = "0.31.0" }
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//! # Example OpenTelemetry Config Console
2+
//!
3+
//! This example demonstrates how to configure OpenTelemetry Metrics
4+
//! using the OpenTelemetry Config crate with a Console Exporter.
5+
6+
use opentelemetry_config::{configurators::TelemetryConfigurator, ConfiguratorManager};
7+
use opentelemetry_config_stdout::ConsolePeriodicExporterConfigurator;
8+
9+
use std::env;
10+
11+
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
12+
let args: Vec<String> = env::args().collect();
13+
14+
if args.len() == 1 || (args.len() > 1 && args[1] == "--help") {
15+
println!("Usage: cargo run -- --file ../metrics_console.yaml");
16+
println!("This example demonstrates how to configure OpenTelemetry Metrics using the OpenTelemetry Config crate with a Console Exporter.");
17+
return Ok(());
18+
}
19+
if args.len() < 3 || args[1] != "--file" {
20+
println!("Error: Configuration file path not provided.");
21+
println!("Usage: cargo run -- --file ../metrics_console.yaml");
22+
return Ok(());
23+
}
24+
let config_file = &args[2];
25+
26+
// Setup configurator manager with console exporter configurator
27+
let mut configurator_manager = ConfiguratorManager::new();
28+
ConsolePeriodicExporterConfigurator::register_into(&mut configurator_manager);
29+
30+
let telemetry_configurator = TelemetryConfigurator::new();
31+
let providers = telemetry_configurator
32+
.configure_from_yaml_file(&configurator_manager, config_file)?;
33+
34+
println!("Metrics configured with Console Exporter successfully.");
35+
36+
println!(
37+
"Meter provider configured: {}",
38+
providers.meter_provider().is_some()
39+
);
40+
println!(
41+
"Logs provider configured: {}",
42+
providers.logs_provider().is_some()
43+
);
44+
println!(
45+
"Traces provider configured: {}",
46+
providers.traces_provider().is_some()
47+
);
48+
49+
println!("Shutting down telemetry providers...");
50+
providers.shutdown()?;
51+
Ok(())
52+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
metrics:
2+
readers:
3+
- periodic:
4+
exporter:
5+
console:
6+
temporality: delta
7+
resource:
8+
service.name: "test-service"
9+
service.version: "1.0.0"
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
//! # OpenTelemetry Dynamic Configurator module for Stdout (Console) exporter
2+
//!
3+
//! This module provides a configurator for OpenTelemetry Metrics
4+
//! that enables exporting metrics to the console (stdout) using
5+
//! the OpenTelemetry Config crate.
6+
7+
use opentelemetry_config::{
8+
model::metrics::reader::PeriodicExporterConsole, MetricsReaderPeriodicExporterConfigurator,
9+
};
10+
use opentelemetry_sdk::metrics::MeterProviderBuilder;
11+
12+
#[derive(Clone)]
13+
pub struct ConsolePeriodicExporterConfigurator {}
14+
15+
impl ConsolePeriodicExporterConfigurator {
16+
pub fn new() -> Self {
17+
Self {}
18+
}
19+
20+
pub fn register_into(configurators_manager: &mut opentelemetry_config::ConfiguratorManager) {
21+
let configurator = ConsolePeriodicExporterConfigurator::new();
22+
configurators_manager
23+
.metrics_mut()
24+
.register_periodic_exporter_configurator::<PeriodicExporterConsole>(Box::new(
25+
configurator.clone(),
26+
));
27+
// TODO: Add logs and traces configurator registration.
28+
}
29+
}
30+
31+
impl MetricsReaderPeriodicExporterConfigurator for ConsolePeriodicExporterConfigurator {
32+
fn configure(
33+
&self,
34+
mut meter_provider_builder: MeterProviderBuilder,
35+
config: &dyn std::any::Any,
36+
) -> MeterProviderBuilder {
37+
let mut exporter_builder = opentelemetry_stdout::MetricExporter::builder();
38+
39+
let config = config
40+
.downcast_ref::<PeriodicExporterConsole>()
41+
.expect("Invalid config type. Expected PeriodicExporterConsole.");
42+
43+
if let Some(temporality) = &config.temporality {
44+
match temporality {
45+
opentelemetry_config::model::metrics::reader::Temporality::Delta => {
46+
exporter_builder = exporter_builder
47+
.with_temporality(opentelemetry_sdk::metrics::Temporality::Delta);
48+
}
49+
opentelemetry_config::model::metrics::reader::Temporality::Cumulative => {
50+
exporter_builder = exporter_builder
51+
.with_temporality(opentelemetry_sdk::metrics::Temporality::Cumulative);
52+
}
53+
}
54+
}
55+
56+
let exporter = exporter_builder.build();
57+
meter_provider_builder = meter_provider_builder.with_periodic_exporter(exporter);
58+
meter_provider_builder
59+
}
60+
61+
fn as_any(&self) -> &dyn std::any::Any {
62+
self
63+
}
64+
}
65+
66+
impl Default for ConsolePeriodicExporterConfigurator {
67+
fn default() -> Self {
68+
Self::new()
69+
}
70+
}
71+
72+
#[cfg(test)]
73+
mod tests {
74+
use super::*;
75+
76+
#[test]
77+
fn test_console_configurator_registration() {
78+
// Arrange
79+
let mut configurator_manager = opentelemetry_config::ConfiguratorManager::new();
80+
81+
// Act
82+
ConsolePeriodicExporterConfigurator::register_into(&mut configurator_manager);
83+
84+
let configurators_option = configurator_manager
85+
.metrics()
86+
.readers_periodic_exporter::<PeriodicExporterConsole>();
87+
88+
// Assert
89+
assert!(configurators_option.is_some());
90+
}
91+
92+
#[test]
93+
fn test_console_configurator_configure_temporality_minimal() {
94+
// Arrange
95+
let configurator = ConsolePeriodicExporterConfigurator::new();
96+
let meter_provider_builder = opentelemetry_sdk::metrics::SdkMeterProvider::builder();
97+
98+
let config = PeriodicExporterConsole { temporality: None };
99+
100+
// Act
101+
let configured_builder = configurator.configure(meter_provider_builder, &config);
102+
103+
// Assert
104+
// Since the MeterProviderBuilder does not expose its internal state,
105+
// we will just ensure that the returned builder is not the same as the original.
106+
assert!(!std::ptr::eq(
107+
&configured_builder,
108+
&opentelemetry_sdk::metrics::SdkMeterProvider::builder()
109+
));
110+
}
111+
112+
#[test]
113+
fn test_console_configurator_configure_temporality_delta() {
114+
// Arrange
115+
let configurator = ConsolePeriodicExporterConfigurator::new();
116+
let meter_provider_builder = opentelemetry_sdk::metrics::SdkMeterProvider::builder();
117+
118+
let config = PeriodicExporterConsole {
119+
temporality: Some(opentelemetry_config::model::metrics::reader::Temporality::Delta),
120+
};
121+
122+
// Act
123+
let configured_builder = configurator.configure(meter_provider_builder, &config);
124+
125+
// Assert
126+
// Since the MeterProviderBuilder does not expose its internal state,
127+
// we will just ensure that the returned builder is not the same as the original.
128+
assert!(!std::ptr::eq(
129+
&configured_builder,
130+
&opentelemetry_sdk::metrics::SdkMeterProvider::builder()
131+
));
132+
}
133+
134+
#[test]
135+
fn test_console_configurator_configure_temporality_cumulative() {
136+
// Arrange
137+
let configurator = ConsolePeriodicExporterConfigurator::new();
138+
let meter_provider_builder = opentelemetry_sdk::metrics::SdkMeterProvider::builder();
139+
140+
let config = PeriodicExporterConsole {
141+
temporality: Some(
142+
opentelemetry_config::model::metrics::reader::Temporality::Cumulative,
143+
),
144+
};
145+
146+
// Act
147+
let configured_builder = configurator.configure(meter_provider_builder, &config);
148+
149+
// Assert
150+
// Since the MeterProviderBuilder does not expose its internal state,
151+
// we will just ensure that the returned builder is not the same as the original.
152+
assert!(!std::ptr::eq(
153+
&configured_builder,
154+
&opentelemetry_sdk::metrics::SdkMeterProvider::builder()
155+
));
156+
}
157+
}

0 commit comments

Comments
 (0)