Skip to content

Commit

Permalink
New metrics SDK
Browse files Browse the repository at this point in the history
This patch updates the metrics SDK to the latest spec. The following
breaking changes are introduced.

Metrics API changes:

* Move `AttributeSet` to SDK as it's not mentioned in the spec or used
  in the api
* Consolidate `AsyncCounter`, `AsyncUpDownCounter`, and `AsyncGauge`
  into `AsyncInstrument` trait and add downcasting for observer
callbacks.
* Add `AsyncInstrumentBuilder` to allow per-instrument callback
  configuration.
* Allow metric `name` and `description` fields to be `Cow<'static, str>`
* Warn on metric misconfiguration when using instrument builder `init`
  rather than returning error
* Update `Meter::register_callback` to take a list of async instruments
  and validate they are registered in the callback through the
associated `Observer`
* Allow registered callbacks to be unregistered.

Metrics SDK changes:

* Introduce `Scope` as type alias for `InstrumentationLibrary`
* Update `Aggregation` to match aggregation spec
* Refactor `BasicController` to spec compliant `ManualReader`
* Refactor `PushController` to spec compliant `PeriodicReader`
* Update metric data fields to match spec, including exemplars.
* Split `MetricsExporter` into `Reader`s and `PushMetricExporter`s
* Add `View` implementation
* Remove `AtomicNumber`s
* Refactor `Processor`s into `Pipeline`

Metrics exporter changes:

* Update otlp exporter to match new metrics data
* Update otlp exporter configuration to allow aggregation and
  temporality selectors to be optional.
* Update prometheus exporter to match new metrics data

Example changes:
* Update otlp metrics and prometheus examples.
* Remove basic example as we should be focusing on the OTLP variants
  • Loading branch information
jtescher committed Mar 19, 2023
1 parent 57147b1 commit e242ae0
Show file tree
Hide file tree
Showing 117 changed files with 6,964 additions and 6,649 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ members = [
"examples/actix-udp",
"examples/async",
"examples/aws-xray",
"examples/basic",
"examples/basic-otlp",
"examples/basic-otlp-http",
"examples/datadog",
Expand Down
5 changes: 3 additions & 2 deletions examples/basic-otlp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ publish = false

[dependencies]
futures-util = { version = "0.3", default-features = false, features = ["std"] }
lazy_static = "1.4"
opentelemetry = { path = "../../opentelemetry", features = ["rt-tokio", "metrics"] }
once_cell = "1.17"
opentelemetry_api = { path = "../../opentelemetry-api", features = ["metrics"] }
opentelemetry_sdk = { path = "../../opentelemetry-sdk", features = ["rt-tokio"] }
opentelemetry-otlp = { path = "../../opentelemetry-otlp", features = ["tonic", "metrics"] }
opentelemetry-semantic-conventions = { path = "../../opentelemetry-semantic-conventions" }
serde_json = "1.0"
Expand Down
42 changes: 19 additions & 23 deletions examples/basic-otlp/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
use opentelemetry::global::shutdown_tracer_provider;
use opentelemetry::runtime;
use opentelemetry::sdk::export::metrics::aggregation::cumulative_temporality_selector;
use opentelemetry::sdk::metrics::controllers::BasicController;
use opentelemetry::sdk::metrics::selectors;
use opentelemetry::sdk::Resource;
use opentelemetry::trace::TraceError;
use opentelemetry::{global, sdk::trace as sdktrace};
use opentelemetry::{
use once_cell::sync::Lazy;
use opentelemetry_api::global;
use opentelemetry_api::global::shutdown_tracer_provider;
use opentelemetry_api::trace::TraceError;
use opentelemetry_api::{
metrics,
trace::{TraceContextExt, Tracer},
Context, Key, KeyValue,
};
use opentelemetry_otlp::{ExportConfig, WithExportConfig};
use opentelemetry_sdk::{metrics::MeterProvider, runtime, trace as sdktrace, Resource};
use std::error::Error;
use std::time::Duration;

Expand All @@ -29,20 +26,16 @@ fn init_tracer() -> Result<sdktrace::Tracer, TraceError> {
"trace-demo",
)])),
)
.install_batch(opentelemetry::runtime::Tokio)
.install_batch(runtime::Tokio)
}

fn init_metrics() -> metrics::Result<BasicController> {
fn init_metrics() -> metrics::Result<MeterProvider> {
let export_config = ExportConfig {
endpoint: "http://localhost:4317".to_string(),
..ExportConfig::default()
};
opentelemetry_otlp::new_pipeline()
.metrics(
selectors::simple::inexpensive(),
cumulative_temporality_selector(),
runtime::Tokio,
)
.metrics(runtime::Tokio)
.with_exporter(
opentelemetry_otlp::new_exporter()
.tonic()
Expand All @@ -54,22 +47,22 @@ fn init_metrics() -> metrics::Result<BasicController> {
const LEMONS_KEY: Key = Key::from_static_str("lemons");
const ANOTHER_KEY: Key = Key::from_static_str("ex.com/another");

lazy_static::lazy_static! {
static ref COMMON_ATTRIBUTES: [KeyValue; 4] = [
static COMMON_ATTRIBUTES: Lazy<[KeyValue; 4]> = Lazy::new(|| {
[
LEMONS_KEY.i64(10),
KeyValue::new("A", "1"),
KeyValue::new("B", "2"),
KeyValue::new("C", "3"),
];
}
]
});

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
// By binding the result to an unused variable, the lifetime of the variable
// matches the containing block, reporting traces and metrics during the whole
// execution.
let _ = init_tracer()?;
let metrics_controller = init_metrics()?;
let meter_provider = init_metrics()?;
let cx = Context::new();

let tracer = global::tracer("ex.com/basic");
Expand All @@ -79,7 +72,10 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
.f64_observable_gauge("ex.com.one")
.with_description("A gauge set to 1.0")
.init();
meter.register_callback(move |cx| gauge.observe(cx, 1.0, COMMON_ATTRIBUTES.as_ref()))?;

meter.register_callback(&[gauge.as_any()], move |cx, observer| {
observer.observe_f64(cx, &gauge, 1.0, COMMON_ATTRIBUTES.as_ref())
})?;

let histogram = meter.f64_histogram("ex.com.two").init();
histogram.record(&cx, 5.5, COMMON_ATTRIBUTES.as_ref());
Expand All @@ -106,7 +102,7 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
tokio::time::sleep(Duration::from_secs(60)).await;

shutdown_tracer_provider();
metrics_controller.stop(&cx)?;
meter_provider.shutdown()?;

Ok(())
}
13 changes: 0 additions & 13 deletions examples/basic/Cargo.toml

This file was deleted.

18 changes: 0 additions & 18 deletions examples/basic/README.md

This file was deleted.

109 changes: 0 additions & 109 deletions examples/basic/src/main.rs

This file was deleted.

Binary file removed examples/basic/trace.png
Binary file not shown.
8 changes: 4 additions & 4 deletions examples/dynatrace/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ publish = false
futures = "0.3"
http = "0.2"
lazy_static = "1.4"
opentelemetry = { path = "../../opentelemetry", default-features = false, features = ["trace"] }
opentelemetry = { version = "0.18", default-features = false, features = ["trace"] }
opentelemetry-dynatrace = { path = "../../opentelemetry-dynatrace" }
opentelemetry-http = { path = "../../opentelemetry-http", default-features = false }
opentelemetry-otlp = { path = "../../opentelemetry-otlp", default-features = false, features = ["http-proto", "reqwest-client"] }
opentelemetry-semantic-conventions = { path = "../../opentelemetry-semantic-conventions" }
opentelemetry-http = { version = "0.7", default-features = false }
opentelemetry-otlp = { version = "0.11", default-features = false, features = ["http-proto", "reqwest-client"] }
opentelemetry-semantic-conventions = "0.10"
tokio = { version = "1.0", features = ["full"] }
7 changes: 4 additions & 3 deletions examples/hyper-prometheus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
opentelemetry = { path = "../../opentelemetry", features = ["rt-tokio"] }
hyper = { version = "0.14", features = ["full"] }
once_cell = "1.17"
opentelemetry_api = { path = "../../opentelemetry-api", features = ["metrics"] }
opentelemetry_sdk = { path = "../../opentelemetry-sdk", features = ["metrics", "rt-tokio"] }
opentelemetry-prometheus = { path = "../../opentelemetry-prometheus" }
prometheus = "0.13"
lazy_static = "1.4"
hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1", features = ["full"] }
57 changes: 25 additions & 32 deletions examples/hyper-prometheus/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,20 @@
#[macro_use]
extern crate lazy_static;

use hyper::{
header::CONTENT_TYPE,
service::{make_service_fn, service_fn},
Body, Method, Request, Response, Server,
};
use opentelemetry::{
global,
metrics::{Counter, Histogram},
sdk::{
export::metrics::aggregation,
metrics::{controllers, processors, selectors},
},
use once_cell::sync::Lazy;
use opentelemetry_api::{
metrics::{Counter, Histogram, MeterProvider as _, Unit},
Context, KeyValue,
};
use opentelemetry_prometheus::PrometheusExporter;
use prometheus::{Encoder, TextEncoder};
use opentelemetry_sdk::metrics::MeterProvider;
use prometheus::{Encoder, Registry, TextEncoder};
use std::convert::Infallible;
use std::sync::Arc;
use std::time::SystemTime;

lazy_static! {
static ref HANDLER_ALL: [KeyValue; 1] = [KeyValue::new("handler", "all")];
}
static HANDLER_ALL: Lazy<[KeyValue; 1]> = Lazy::new(|| [KeyValue::new("handler", "all")]);

async fn serve_req(
cx: Context,
Expand All @@ -33,15 +24,17 @@ async fn serve_req(
println!("Receiving request at path {}", req.uri());
let request_start = SystemTime::now();

state.http_counter.add(&cx, 1, &[]);
state.http_counter.add(&cx, 1, HANDLER_ALL.as_ref());

let response = match (req.method(), req.uri().path()) {
(&Method::GET, "/metrics") => {
let mut buffer = vec![];
let encoder = TextEncoder::new();
let metric_families = state.exporter.registry().gather();
let metric_families = state.registry.gather();
encoder.encode(&metric_families, &mut buffer).unwrap();
state.http_body_gauge.record(&cx, buffer.len() as u64, &[]);
state
.http_body_gauge
.record(&cx, buffer.len() as u64, HANDLER_ALL.as_ref());

Response::builder()
.status(200)
Expand All @@ -68,37 +61,37 @@ async fn serve_req(
}

struct AppState {
exporter: PrometheusExporter,
registry: Registry,
http_counter: Counter<u64>,
http_body_gauge: Histogram<u64>,
http_req_histogram: Histogram<f64>,
}

#[tokio::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let controller = controllers::basic(processors::factory(
selectors::simple::histogram([1.0, 2.0, 5.0, 10.0, 20.0, 50.0]),
aggregation::cumulative_temporality_selector(),
))
.build();

let exporter = opentelemetry_prometheus::exporter(controller).init();
let registry = Registry::new();
let exporter = opentelemetry_prometheus::exporter()
.with_registry(registry.clone())
.build()?;
let provider = MeterProvider::builder().with_reader(exporter).build();
let cx = Context::new();

let meter = global::meter("ex.com/hyper");
let meter = provider.meter("hyper-prometheus-example");
let state = Arc::new(AppState {
exporter,
registry,
http_counter: meter
.u64_counter("example.http_requests_total")
.u64_counter("http_requests_total")
.with_description("Total number of HTTP requests made.")
.init(),
http_body_gauge: meter
.u64_histogram("example.http_response_size_bytes")
.u64_histogram("example.http_response_size")
.with_unit(Unit::new("By"))
.with_description("The metrics HTTP response sizes in bytes.")
.init(),
http_req_histogram: meter
.f64_histogram("example.http_request_duration_seconds")
.with_description("The HTTP request latencies in seconds.")
.f64_histogram("example.http_request_duration")
.with_unit(Unit::new("ms"))
.with_description("The HTTP request latencies in milliseconds.")
.init(),
});

Expand Down
Loading

0 comments on commit e242ae0

Please sign in to comment.