metrics_demo/src/main.rs
Oliver Atkinson aeb2801b56 init
2024-10-10 12:49:19 -06:00

113 lines
3.5 KiB
Rust

use opentelemetry::{global, metrics::Counter, KeyValue};
use prometheus::Registry;
use rocket::{fairing::{Fairing, Info, Kind}, get, routes, Request, Response, State};
use tracing::error;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use std::process;
use url::Url;
struct MetricsState {
counter: Counter<u64>,
}
#[rocket::main]
async fn main() -> Result<(), tracing_loki::Error> {
let logs_provider = init_logs_provider("http://127.0.0.1:3100");
let (meter_provider, registry) = init_meter_provider();
// Create a meter from the above MeterProvider.
let meter = global::meter("mylibraryname");
// Create a Counter Instrument.
let counter = meter.u64_counter("my_counter").init();
let _ = rocket::build()
.mount("/", routes![metrics, hello_world])
.manage(registry)
.attach(MetricsState { counter })
.launch().await;
meter_provider.shutdown().unwrap();
logs_provider.abort();
Ok(())
}
#[get("/")]
fn hello_world() -> &'static str {
"Hello world."
}
// "/metrics" Is where prometheus expects to gather metrics at.
// If you change this make sure your prometheus config reflects the change.
#[get("/metrics")]
fn metrics(state: &State<Registry>) -> String {
let mut buffer = String::new();
let encoder = prometheus::TextEncoder::new();
let metric_families = state.gather();
encoder.encode_utf8(&metric_families, &mut buffer).unwrap();
buffer
}
fn init_logs_provider(url: &str) -> tokio::task::JoinHandle<()> {
// Setup logs
let (layer, task) = tracing_loki::builder()
/*
* Labels are for static things
* Fields are for dynamic things
*/
.label("program", "webserver").expect("Invalid log label name")
.extra_field("version", "v1.0").expect("Invalid log field name")
.extra_field("pid", format!("{}", process::id())).expect("Invalid log field name")
.build_url(Url::parse(url).unwrap()).expect("Invalid log url endpoint");
tracing_subscriber::registry()
.with(layer)
// Stdout if disired:
// .with(tracing_subscriber::fmt::Layer::new())
.init();
tokio::spawn(task)
}
fn init_meter_provider() -> (opentelemetry_sdk::metrics::SdkMeterProvider, Registry) {
use opentelemetry_sdk::metrics::SdkMeterProvider;
let registry = Registry::new();
let exporter = opentelemetry_prometheus::exporter()
.with_registry(registry.clone())
.build().unwrap();
let provider = SdkMeterProvider::builder()
.with_reader(exporter)
// .with_resource(Resource::new([KeyValue::new(
// "service.name",
// "metrics-basic-example",
// )]))
.build();
global::set_meter_provider(provider.clone());
(provider, registry)
}
#[rocket::async_trait]
impl Fairing for MetricsState {
fn info(&self) -> Info {
Info {
name: "Route metris provider",
kind: Kind::Response
}
}
async fn on_response<'r>(&self, req: &'r Request<'_>, res: &mut Response<'r>) {
let path = req.uri().path().as_str().to_owned();
let method = req.method().as_str();
let status = res.status().to_string();
let query = req.query_fields().map(|f| format!("{}={},", f.name, f.value)).collect::<String>();
self.counter.add(
1, &[
KeyValue::new("Path", path),
KeyValue::new("Method", method),
KeyValue::new("Status", status),
KeyValue::new("Query", query),
], );
}
}