Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add otel push exporter #137

Merged
merged 8 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
33 changes: 33 additions & 0 deletions autometrics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,38 @@ prometheus-exporter = [
"dep:prometheus-client",
]

otel-push-exporter = [
"opentelemetry_sdk",
"opentelemetry_api",
"opentelemetry-otlp",
"opentelemetry-otlp/tls-roots",
]

otel-push-exporter-http = [
"otel-push-exporter",
"opentelemetry-otlp/http-proto"
]

otel-push-exporter-grpc = [
"otel-push-exporter",
"opentelemetry-otlp/grpc-tonic"
]

otel-push-exporter-tokio = [
"otel-push-exporter",
"opentelemetry_sdk/rt-tokio"
]

otel-push-exporter-tokio-current-thread = [
"otel-push-exporter",
"opentelemetry_sdk/rt-tokio-current-thread"
]

otel-push-exporter-async-std = [
"otel-push-exporter",
"opentelemetry_sdk/rt-async-std"
]

# Exemplars
exemplars-tracing = ["tracing", "tracing-subscriber"]
exemplars-tracing-opentelemetry-0_20 = [
Expand Down Expand Up @@ -68,6 +100,7 @@ opentelemetry-prometheus = { version = "0.13.0", optional = true }
opentelemetry_sdk = { version = "0.20", default-features = false, features = [
"metrics",
], optional = true }
opentelemetry-otlp = { version = "0.13.0", default-features = false, optional = true }
prometheus = { version = "0.13", default-features = false, optional = true }

# Used for prometheus-client feature
Expand Down
2 changes: 2 additions & 0 deletions autometrics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ mod constants;
pub mod exemplars;
mod labels;
pub mod objectives;
#[cfg(feature = "otel-push-exporter")]
pub mod otel_push_exporter;
#[cfg(feature = "prometheus-exporter")]
pub mod prometheus_exporter;
pub mod settings;
Expand Down
93 changes: 93 additions & 0 deletions autometrics/src/otel_push_exporter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use opentelemetry_api::metrics::MetricsError;
use opentelemetry_otlp::OtlpMetricPipeline;
use opentelemetry_otlp::{ExportConfig, Protocol, WithExportConfig};
use opentelemetry_sdk::metrics::MeterProvider;
use opentelemetry_sdk::runtime;
use std::ops::Deref;
use std::time::Duration;

#[repr(transparent)]
pub struct OtelMeterProvider(MeterProvider);

impl Deref for OtelMeterProvider {
type Target = MeterProvider;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl Drop for OtelMeterProvider {
fn drop(&mut self) {
// this will only error if `.shutdown` gets called multiple times
let _ = self.0.shutdown();
}
}

#[cfg(feature = "otel-push-exporter-http")]
pub fn init_http(url: impl Into<String>) -> Result<OtelMeterProvider, MetricsError> {
runtime()
.with_exporter(
opentelemetry_otlp::new_exporter()
.http()
.with_export_config(ExportConfig {
endpoint: url.into(),
protocol: Protocol::HttpBinary,
..Default::default()
}),
)
.with_period(Duration::from_secs(1))
mellowagain marked this conversation as resolved.
Show resolved Hide resolved
.build()
.map(OtelMeterProvider)
}

#[cfg(feature = "otel-push-exporter-grpc")]
pub fn init_grpc(url: impl Into<String>) -> Result<OtelMeterProvider, MetricsError> {
runtime()
.with_exporter(
opentelemetry_otlp::new_exporter()
.tonic()
.with_export_config(ExportConfig {
endpoint: url.into(),
protocol: Protocol::Grpc,
..Default::default()
}),
)
.with_period(Duration::from_secs(1))
.build()
.map(OtelMeterProvider)
}

#[cfg(all(
feature = "otel-push-exporter-tokio",
not(any(
feature = "otel-push-exporter-tokio-current-thread",
feature = "otel-push-exporter-async-std"
))
))]
fn runtime() -> OtlpMetricPipeline<opentelemetry_sdk::runtime::Tokio> {
return opentelemetry_otlp::new_pipeline().metrics(opentelemetry_sdk::runtime::Tokio);
}

#[cfg(all(
feature = "otel-push-exporter-tokio-current-thread",
not(any(
feature = "otel-push-exporter-tokio",
feature = "otel-push-exporter-async-std"
))
))]
fn runtime() -> OtlpMetricPipeline<opentelemetry_sdk::runtime::TokioCurrentThread> {
return opentelemetry_otlp::new_pipeline()
.metrics(opentelemetry_sdk::runtime::TokioCurrentThread);
}

#[cfg(all(
feature = "otel-push-exporter-async-std",
not(any(
feature = "otel-push-exporter-tokio",
feature = "otel-push-exporter-tokio-current-thread"
))
))]
fn runtime() -> OtlpMetricPipeline<opentelemetry_sdk::runtime::AsyncStd> {
return opentelemetry_otlp::new_pipeline().metrics(opentelemetry_sdk::runtime::AsyncStd);
}
2 changes: 1 addition & 1 deletion autometrics/tests/compile_test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::io;
use autometrics::autometrics;
use std::io;

// general purpose `Result`, part of the std prelude.
// notice both `Ok` and `Err` generic type arguments are explicitly provided
Expand Down
3 changes: 2 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ cargo run --package example-{name of example}
- [axum](./axum) - Use autometrics to instrument HTTP handlers using the `axum` framework
- [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
- [opentelemetry-push](./opentelemetry-push/) - Push metrics to an OpenTelemetry Collector via the OTLP HTTP or gRPC protocol using the Autometrics provided interface
- [opentelemetry-push-custom](./opentelemetry-push-custom/) - Push metrics to an OpenTelemetry Collector via the OTLP gRPC protocol using custom options

## Full Example

Expand Down
15 changes: 15 additions & 0 deletions examples/opentelemetry-push-custom/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "example-opentelemetry-push-custom"
version = "0.0.0"
publish = false
edition = "2021"

[dependencies]
autometrics = { path = "../../autometrics", features = ["opentelemetry-0_20"] }
autometrics-example-util = { path = "../util" }
# Note that the version of the opentelemetry crate MUST match
# the version used by autometrics
opentelemetry = { version = "0.20", features = ["metrics", "rt-tokio"] }
opentelemetry-otlp = { version = "0.13", features = ["tonic", "metrics"] }
opentelemetry-semantic-conventions = { version = "0.12.0" }
tokio = { version = "1", features = ["full"] }
106 changes: 106 additions & 0 deletions examples/opentelemetry-push-custom/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Autometrics + OTLP push controller (custom)

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
without using the Autometrics provided interface.

## 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 [email protected]/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-custom
```

### 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
-> 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.
19 changes: 19 additions & 0 deletions examples/opentelemetry-push-custom/otel-collector-config.yml
Original file line number Diff line number Diff line change
@@ -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]
49 changes: 49 additions & 0 deletions examples/opentelemetry-push-custom/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use autometrics::autometrics;
use autometrics_example_util::sleep_random_duration;
use opentelemetry::metrics::MetricsError;
use opentelemetry::sdk::metrics::MeterProvider;
use opentelemetry::{runtime, Context};
use opentelemetry_otlp::{ExportConfig, WithExportConfig};
use std::error::Error;
use std::time::Duration;
use tokio::time::sleep;

fn init_metrics() -> Result<MeterProvider, MetricsError> {
let export_config = ExportConfig {
endpoint: "http://localhost:4317".to_string(),
..ExportConfig::default()
};
let push_interval = Duration::from_secs(1);
opentelemetry_otlp::new_pipeline()
.metrics(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<dyn Error + Send + Sync + 'static>> {
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.force_flush(&cx)?;

meter_provider.shutdown()?;
Ok(())
}
7 changes: 1 addition & 6 deletions examples/opentelemetry-push/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ publish = false
edition = "2021"

[dependencies]
autometrics = { path = "../../autometrics", features = ["opentelemetry-0_20"] }
autometrics = { path = "../../autometrics", features = ["opentelemetry-0_20", "otel-push-exporter-http", "otel-push-exporter-tokio"] }
autometrics-example-util = { path = "../util" }
# Note that the version of the opentelemetry crate MUST match
# the version used by autometrics
opentelemetry = { version = "0.20", features = ["metrics", "rt-tokio"] }
opentelemetry-otlp = { version = "0.13", features = ["tonic", "metrics"] }
opentelemetry-semantic-conventions = { version = "0.12.0" }
tokio = { version = "1", features = ["full"] }
Loading