diff --git a/examples/README.md b/examples/README.md index ec68296f..c9af51be 100644 --- a/examples/README.md +++ b/examples/README.md @@ -12,6 +12,7 @@ cargo run --package example-{name of example} - [axum](./axum) - Use autometrics to instrument HTTP handlers - [custom-metrics](./custom-metrics/) - Define your own custom metrics alongside the ones generated by autometrics (using any of the metrics collection crates) - [exemplars-tracing](./exemplars-tracing/) - Use fields from `tracing::Span`s as Prometheus exemplars +- [opentelemetry-push](./opentelemetry-push/) - Push metrics to an OpenTelemetry Collector via the OTLP gRPC protocol ## Full Example diff --git a/examples/opentelemetry-push/Cargo.toml b/examples/opentelemetry-push/Cargo.toml new file mode 100644 index 00000000..beac86d4 --- /dev/null +++ b/examples/opentelemetry-push/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "example-opentelemetry-push" +version = "0.0.0" +publish = false +edition = "2021" + +[dependencies] +autometrics = { path = "../../autometrics", features = ["opentelemetry"] } +autometrics-example-util = { path = "../util" } +# Note that the version of the opentelemetry crate MUST match +# the version used by autometrics +opentelemetry = { version = "0.19", features = ["metrics", "rt-tokio"] } +opentelemetry-otlp = { version = "0.12", features = ["tonic", "metrics"] } +opentelemetry-semantic-conventions = { version = "0.11" } +tokio = { version = "1", features = ["full"] } diff --git a/examples/opentelemetry-push/README.md b/examples/opentelemetry-push/README.md new file mode 100644 index 00000000..bb2358b7 --- /dev/null +++ b/examples/opentelemetry-push/README.md @@ -0,0 +1,105 @@ +# Autometrics + OTLP push controller + +This example demonstrates how you can push autometrics via OTLP gRPC protocol to the [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) or another OTel-compatible solution. + +## Running the example + +### Start a basic OTEL-Collector + +You can use the [`otel-collector-config.yml`](./otel-collector-config.yml) file to start an otel-collector container that listens on 0.0.0.0:4317 for incoming otlp-gRPC traffic, and exports the metrics it receives to stdout. + +Run the following command in a second terminal to start a container in interactive mode: + +```bash +docker run -it --name otel-col \ + -p 4317:4317 -p 13133:13133 \ + -v $PWD/otel-collector-config.yml:/etc/otelcol/config.yaml \ + otel/opentelemetry-collector:latest +``` + +You should see the collector initialization output, that should end with something like: + +```text +... +2023-06-07T15:56:42.617Z info otlpreceiver@v0.75.0/otlp.go:94 Starting GRPC server {"kind": "receiver", "name": "otlp", "data_type": "metrics", "endpoint": "0.0.0.0:4317"} +2023-06-07T15:56:42.618Z info service/service.go:146 Everything is ready. Begin running and processing data. +``` + +### Execute example code + +Then come back on your primary shell and run this example: + +```shell +cargo run -p example-opentelemetry-push +``` + +### Check the output + +On the stdout of the terminal where you started the opentelemetry-collector container, you should see the metrics generated by autometrics macro being pushed every 10 seconds since example exit, like: + +```text +... +Metric #0 +Descriptor: + -> Name: function.calls.count + -> Description: Autometrics counter for tracking function calls + -> Unit: + -> DataType: Sum + -> IsMonotonic: true + -> AggregationTemporality: Cumulative +NumberDataPoints #0 +Data point attributes: + -> caller: Str() + -> function: Str(do_stuff) + -> module: Str(example_opentelemetry_push) +StartTimestamp: 2023-06-07 16:01:08.549300623 +0000 UTC +Timestamp: 2023-06-07 16:01:48.551531429 +0000 UTC +Value: 10 +Metric #1 +Descriptor: + -> Name: build_info + -> Description: Autometrics info metric for tracking software version and build details + -> Unit: + -> DataType: Sum + -> IsMonotonic: false + -> AggregationTemporality: Cumulative +NumberDataPoints #0 +Data point attributes: + -> branch: Str() + -> commit: Str() + -> version: Str(0.0.0) +StartTimestamp: 2023-06-07 16:01:08.549300623 +0000 UTC +Timestamp: 2023-06-07 16:01:48.551531429 +0000 UTC +Value: 1.000000 +Metric #2 +Descriptor: + -> Name: function.calls.duration + -> Description: Autometrics histogram for tracking function call duration + -> Unit: + -> DataType: Sum + -> IsMonotonic: false + -> AggregationTemporality: Cumulative +NumberDataPoints #0 +Data point attributes: + -> function: Str(do_stuff) + -> module: Str(example_opentelemetry_push) +StartTimestamp: 2023-06-07 16:01:08.549300623 +0000 UTC +Timestamp: 2023-06-07 16:01:48.551531429 +0000 UTC +Value: 0.000122 + {"kind": "exporter", "data_type": "metrics", "name": "logging"} +... +``` + +### Cleanup + +In the end, to stop the opentelemetry collector container just hit `^C`. + +Then delete the container with + +```bash +docker rm otel-col +``` + +## OpenTelemetry Metrics Push Controller + +The metric push controller is implemented as from this [example](https://github.com/open-telemetry/opentelemetry-rust/blob/f20c9b40547ee20b6ec99414bb21abdd3a54d99b/examples/basic-otlp/src/main.rs#L35-L52) from `opentelemetry-rust` crate. diff --git a/examples/opentelemetry-push/otel-collector-config.yml b/examples/opentelemetry-push/otel-collector-config.yml new file mode 100644 index 00000000..731c5d09 --- /dev/null +++ b/examples/opentelemetry-push/otel-collector-config.yml @@ -0,0 +1,19 @@ +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + +exporters: + logging: + loglevel: debug + +processors: + batch: + +service: + pipelines: + metrics: + receivers: [otlp] + processors: [] + exporters: [logging] diff --git a/examples/opentelemetry-push/src/main.rs b/examples/opentelemetry-push/src/main.rs new file mode 100644 index 00000000..ca2ef31d --- /dev/null +++ b/examples/opentelemetry-push/src/main.rs @@ -0,0 +1,54 @@ +use autometrics::autometrics; +use autometrics_example_util::sleep_random_duration; +use opentelemetry::{runtime, Context}; +use opentelemetry::sdk::export::metrics::aggregation::cumulative_temporality_selector; +use opentelemetry::sdk::metrics::controllers::BasicController; +use opentelemetry::sdk::metrics::selectors; +use opentelemetry::metrics; +use opentelemetry_otlp::{ExportConfig, WithExportConfig}; +use tokio::time::sleep; +use std::error::Error; +use std::time::Duration; + +fn init_metrics() -> metrics::Result { + let export_config = ExportConfig { + endpoint: "http://localhost:4317".to_string(), + ..ExportConfig::default() + }; + let push_interval = Duration::from_secs(1); + opentelemetry_otlp::new_pipeline() + .metrics( + selectors::simple::inexpensive(), + cumulative_temporality_selector(), + runtime::Tokio, + ) + .with_exporter( + opentelemetry_otlp::new_exporter() + .tonic() + .with_export_config(export_config), + ) + .with_period(push_interval) + .build() +} + +#[autometrics] +async fn do_stuff() { + println!("Doing stuff..."); + sleep_random_duration().await; +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let meter_provider = init_metrics()?; + let cx = Context::current(); + + for _ in 0..100 { + do_stuff().await; + } + + println!("Waiting so that we could see metrics going down..."); + sleep(Duration::from_secs(10)).await; + meter_provider.stop(&cx)?; + + Ok(()) +}