wip
This commit is contained in:
parent
b43aa68130
commit
8366a1d82e
41
Cargo.lock
generated
41
Cargo.lock
generated
@ -666,6 +666,16 @@ dependencies = [
|
|||||||
"version_check",
|
"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]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
@ -1045,6 +1055,26 @@ dependencies = [
|
|||||||
"version_check",
|
"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]]
|
[[package]]
|
||||||
name = "nu-ansi-term"
|
name = "nu-ansi-term"
|
||||||
version = "0.46.0"
|
version = "0.46.0"
|
||||||
@ -1219,22 +1249,13 @@ dependencies = [
|
|||||||
"fern",
|
"fern",
|
||||||
"headless_chrome",
|
"headless_chrome",
|
||||||
"log",
|
"log",
|
||||||
"opentelemetry",
|
"nanobyte_opentelemetry",
|
||||||
"opentelemetry-otlp",
|
|
||||||
"opentelemetry-semantic-conventions",
|
|
||||||
"opentelemetry-stdout",
|
|
||||||
"opentelemetry_sdk",
|
|
||||||
"ovlach_data",
|
"ovlach_data",
|
||||||
"rocket",
|
"rocket",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-appender",
|
|
||||||
"tracing-log",
|
|
||||||
"tracing-opentelemetry",
|
|
||||||
"tracing-subscriber",
|
|
||||||
"uuid",
|
|
||||||
"yansi 0.5.1",
|
"yansi 0.5.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
14
Cargo.toml
14
Cargo.toml
@ -21,14 +21,6 @@ tokio = { version = "1.34.0", features = ["macros"] }
|
|||||||
|
|
||||||
|
|
||||||
tracing = { version = "0.1.40", features = ["attributes", "std"] }
|
tracing = { version = "0.1.40", features = ["attributes", "std"] }
|
||||||
tracing-appender = "0.2.2"
|
yansi = "0.5.1"
|
||||||
tracing-log = "0.2.0"
|
|
||||||
tracing-subscriber = {version="0.3.17", features=["env-filter", "json", "registry", "smallvec"]}
|
nanobyte_opentelemetry = { path = "../nanobyte_opentelemetry" }
|
||||||
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"
|
|
@ -2,7 +2,7 @@ use std::{sync::Arc, time::Duration};
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use async_mutex::Mutex;
|
use async_mutex::Mutex;
|
||||||
use headless_chrome::{Browser, LaunchOptions};
|
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 rocket::{fairing::{Fairing, self}, Rocket, Build, Request, request::{FromRequest, Outcome}, futures::pin_mut, http::Status};
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
@ -55,10 +55,12 @@ impl ChromiumCoordinator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_browser_instance() -> BrowserHolder {
|
fn create_browser_instance() -> BrowserHolder {
|
||||||
BrowserHolder { browser: Browser::new(LaunchOptions {
|
trace_span!("Creating Chromium instance").in_scope(|| {
|
||||||
idle_browser_timeout: Duration::MAX, // Wait inifinity for commands
|
BrowserHolder { browser: Browser::new(LaunchOptions {
|
||||||
..LaunchOptions::default()
|
idle_browser_timeout: Duration::MAX, // Wait inifinity for commands
|
||||||
}).unwrap() }
|
..LaunchOptions::default()
|
||||||
|
}).unwrap() }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_browser(&self) {
|
fn spawn_browser(&self) {
|
||||||
|
115
src/lib.rs
115
src/lib.rs
@ -1,126 +1,15 @@
|
|||||||
use chromium::rocket::Chromium;
|
use chromium::rocket::Chromium;
|
||||||
use log::debug;
|
use nanobyte_opentelemetry::rocket::TracingFairing;
|
||||||
use rocket::{Rocket, Build, routes};
|
use rocket::{Rocket, Build, routes};
|
||||||
use tools::rocket::TracingFairing;
|
|
||||||
use tracing::Level;
|
|
||||||
use tracing_subscriber::EnvFilter;
|
|
||||||
use tracing_subscriber::prelude::*;
|
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
mod chromium;
|
mod chromium;
|
||||||
mod tools;
|
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<S> {
|
|
||||||
//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<S>() -> impl tracing_subscriber::Layer<S>
|
|
||||||
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<Build> {
|
pub fn rocket_builder() -> Rocket<Build> {
|
||||||
//let tracer_provider = init_trace();
|
|
||||||
|
|
||||||
rocket::build()
|
rocket::build()
|
||||||
.attach(TracingFairing {})
|
.attach(TracingFairing::ignite())
|
||||||
.attach(Chromium::ignite())
|
.attach(Chromium::ignite())
|
||||||
.attach(tools::rocket::RequestTimer)
|
|
||||||
.mount("/", routes![
|
.mount("/", routes![
|
||||||
routes::pdf::render_pdf_cv,
|
routes::pdf::render_pdf_cv,
|
||||||
])
|
])
|
||||||
|
62
src/main.rs
62
src/main.rs
@ -1,67 +1,7 @@
|
|||||||
use opentelemetry_otlp::WithExportConfig;
|
|
||||||
use opentelemetry_sdk::Resource;
|
|
||||||
use ovlach_pdf::rocket_builder;
|
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_sdk::trace::Tracer, TraceError> {
|
|
||||||
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]
|
#[rocket::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let logger = opentelemetry_stdout::SpanExporter::default();
|
let _opentelemetry = nanobyte_opentelemetry::init_telemetry(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
|
||||||
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 _ = rocket_builder().launch().await;
|
let _ = rocket_builder().launch().await;
|
||||||
}
|
}
|
@ -1,14 +1,12 @@
|
|||||||
use std::{fs, thread};
|
use core::panic;
|
||||||
use std::{fs::File, time::Duration};
|
use std::{fs, io::Write};
|
||||||
use std::io::prelude::*;
|
|
||||||
|
|
||||||
use headless_chrome::Browser;
|
use headless_chrome::Browser;
|
||||||
use headless_chrome::{types::PrintToPdfOptions, LaunchOptions};
|
use headless_chrome::types::PrintToPdfOptions;
|
||||||
use rocket::{get, Response, futures::Stream, tokio::net::UnixStream, fs::NamedFile};
|
use nanobyte_opentelemetry::rocket::TracingSpan;
|
||||||
use tracing::{info_span, debug, info, error, Span, instrument};
|
use rocket::{get, fs::NamedFile};
|
||||||
|
use tracing::{info_span, info};
|
||||||
use crate::chromium::rocket::BrowserHolder;
|
use crate::chromium::rocket::BrowserHolder;
|
||||||
use crate::tools::rocket::TracingSpan;
|
|
||||||
|
|
||||||
fn generate_pdf(browser: Browser) {
|
fn generate_pdf(browser: Browser) {
|
||||||
let tab = browser.new_tab().unwrap();
|
let tab = browser.new_tab().unwrap();
|
||||||
|
@ -1 +0,0 @@
|
|||||||
pub mod rocket;
|
|
@ -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<T = String>(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<Self, ()> {
|
|
||||||
match &*request.local_cache(|| RequestId::<Option<String>>(None)) {
|
|
||||||
RequestId(Some(request_id)) => Outcome::Success(RequestId(request_id.to_owned())),
|
|
||||||
RequestId(None) => request::Outcome::Error((Status::InternalServerError, ())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TracingSpan<T = Span>(
|
|
||||||
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<Build>) -> 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::<Option<Span>>(Some(span)));
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn on_response<'r>(&self, req: &'r Request<'_>, res: &mut Response<'r>) {
|
|
||||||
if let Some(span) = req.local_cache(|| TracingSpan::<Option<Span>>(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::<Option<String>>(None)).0 {
|
|
||||||
info!("Returning request {} with {}", request_id, res.status());
|
|
||||||
}
|
|
||||||
|
|
||||||
drop(_entered_span);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(request_id) = &req.local_cache(|| RequestId::<Option<String>>(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<Self, ()> {
|
|
||||||
match &*request.local_cache(|| TracingSpan::<Option<Span>>(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<SystemTime>);
|
|
||||||
|
|
||||||
#[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<Self, ()> {
|
|
||||||
match *request.local_cache(|| TimerStart(None)) {
|
|
||||||
TimerStart(Some(time)) => request::Outcome::Success(StartTime(time)),
|
|
||||||
TimerStart(None) => request::Outcome::Error((Status::InternalServerError, ())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user