11use opentelemetry:: global;
22use opentelemetry:: KeyValue ;
3- use opentelemetry_sdk:: metrics:: { Instrument , SdkMeterProvider , Stream , Temporality } ;
3+ use opentelemetry_sdk:: metrics:: { Aggregation , Instrument , SdkMeterProvider , Stream , Temporality } ;
44use opentelemetry_sdk:: Resource ;
55use std:: error:: Error ;
66
@@ -33,6 +33,36 @@ fn init_meter_provider() -> opentelemetry_sdk::metrics::SdkMeterProvider {
3333 }
3434 } ;
3535
36+ // for example 3
37+ // Unlike a regular OpenTelemetry histogram with fixed buckets, which can be
38+ // specified explicitly, an exponential histogram calculates bucket widths
39+ // automatically, growing them exponentially. The configuration is
40+ // controlled by two parameters: max_size defines the maximum number of
41+ // buckets, while max_scale adjusts the resolution, with higher values
42+ // providing greater precision.
43+ // If the minimum and maximum values are known in advance, a regular
44+ // histogram is often the better choice. However, if the range of values is
45+ // unpredictable e.g. may include extreme outliers, an exponential histogram
46+ // is more suitable. A example is measuring packet round-trip time in a
47+ // WLAN: while most packets return in milliseconds, some may occasionally
48+ // take hundreds of milliseconds or even seconds.
49+ // Details are in:
50+ // https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponentialhistogram
51+ let my_view_change_aggregation = |i : & Instrument | {
52+ if i. name ( ) == "my_third_histogram" {
53+ Stream :: builder ( )
54+ . with_aggregation ( Aggregation :: Base2ExponentialHistogram {
55+ max_size : 10 ,
56+ max_scale : 5 ,
57+ record_min_max : true ,
58+ } )
59+ . build ( )
60+ . ok ( )
61+ } else {
62+ None
63+ }
64+ } ;
65+
3666 // Build exporter using Delta Temporality.
3767 let exporter = opentelemetry_stdout:: MetricExporterBuilder :: default ( )
3868 . with_temporality ( Temporality :: Delta )
@@ -47,6 +77,7 @@ fn init_meter_provider() -> opentelemetry_sdk::metrics::SdkMeterProvider {
4777 . with_resource ( resource)
4878 . with_view ( my_view_rename_and_unit)
4979 . with_view ( my_view_change_cardinality)
80+ . with_view ( my_view_change_aggregation)
5081 . build ( ) ;
5182 global:: set_meter_provider ( provider. clone ( ) ) ;
5283 provider
@@ -112,6 +143,23 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
112143
113144 histogram2. record ( 1.8 , & [ KeyValue :: new ( "mykey1" , "v7" ) ] ) ;
114145
146+ // Example 3 - Use exponential histogram.
147+ let histogram3 = meter
148+ . f64_histogram ( "my_third_histogram" )
149+ . with_description ( "My histogram example description" )
150+ . build ( ) ;
151+ histogram3. record ( -1.3 , & [ KeyValue :: new ( "mykey1" , "v1" ) ] ) ;
152+ histogram3. record ( -5.5 , & [ KeyValue :: new ( "mykey1" , "v1" ) ] ) ;
153+ // is intentionally at the boundary of bucket
154+ histogram3. record ( -4.0 , & [ KeyValue :: new ( "mykey1" , "v1" ) ] ) ;
155+ histogram3. record ( 16.0 , & [ KeyValue :: new ( "mykey1" , "v1" ) ] ) ;
156+ // Internally the exponential histogram puts values either into a list of
157+ // negative buckets or a list of positive buckets. Based on the values which
158+ // are added the buckets are adjusted automatically. E.g. depending if the
159+ // next record is commented/uncommented, then exponential histogram will
160+ // have a different scale.
161+ histogram3. record ( 0.4 , & [ KeyValue :: new ( "mykey1" , "v1" ) ] ) ;
162+
115163 // Metrics are exported by default every 60 seconds when using stdout exporter,
116164 // however shutting down the MeterProvider here instantly flushes
117165 // the metrics, instead of waiting for the 60 sec interval.
0 commit comments