Skip to content

Commit 77c0ba6

Browse files
authored
Merge pull request #40 from frigus02/metrics
Metrics
2 parents b21cd01 + 0f2d6a4 commit 77c0ba6

File tree

16 files changed

+895
-349
lines changed

16 files changed

+895
-349
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
77

88
## [Unreleased]
99

10+
- Add support for metrics.
11+
- Take span resource into account for tags. Before a `service.name` in the resource would not populate the Cloud role name tag. Now it does.
12+
1013
## [0.17.0] - 2021-08-08
1114

1215
- Upgrade to `v0.16.0` of `opentelemetry`.

Cargo.toml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,21 @@ exclude = [
1818
"README.tpl",
1919
]
2020

21+
[package.metadata.docs.rs]
22+
all-features = true
23+
rustdoc-args = ["--cfg", "docsrs"]
24+
2125
[features]
2226
reqwest-blocking-client = ["reqwest-client"]
2327
reqwest-blocking-client-rustls = ["reqwest-client-rustls"]
2428
reqwest-client = ["opentelemetry-http/reqwest", "reqwest/native-tls"]
2529
reqwest-client-rustls = ["opentelemetry-http/reqwest", "reqwest/rustls-tls"]
2630
surf-client = ["opentelemetry-http/surf"]
31+
metrics = ["opentelemetry/metrics", "ureq"]
2732

2833
[dependencies]
2934
async-trait = "0.1"
35+
bytes = "1"
3036
chrono = "0.4"
3137
http = "0.2"
3238
once_cell = "1"
@@ -37,21 +43,20 @@ reqwest = { version = "0.11", default-features = false, features = ["blocking"],
3743
serde = { version = "1", features = ["derive"] }
3844
serde_json = "1"
3945
thiserror = "1"
46+
ureq = { version = "2", optional = true }
4047

4148
[dev-dependencies]
4249
async-std = { version = "1.9.0", features = ["attributes"] }
4350
backtrace = "0.3.60"
4451
env_logger = "0.9.0"
4552
opentelemetry = { version = "0.16", features = ["rt-tokio"] }
4653
opentelemetry-application-insights = { path = ".", features = ["reqwest-client", "reqwest-blocking-client"] }
54+
rand = "0.8.4"
4755
surf = "2.2.0"
4856
test-case = "1.1.0"
4957
tokio = { version = "1.7.0", features = ["rt", "macros", "process", "time"] }
5058
version-sync = "0.9.2"
5159

52-
[package.metadata.docs.rs]
53-
all-features = true
54-
5560
[badges]
5661
github = { repository = "frigus02/opentelemetry-application-insights", workflow = "CI" }
5762

@@ -71,6 +76,10 @@ required-features = ["reqwest-client"]
7176
name = "http_client_surf"
7277
required-features = ["surf-client", "opentelemetry/rt-async-std"]
7378

79+
[[example]]
80+
name = "metrics"
81+
required-features = ["metrics"]
82+
7483
[[example]]
7584
name = "opentelemetry"
7685
required-features = ["reqwest-client", "opentelemetry/rt-tokio"]

README.md

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ async fn main() {
5858
}
5959
```
6060

61-
### Features
61+
### Async runtimes and HTTP clients
6262

6363
In order to support different async runtimes, the exporter requires you to specify an HTTP
6464
client that works with your chosen runtime. The [`opentelemetry-http`] crate comes with support
@@ -78,6 +78,46 @@ for:
7878

7979
Alternatively you can bring any other HTTP client by implementing the `HttpClient` trait.
8080

81+
### Metrics
82+
83+
Please note: Metrics are still experimental both in the OpenTelemetry specification as well as
84+
Rust implementation.
85+
86+
Please note: The metrics export configuration is still a bit rough in this crate. But once
87+
configured it should work as expected.
88+
89+
This requires the **metrics** feature.
90+
91+
```rust
92+
use opentelemetry::{global, sdk};
93+
use std::time::Duration;
94+
95+
#[tokio::main]
96+
async fn main() {
97+
// Setup exporter
98+
let instrumentation_key = std::env::var("INSTRUMENTATION_KEY").unwrap();
99+
let exporter = opentelemetry_application_insights::Exporter::new(instrumentation_key, ());
100+
let controller = sdk::metrics::controllers::push(
101+
sdk::metrics::selectors::simple::Selector::Exact,
102+
sdk::export::metrics::ExportKindSelector::Stateless,
103+
exporter,
104+
tokio::spawn,
105+
opentelemetry::util::tokio_interval_stream,
106+
)
107+
.with_period(Duration::from_secs(1))
108+
.build();
109+
global::set_meter_provider(controller.provider());
110+
111+
// Record value
112+
let meter = global::meter("example");
113+
let value_recorder = meter.f64_value_recorder("pi").init();
114+
value_recorder.record(3.14, &[]);
115+
116+
// Give exporter some time to export values before exiting
117+
tokio::time::sleep(Duration::from_secs(5)).await;
118+
}
119+
```
120+
81121
## Attribute mapping
82122

83123
OpenTelemetry and Application Insights are using different terminology. This crate tries it's
@@ -154,6 +194,23 @@ All other attributes are directly converted to custom properties.
154194

155195
[exceptions]: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/exceptions.md
156196

197+
### Metrics
198+
199+
Metrics get reported to Application Insights as Metric Data. The [`Aggregator`] (chosen through
200+
the [`Selector`] passed to the controller) determines how the data is represented.
201+
202+
| Aggregator | Data representation |
203+
| -------------- | --------------------------------------------------------- |
204+
| Array | list of measurements |
205+
| DDSketch | aggregation with value, min, max and count |
206+
| Histogram | aggregation with sum and count (buckets are not exported) |
207+
| LastValue | one measurement |
208+
| MinMaxSumCount | aggregation with value, min, max and count |
209+
| Sum | aggregation with only a value |
210+
211+
[`Aggregator`]: https://docs.rs/opentelemetry/0.16.0/opentelemetry/sdk/export/metrics/trait.Aggregator.html
212+
[`Selector`]: https://docs.rs/opentelemetry/0.16.0/opentelemetry/sdk/metrics/selectors/simple/enum.Selector.html
213+
157214
## Application Insights integration
158215

159216
### Thanks

examples/metrics.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use opentelemetry::{
2+
global,
3+
metrics::{ObserverResult, Unit},
4+
sdk::{self, metrics::controllers},
5+
KeyValue,
6+
};
7+
use rand::{thread_rng, Rng};
8+
use std::{env, time::Duration};
9+
10+
#[tokio::main]
11+
async fn main() {
12+
env_logger::init();
13+
14+
let instrumentation_key =
15+
env::var("INSTRUMENTATION_KEY").expect("env var INSTRUMENTATION_KEY should exist");
16+
17+
let exporter = opentelemetry_application_insights::Exporter::new(instrumentation_key, ());
18+
let controller = controllers::push(
19+
sdk::metrics::selectors::simple::Selector::Exact,
20+
sdk::export::metrics::ExportKindSelector::Stateless,
21+
exporter,
22+
tokio::spawn,
23+
opentelemetry::util::tokio_interval_stream,
24+
)
25+
.with_period(Duration::from_secs(1))
26+
.build();
27+
28+
global::set_meter_provider(controller.provider());
29+
30+
let meter = global::meter("custom.instrumentation");
31+
32+
// Observer
33+
let cpu_utilization_callback = |res: ObserverResult<f64>| {
34+
let mut rng = thread_rng();
35+
res.observe(
36+
rng.gen_range(0.1..0.2),
37+
&[KeyValue::new("state", "idle"), KeyValue::new("cpu", 0)],
38+
)
39+
};
40+
let _ = meter
41+
.f64_value_observer("system.cpu.utilization", cpu_utilization_callback)
42+
.with_unit(Unit::new("1"))
43+
.init();
44+
45+
// Recorder
46+
let value_recorder = meter
47+
.i64_value_recorder("http.server.duration")
48+
.with_unit(Unit::new("milliseconds"))
49+
.init();
50+
let mut rng = thread_rng();
51+
for _ in 1..5 {
52+
value_recorder.record(
53+
rng.gen_range(200..300),
54+
&[
55+
KeyValue::new("http.method", "GET"),
56+
KeyValue::new("http.host", "10.1.2.4"),
57+
KeyValue::new("http.scheme", "http"),
58+
KeyValue::new("http.target", "/hello/world?name={}"),
59+
KeyValue::new("http.status_code", "200"),
60+
],
61+
);
62+
tokio::time::sleep(Duration::from_millis(500)).await;
63+
}
64+
65+
tokio::time::sleep(Duration::from_secs(3)).await;
66+
}

src/convert.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ use opentelemetry::{
44
sdk::{trace::EvictedHashMap, Resource},
55
trace::{SpanId, TraceId},
66
};
7-
use std::{
8-
sync::Arc,
9-
time::{Duration, SystemTime},
10-
};
7+
use std::time::{Duration, SystemTime};
118

129
pub(crate) fn trace_id_to_string(trace_id: TraceId) -> String {
1310
format!("{:032x}", trace_id.to_u128())
@@ -36,7 +33,7 @@ pub(crate) fn time_to_string(time: SystemTime) -> String {
3633

3734
pub(crate) fn attrs_to_properties(
3835
attributes: &EvictedHashMap,
39-
resource: Option<Arc<Resource>>,
36+
resource: Option<&Resource>,
4037
) -> Option<Properties> {
4138
let properties_from_attrs = attributes
4239
.iter()

0 commit comments

Comments
 (0)