Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions opentelemetry-sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
- Added `Resource::get_ref(&self, key: &Key) -> Option<&Value>` to allow retrieving a reference to a resource value without cloning.
- **Breaking** Removed the following public hidden methods from the `SdkTracer` [#3227][3227]:
- `id_generator`, `should_sample`
- **Breaking** Removed `Default` and `Clone` implementation from `InMemoryMetricExporter`.
- **Breaking** `InMemoryMetricExporterBuilder` requires mandatory `metrics` field to be set via
`.with_metrics` method.
Comment on lines +8 to +10
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should phrase this so the user knows what the impact on them is. Something like this perhaps?:

Suggested change
- **Breaking** Removed `Default` and `Clone` implementation from `InMemoryMetricExporter`.
- **Breaking** `InMemoryMetricExporterBuilder` requires mandatory `metrics` field to be set via
`.with_metrics` method.
- **Breaking** Redesigned `InMemoryMetricExporter` to accept user-provided storage instead of managing it internally.
Users now pass an `Arc<Mutex<Vec<ResourceMetrics>>>` via `InMemoryMetricExporterBuilder::with_metrics()` and access
exported data directly through that reference. This removes the need to clone the exporter and aligns with how
production exporters receive their sink. `Default` and `Clone` implementations have been removed.
[#3257](https://github.com/open-telemetry/opentelemetry-rust/pull/3257)


[3227]: https://github.com/open-telemetry/opentelemetry-rust/pull/3227

Expand Down
95 changes: 41 additions & 54 deletions opentelemetry-sdk/src/metrics/in_memory_exporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@ use crate::metrics::data::{
};
use crate::metrics::exporter::PushMetricExporter;
use crate::metrics::Temporality;
use crate::InMemoryExporterError;
use std::collections::VecDeque;
use std::fmt;
use std::sync::{Arc, Mutex};
use std::time::Duration;

use super::data::{AggregatedMetrics, Metric, ScopeMetrics};

// Not a user-facing type, just a type alias for clarity within this module.
type InMemoryMetrics = Vec<ResourceMetrics>;

/// An in-memory metrics exporter that stores metrics data in memory.
///
/// This exporter is useful for testing and debugging purposes. It stores
/// metric data in a `VecDeque<ResourceMetrics>`. Metrics can be retrieved
/// using the `get_finished_metrics` method.
/// metric data in a user-provided `Vec<ResourceMetrics>`, from which the
/// exported data can be retrieved as well.
Comment on lines 18 to +20
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should be more explicit here. Something like this perhaps?

Suggested change
/// This exporter is useful for testing and debugging purposes. It stores
/// metric data in a `VecDeque<ResourceMetrics>`. Metrics can be retrieved
/// using the `get_finished_metrics` method.
/// metric data in a user-provided `Vec<ResourceMetrics>`, from which the
/// exported data can be retrieved as well.
/// This exporter is useful for testing and debugging purposes. It stores
/// metric data in a user-provided `Arc<Mutex<Vec<ResourceMetrics>>>`. Users
/// retain a clone of this `Arc` and can access the exported metrics directly
/// by locking the mutex.

///
/// # Panics
///
Expand All @@ -27,6 +28,7 @@ use super::data::{AggregatedMetrics, Metric, ScopeMetrics};
/// # Example
///
/// ```
///# use std::sync::{Arc, Mutex};
///# use opentelemetry_sdk::metrics;
///# use opentelemetry::{KeyValue};
///# use opentelemetry::metrics::MeterProvider;
Expand All @@ -35,12 +37,15 @@ use super::data::{AggregatedMetrics, Metric, ScopeMetrics};
///
///# #[tokio::main]
///# async fn main() {
/// // Create an InMemoryMetricExporter
/// let exporter = InMemoryMetricExporter::default();
/// // Create an InMemoryMetricExporter
/// let metrics = Arc::new(Mutex::new(Vec::new()));
/// let exporter = InMemoryMetricExporter::builder()
/// .with_metrics(metrics.clone())
/// .build();
///
/// // Create a MeterProvider and register the exporter
/// let meter_provider = metrics::SdkMeterProvider::builder()
/// .with_reader(PeriodicReader::builder(exporter.clone()).build())
/// .with_reader(PeriodicReader::builder(exporter).build())
/// .build();
///
/// // Create and record metrics using the MeterProvider
Expand All @@ -50,26 +55,21 @@ use super::data::{AggregatedMetrics, Metric, ScopeMetrics};
///
/// meter_provider.force_flush().unwrap();
///
/// // Retrieve the finished metrics from the exporter
/// let finished_metrics = exporter.get_finished_metrics().unwrap();
///
/// // Print the finished metrics
/// for resource_metrics in finished_metrics {
/// for resource_metrics in metrics.lock().unwrap().iter() {
/// println!("{:?}", resource_metrics);
/// }
///# }
/// ```
pub struct InMemoryMetricExporter {
metrics: Arc<Mutex<VecDeque<ResourceMetrics>>>,
metrics: Arc<Mutex<InMemoryMetrics>>,
temporality: Temporality,
}

impl Clone for InMemoryMetricExporter {
fn clone(&self) -> Self {
InMemoryMetricExporter {
metrics: self.metrics.clone(),
temporality: self.temporality,
}
impl InMemoryMetricExporter {
/// Creates a new instance of the [`InMemoryMetricExporterBuilder`].
pub fn builder() -> InMemoryMetricExporterBuilder {
InMemoryMetricExporterBuilder::new()
}
}

Expand All @@ -79,22 +79,20 @@ impl fmt::Debug for InMemoryMetricExporter {
}
}

impl Default for InMemoryMetricExporter {
fn default() -> Self {
InMemoryMetricExporterBuilder::new().build()
}
}

/// Builder for [`InMemoryMetricExporter`].
/// # Example
///
/// ```
/// # use opentelemetry_sdk::metrics::{InMemoryMetricExporter, InMemoryMetricExporterBuilder};
///# use opentelemetry_sdk::metrics::{InMemoryMetricExporter, InMemoryMetricExporterBuilder};
///# use std::sync::{Arc, Mutex};
///
/// let exporter = InMemoryMetricExporterBuilder::new().build();
/// let metrics = Arc::new(Mutex::new(Vec::new()));
/// let exporter = InMemoryMetricExporterBuilder::new()
/// .with_metrics(metrics.clone()).build();
/// ```
pub struct InMemoryMetricExporterBuilder {
temporality: Option<Temporality>,
metrics: Option<Arc<Mutex<InMemoryMetrics>>>,
}

impl fmt::Debug for InMemoryMetricExporterBuilder {
Expand All @@ -112,7 +110,10 @@ impl Default for InMemoryMetricExporterBuilder {
impl InMemoryMetricExporterBuilder {
/// Creates a new instance of the `InMemoryMetricExporterBuilder`.
pub fn new() -> Self {
Self { temporality: None }
Self {
temporality: None,
metrics: None,
}
}

/// Set the [Temporality] of the exporter.
Expand All @@ -121,48 +122,34 @@ impl InMemoryMetricExporterBuilder {
self
}

/// Set the internal collection to store the metrics.
pub fn with_metrics(mut self, metrics: Arc<Mutex<InMemoryMetrics>>) -> Self {
self.metrics = Some(metrics);
self
}

/// Creates a new instance of the `InMemoryMetricExporter`.
///
pub fn build(self) -> InMemoryMetricExporter {
InMemoryMetricExporter {
metrics: Arc::new(Mutex::new(VecDeque::new())),
metrics: self.metrics.expect("Metrics must be provided"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we return a Result here rather than paninicng?

temporality: self.temporality.unwrap_or_default(),
}
}
}

impl InMemoryMetricExporter {
/// Returns the finished metrics as a vector of `ResourceMetrics`.
///
/// # Errors
///
/// Returns a `MetricError` if the internal lock cannot be acquired.
///
/// # Example
///
/// ```
/// # use opentelemetry_sdk::metrics::InMemoryMetricExporter;
///
/// let exporter = InMemoryMetricExporter::default();
/// let finished_metrics = exporter.get_finished_metrics().unwrap();
/// ```
pub fn get_finished_metrics(&self) -> Result<Vec<ResourceMetrics>, InMemoryExporterError> {
let metrics = self
.metrics
.lock()
.map(|metrics_guard| metrics_guard.iter().map(Self::clone_metrics).collect())
.map_err(InMemoryExporterError::from)?;
Ok(metrics)
}

/// Clears the internal storage of finished metrics.
///
/// # Example
///
/// ```
/// # use opentelemetry_sdk::metrics::InMemoryMetricExporter;
/// use opentelemetry_sdk::metrics::InMemoryMetricExporter;
/// use std::sync::{Arc, Mutex};
///
/// let exporter = InMemoryMetricExporter::default();
/// let metrics = Arc::new(Mutex::new(Vec::new()));
/// let exporter = InMemoryMetricExporter::builder()
/// .with_metrics(metrics.clone()).build();
/// exporter.reset();
/// ```
pub fn reset(&self) {
Expand Down Expand Up @@ -241,7 +228,7 @@ impl PushMetricExporter for InMemoryMetricExporter {
self.metrics
.lock()
.map(|mut metrics_guard| {
metrics_guard.push_back(InMemoryMetricExporter::clone_metrics(metrics))
metrics_guard.push(InMemoryMetricExporter::clone_metrics(metrics))
})
.map_err(|_| OTelSdkError::InternalFailure("Failed to lock metrics".to_string()))
}
Expand Down
Loading