diff --git a/Cargo.lock b/Cargo.lock index 05cebed..e742898 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -666,6 +666,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets", +] + [[package]] name = "getrandom" version = "0.2.11" @@ -1045,6 +1055,26 @@ dependencies = [ "version_check", ] +[[package]] +name = "nanobyte_opentelemetry" +version = "0.1.0" +dependencies = [ + "gethostname", + "opentelemetry", + "opentelemetry-otlp", + "opentelemetry-semantic-conventions", + "opentelemetry-stdout", + "opentelemetry_sdk", + "rocket", + "tracing", + "tracing-appender", + "tracing-log", + "tracing-opentelemetry", + "tracing-subscriber", + "uuid", + "yansi 0.5.1", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1219,22 +1249,13 @@ dependencies = [ "fern", "headless_chrome", "log", - "opentelemetry", - "opentelemetry-otlp", - "opentelemetry-semantic-conventions", - "opentelemetry-stdout", - "opentelemetry_sdk", + "nanobyte_opentelemetry", "ovlach_data", "rocket", "serde", "serde_yaml", "tokio", "tracing", - "tracing-appender", - "tracing-log", - "tracing-opentelemetry", - "tracing-subscriber", - "uuid", "yansi 0.5.1", ] diff --git a/Cargo.toml b/Cargo.toml index 316c296..25d4210 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,14 +21,6 @@ tokio = { version = "1.34.0", features = ["macros"] } tracing = { version = "0.1.40", features = ["attributes", "std"] } -tracing-appender = "0.2.2" -tracing-log = "0.2.0" -tracing-subscriber = {version="0.3.17", features=["env-filter", "json", "registry", "smallvec"]} -tracing-opentelemetry = "0.22.0" -opentelemetry = {version="0.21"} -opentelemetry_sdk = {version="0.21", features = ["trace", "rt-tokio", "logs"]} -opentelemetry-stdout = { version = "0.2.0", features = ["trace", "logs", "metrics"] } -opentelemetry-otlp = { version="*", features = ["trace", "logs", "metrics"] } -opentelemetry-semantic-conventions = "0.13.0" -uuid = {version="1.4.1", features = ["v4"]} -yansi = "0.5.1" \ No newline at end of file +yansi = "0.5.1" + +nanobyte_opentelemetry = { path = "../nanobyte_opentelemetry" } \ No newline at end of file diff --git a/src/chromium/rocket.rs b/src/chromium/rocket.rs index a93717d..3fd1dc1 100644 --- a/src/chromium/rocket.rs +++ b/src/chromium/rocket.rs @@ -2,7 +2,7 @@ use std::{sync::Arc, time::Duration}; use async_trait::async_trait; use async_mutex::Mutex; use headless_chrome::{Browser, LaunchOptions}; -use tracing::{error, info, debug, trace, warn}; +use tracing::{error, info, debug, trace, warn, trace_span}; use rocket::{fairing::{Fairing, self}, Rocket, Build, Request, request::{FromRequest, Outcome}, futures::pin_mut, http::Status}; use tokio::time::sleep; @@ -55,10 +55,12 @@ impl ChromiumCoordinator { } fn create_browser_instance() -> BrowserHolder { - BrowserHolder { browser: Browser::new(LaunchOptions { - idle_browser_timeout: Duration::MAX, // Wait inifinity for commands - ..LaunchOptions::default() - }).unwrap() } + trace_span!("Creating Chromium instance").in_scope(|| { + BrowserHolder { browser: Browser::new(LaunchOptions { + idle_browser_timeout: Duration::MAX, // Wait inifinity for commands + ..LaunchOptions::default() + }).unwrap() } + }) } fn spawn_browser(&self) { diff --git a/src/lib.rs b/src/lib.rs index 3dfc13a..514dc2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,126 +1,15 @@ use chromium::rocket::Chromium; -use log::debug; +use nanobyte_opentelemetry::rocket::TracingFairing; use rocket::{Rocket, Build, routes}; -use tools::rocket::TracingFairing; -use tracing::Level; -use tracing_subscriber::EnvFilter; -use tracing_subscriber::prelude::*; pub mod routes; mod chromium; mod tools; -use rocket::http::Status; -use rocket::request::FromRequest; -use rocket::request::Outcome; -use rocket::serde::{json::Json, Serialize}; -use rocket::{ - fairing::{Fairing, Info, Kind}, - Data, Request, Response, -}; - - -use tracing::{info_span, Span}; -use tracing_log::LogTracer; - -use tracing_subscriber::registry::LookupSpan; -use uuid::Uuid; -use yansi::Paint; - - use opentelemetry::trace::TracerProvider as _; - use opentelemetry_sdk::trace::TracerProvider; - use opentelemetry_stdout as stdout; - use tracing::{error, span}; - use tracing_subscriber::layer::SubscriberExt; - use tracing_subscriber::Registry; - - - use opentelemetry::{ - metrics::MeterProvider as _, - trace::{Tracer, TracerProvider as _}, - KeyValue, - }; - use opentelemetry_sdk::{ - metrics::{PeriodicReader, MeterProvider}, - runtime, - }; - - -#[derive(PartialEq, Eq, Debug, Clone, Copy)] -pub enum LogLevel { - /// Only shows errors and warnings: `"critical"`. - Critical, - /// Shows errors, warnings, and some informational messages that are likely - /// to be relevant when troubleshooting such as configuration: `"support"`. - Support, - /// Shows everything except debug and trace information: `"normal"`. - Normal, - /// Shows everything: `"debug"`. - Debug, - /// Shows nothing: "`"off"`". - Off, -} - - -pub fn filter_layer(level: LogLevel) -> EnvFilter { - let filter_str = match level { - LogLevel::Critical => "warn,hyper=off,rustls=off", - LogLevel::Support => "warn,rocket::support=info,hyper=off,rustls=off", - LogLevel::Normal => "info,hyper=off,rustls=off", - LogLevel::Debug => "trace", - LogLevel::Off => "off", - }; - - tracing_subscriber::filter::EnvFilter::try_new(filter_str).expect("filter string must parse") -} - - -pub fn json_logging_layer< - S: for<'a> tracing_subscriber::registry::LookupSpan<'a> + tracing::Subscriber, ->() -> impl tracing_subscriber::Layer { - //Paint::disable(); - - tracing_subscriber::fmt::layer() - .json() - // Configure the formatter to use `print!` rather than - // `stdout().write_str(...)`, so that logs are captured by libtest's test - // capturing. - .with_test_writer() -} - - -pub fn default_logging_layer() -> impl tracing_subscriber::Layer -where - S: tracing::Subscriber, - S: for<'span> LookupSpan<'span>, -{ - let field_format = tracing_subscriber::fmt::format::debug_fn(|writer, field, value| { - // We'll format the field name and value separated with a colon. - if field.name() == "message" { - write!(writer, "{:?}", Paint::new(value).bold()) - } else { - write!(writer, "{}: {:?}", field, Paint::default(value).bold()) - } - }) - .delimited(", ") - .display_messages(); - - tracing_subscriber::fmt::layer() - .fmt_fields(field_format) - // Configure the formatter to use `print!` rather than - // `stdout().write_str(...)`, so that logs are captured by libtest's test - // capturing. - .with_test_writer() -} - - pub fn rocket_builder() -> Rocket { - //let tracer_provider = init_trace(); - rocket::build() - .attach(TracingFairing {}) + .attach(TracingFairing::ignite()) .attach(Chromium::ignite()) - .attach(tools::rocket::RequestTimer) .mount("/", routes![ routes::pdf::render_pdf_cv, ]) diff --git a/src/main.rs b/src/main.rs index d3e6ced..aa5eb88 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,67 +1,7 @@ -use opentelemetry_otlp::WithExportConfig; -use opentelemetry_sdk::Resource; use ovlach_pdf::rocket_builder; -use rocket::launch; - - -use opentelemetry::trace::{TracerProvider as _, TraceError}; -use opentelemetry_sdk::trace::{TracerProvider, Config}; -use opentelemetry_stdout as stdout; -use tokio::net::unix::pipe; -use tracing::{error, span}; -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; -use tracing_subscriber::Registry; - - -use opentelemetry::{ - metrics::MeterProvider as _, - trace::{Tracer, TracerProvider as _}, - KeyValue, -}; -use opentelemetry_sdk::{ - metrics::{PeriodicReader, MeterProvider}, - runtime, -}; - - -fn init_meterprovider() -> MeterProvider { - let exporter = opentelemetry_stdout::MetricsExporter::default(); - let reader = PeriodicReader::builder(exporter, runtime::Tokio).build(); - MeterProvider::builder().with_reader(reader).build() -} - - -fn init_tracer() -> Result { - opentelemetry_otlp::new_pipeline() - .tracing() - .with_exporter( - opentelemetry_otlp::new_exporter() - .tonic() - .with_endpoint("http://localhost:4317"), - ) - .with_trace_config( - opentelemetry_sdk::trace::config().with_resource(Resource::new(vec![KeyValue::new( - opentelemetry_semantic_conventions::resource::SERVICE_NAME, - "ovlach_pdf", - )])), - ) - .install_batch(runtime::Tokio) - //.install_simple() - } #[rocket::main] async fn main() { - let logger = opentelemetry_stdout::SpanExporter::default(); - let provider = TracerProvider::builder() - .with_simple_exporter(logger) - .build(); - - let telemetry = tracing_opentelemetry::layer().with_tracer( - provider.tracer("default") - ).with_tracer( - init_tracer().unwrap() - ); - let meter_provider = init_meterprovider(); - Registry::default().with(telemetry).init(); + let _opentelemetry = nanobyte_opentelemetry::init_telemetry(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); let _ = rocket_builder().launch().await; } \ No newline at end of file diff --git a/src/routes/pdf.rs b/src/routes/pdf.rs index a05c635..08ce2cf 100644 --- a/src/routes/pdf.rs +++ b/src/routes/pdf.rs @@ -1,14 +1,12 @@ -use std::{fs, thread}; -use std::{fs::File, time::Duration}; -use std::io::prelude::*; +use core::panic; +use std::{fs, io::Write}; use headless_chrome::Browser; -use headless_chrome::{types::PrintToPdfOptions, LaunchOptions}; -use rocket::{get, Response, futures::Stream, tokio::net::UnixStream, fs::NamedFile}; -use tracing::{info_span, debug, info, error, Span, instrument}; - +use headless_chrome::types::PrintToPdfOptions; +use nanobyte_opentelemetry::rocket::TracingSpan; +use rocket::{get, fs::NamedFile}; +use tracing::{info_span, info}; use crate::chromium::rocket::BrowserHolder; -use crate::tools::rocket::TracingSpan; fn generate_pdf(browser: Browser) { let tab = browser.new_tab().unwrap(); diff --git a/src/tools/mod.rs b/src/tools/mod.rs index 78211d4..e69de29 100644 --- a/src/tools/mod.rs +++ b/src/tools/mod.rs @@ -1 +0,0 @@ -pub mod rocket; \ No newline at end of file diff --git a/src/tools/rocket.rs b/src/tools/rocket.rs deleted file mode 100644 index f40cb2f..0000000 --- a/src/tools/rocket.rs +++ /dev/null @@ -1,199 +0,0 @@ - -/// Fairing for timing requests. - - -use std::time::SystemTime; -use log::debug; -use log::info; - - -use rocket::Build; -use rocket::Rocket; -use rocket::fairing; -use rocket::http::Status; -use rocket::request; -use rocket::request::FromRequest; -use rocket::request::Outcome; -use rocket::serde::{json::Json, Serialize}; -use rocket::{ - fairing::{Fairing, Info, Kind}, - Data, Request, Response, -}; - - - -use uuid::Uuid; - -use opentelemetry_sdk::trace::BatchSpanProcessor; -use tracing::Level; -use tracing_subscriber::EnvFilter; -use tracing_subscriber::prelude::*; -use tracing::{info_span, Span}; -use tracing_log::LogTracer; - -use tracing_subscriber::registry::LookupSpan; - - - use opentelemetry::trace::TracerProvider as _; - use opentelemetry_sdk::trace::TracerProvider; - use opentelemetry_stdout as stdout; - use tracing::{error, span}; - use tracing_subscriber::layer::SubscriberExt; - use tracing_subscriber::Registry; - - - use opentelemetry::{ - metrics::MeterProvider as _, - trace::{Tracer, TracerProvider as _}, - KeyValue, - }; - use opentelemetry_sdk::{ - metrics::{PeriodicReader, MeterProvider}, - runtime, - }; - -// Test - - -#[derive(Clone, Debug)] -pub struct RequestId(pub T); - -// Allows a route to access the request id -#[rocket::async_trait] -impl<'r> FromRequest<'r> for RequestId { - type Error = (); - - async fn from_request(request: &'r Request<'_>) -> Outcome { - match &*request.local_cache(|| RequestId::>(None)) { - RequestId(Some(request_id)) => Outcome::Success(RequestId(request_id.to_owned())), - RequestId(None) => request::Outcome::Error((Status::InternalServerError, ())), - } - } -} - -#[derive(Clone)] -pub struct TracingSpan( - pub T -); - -pub struct TracingFairing; - - -#[rocket::async_trait] -impl Fairing for TracingFairing { - fn info(&self) -> Info { - Info { - name: "Tracing Fairing", - kind: Kind::Request | Kind::Response | Kind::Ignite, - } - } - - - async fn on_ignite(&self, rocket: Rocket) -> fairing::Result { - Ok(rocket) - } - async fn on_request(&self, req: &mut Request<'_>, _data: &mut Data<'_>) { - let user_agent = req.headers().get_one("User-Agent").unwrap_or(""); - let request_id = req - .headers() - .get_one("X-Request-Id") - .map(ToString::to_string) - .unwrap_or_else(|| Uuid::new_v4().to_string()); - - req.local_cache(|| RequestId(Some(request_id.to_owned()))); - - let span = info_span!( - "request", - otel.name=%format!("{} {}", req.method(), req.uri().path()), - http.method = %req.method(), - http.uri = %req.uri().path(), - http.user_agent=%user_agent, - http.status_code = tracing::field::Empty, - http.request_id=%request_id, - ); - - req.local_cache(|| TracingSpan::>(Some(span))); - } - - async fn on_response<'r>(&self, req: &'r Request<'_>, res: &mut Response<'r>) { - if let Some(span) = req.local_cache(|| TracingSpan::>(None)).0.to_owned() { - let _entered_span = span.entered(); - _entered_span.record("http.status_code", &res.status().code); - - if let Some(request_id) = &req.local_cache(|| RequestId::>(None)).0 { - info!("Returning request {} with {}", request_id, res.status()); - } - - drop(_entered_span); - } - - if let Some(request_id) = &req.local_cache(|| RequestId::>(None)).0 { - res.set_raw_header("X-Request-Id", request_id); - } - } -} - -// Allows a route to access the span -#[rocket::async_trait] -impl<'r> FromRequest<'r> for TracingSpan { - type Error = (); - - async fn from_request(request: &'r Request<'_>) -> Outcome { - match &*request.local_cache(|| TracingSpan::>(None)) { - TracingSpan(Some(span)) => Outcome::Success(TracingSpan(span.to_owned())), - TracingSpan(None) => request::Outcome::Error((Status::InternalServerError, ())), - } - } -} - -pub struct RequestTimer; - -/// Value stored in request-local state. -#[derive(Copy, Clone)] -struct TimerStart(Option); - -#[rocket::async_trait] -impl Fairing for RequestTimer { - fn info(&self) -> Info { - Info { - name: "Request Timer", - kind: Kind::Request | Kind::Response - } - } - - /// Stores the start time of the request in request-local state. - async fn on_request(&self, request: &mut Request<'_>, _: &mut Data<'_>) { - // Store a `TimerStart` instead of directly storing a `SystemTime` - // to ensure that this usage doesn't conflict with anything else - // that might store a `SystemTime` in request-local cache. - request.local_cache(|| TimerStart(Some(SystemTime::now()))); - } - - /// Adds a header to the response indicating how long the server took to - /// process the request. - async fn on_response<'r>(&self, req: &'r Request<'_>, res: &mut Response<'r>) { - let start_time = req.local_cache(|| TimerStart(None)); - if let Some(Ok(duration)) = start_time.0.map(|st| st.elapsed()) { - let ms = duration.as_secs() * 1000 + duration.subsec_millis() as u64; - res.set_raw_header("X-Response-Time", format!("{} ms", ms)); - info!("Response time: {} ms", ms); - } - } -} - -/// Request guard used to retrieve the start time of a request. -#[derive(Copy, Clone)] -pub struct StartTime(pub SystemTime); - -// Allows a route to access the time a request was initiated. -#[rocket::async_trait] -impl<'r> FromRequest<'r> for StartTime { - type Error = (); - - async fn from_request(request: &'r Request<'_>) -> request::Outcome { - match *request.local_cache(|| TimerStart(None)) { - TimerStart(Some(time)) => request::Outcome::Success(StartTime(time)), - TimerStart(None) => request::Outcome::Error((Status::InternalServerError, ())), - } - } -} \ No newline at end of file