datafusion_physical_plan/metrics/
custom.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Custom metric value type.
19
20use std::{any::Any, fmt::Debug, fmt::Display, sync::Arc};
21
22/// A trait for implementing custom metric values.
23///
24/// This trait enables defining application- or operator-specific metric types
25/// that can be aggregated and displayed alongside standard metrics. These
26/// custom metrics integrate with [`MetricValue::Custom`] and support
27/// aggregation logic, introspection, and optional numeric representation.
28///
29/// # Requirements
30/// Implementations of `CustomMetricValue` must satisfy the following:
31///
32/// 1. [`Self::aggregate`]: Defines how two metric values are combined
33/// 2. [`Self::new_empty`]: Returns a new, zero-value instance for accumulation
34/// 3. [`Self::as_any`]: Enables dynamic downcasting for type-specific operations
35/// 4. [`Self::as_usize`]: Optionally maps the value to a `usize` (for sorting, display, etc.)
36/// 5. [`Self::is_eq`]: Implements comparison between two values, this isn't reusing the std
37///    PartialEq trait because this trait is used dynamically in the context of
38///    [`MetricValue::Custom`]
39///
40/// # Examples
41/// ```
42/// # use std::sync::Arc;
43/// # use std::fmt::{Debug, Display};
44/// # use std::any::Any;
45/// # use std::sync::atomic::{AtomicUsize, Ordering};
46///
47/// # use datafusion_physical_plan::metrics::CustomMetricValue;
48///
49/// #[derive(Debug, Default)]
50/// struct MyCounter {
51///     count: AtomicUsize,
52/// }
53///
54/// impl Display for MyCounter {
55///     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
56///         write!(f, "count: {}", self.count.load(Ordering::Relaxed))
57///     }
58/// }
59///
60/// impl CustomMetricValue for MyCounter {
61///     fn new_empty(&self) -> Arc<dyn CustomMetricValue> {
62///         Arc::new(Self::default())
63///     }
64///
65///     fn aggregate(&self, other: Arc<dyn CustomMetricValue>) {
66///         let other = other.as_any().downcast_ref::<Self>().unwrap();
67///         self.count
68///             .fetch_add(other.count.load(Ordering::Relaxed), Ordering::Relaxed);
69///     }
70///
71///     fn as_any(&self) -> &dyn Any {
72///         self
73///     }
74///
75///     fn as_usize(&self) -> usize {
76///         self.count.load(Ordering::Relaxed)
77///     }
78///
79///     fn is_eq(&self, other: &Arc<dyn CustomMetricValue>) -> bool {
80///         let Some(other) = other.as_any().downcast_ref::<Self>() else {
81///             return false;
82///         };
83///
84///         self.count.load(Ordering::Relaxed) == other.count.load(Ordering::Relaxed)
85///     }
86/// }
87/// ```
88///
89/// [`MetricValue::Custom`]: super::MetricValue::Custom
90pub trait CustomMetricValue: Display + Debug + Send + Sync {
91    /// Returns a new, zero-initialized version of this metric value.
92    ///
93    /// This value is used during metric aggregation to accumulate results.
94    fn new_empty(&self) -> Arc<dyn CustomMetricValue>;
95
96    /// Merges another metric value into this one.
97    ///
98    /// The type of `other` could be of a different custom type as long as it's aggregatable into self.
99    fn aggregate(&self, other: Arc<dyn CustomMetricValue + 'static>);
100
101    /// Returns this value as a [`Any`] to support dynamic downcasting.
102    fn as_any(&self) -> &dyn Any;
103
104    /// Optionally returns a numeric representation of the value, if meaningful.
105    /// Otherwise will default to zero.
106    ///
107    /// This is used for sorting and summarizing metrics.
108    fn as_usize(&self) -> usize {
109        0
110    }
111
112    /// Compares this value with another custom value.
113    fn is_eq(&self, other: &Arc<dyn CustomMetricValue>) -> bool;
114}