This commit is contained in:
Oliver Atkinson 2024-10-10 12:49:19 -06:00
commit aeb2801b56
6 changed files with 2462 additions and 0 deletions

2
.env Normal file
View File

@ -0,0 +1,2 @@
export ROCKET_CLI_COLORS=false
export RUST_LOG=debug

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

2317
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

17
Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
name = "demo"
version = "0.1.0"
edition = "2021"
[dependencies]
opentelemetry = { version = "0.24.0", features = ["metrics", "otel_unstable"] }
opentelemetry-prometheus = { version = "0.17.0", features = ["prometheus-encoding"] }
# opentelemetry-stdout = { version = "0.26.0", features = ["metrics"] }
opentelemetry_sdk = { version = "0.24.0", features = ["metrics", "rt-tokio"] }
prometheus = "0.13.4"
rocket = "0.5.1"
tokio = { version = "1.40.0", features = ["full"] }
tracing = "0.1.40"
tracing-loki = "0.2.5"
tracing-subscriber = "0.3.18"
url = "2.5.2"

13
README.md Normal file
View File

@ -0,0 +1,13 @@
# Visable Rocket
Adding visability to rocket.rs
In the project you need to specifiy the url that Loki is at.
Then you need to add this project's url to your prometheus config.
```toml
scrape_configs:
- job_name: rocket
static_configs:
- targets: ['localhost:8000']
```

112
src/main.rs Normal file
View File

@ -0,0 +1,112 @@
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),
], );
}
}