telemetry ftw

This commit is contained in:
2023-08-29 21:34:01 +02:00
parent 8ed85b6f72
commit 3719dfcef6
22 changed files with 650 additions and 68 deletions

View File

@@ -5,12 +5,13 @@ use hyper::Request;
use super::models;
use super::{AppState, Error, RequestError};
#[derive(Clone)]
#[derive(Clone, Debug)]
pub enum Config {
Enabled,
Disabled { assume_user: String },
}
#[tracing::instrument(skip(state, request, next))]
pub async fn authorize<B>(
State(state): State<AppState>,
mut request: Request<B>,

View File

@@ -44,6 +44,7 @@ impl From<RequestHeaders> for HeaderName {
}
}
#[tracing::instrument]
pub fn is_htmx(headers: &HeaderMap) -> bool {
headers
.get::<HeaderName>(RequestHeaders::HtmxRequest.into())

View File

@@ -8,19 +8,20 @@ pub mod htmx;
pub mod models;
pub mod routing;
pub mod sqlite;
pub mod telemetry;
mod view;
pub use error::{CommandError, Error, RequestError, StartError};
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct AppState {
pub database_pool: sqlite::Pool<sqlite::Sqlite>,
pub client_state: ClientState,
pub auth_config: auth::Config,
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Context {
user: models::user::User,
}
@@ -31,7 +32,7 @@ impl Context {
}
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct ClientState {
pub active_category_id: Option<Uuid>,
pub edit_item: Option<Uuid>,
@@ -70,7 +71,7 @@ impl<'a> From<&'a UriPath> for &'a str {
}
}
#[derive(PartialEq, Eq)]
#[derive(PartialEq, Eq, Debug)]
pub enum TopLevelPage {
Inventory,
Trips,

View File

@@ -4,7 +4,7 @@ use std::str::FromStr;
use clap::{Parser, Subcommand};
use packager::{auth, models, routing, sqlite, AppState, ClientState, Error};
use packager::{auth, models, routing, sqlite, telemetry, AppState, ClientState, Error};
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
@@ -73,49 +73,9 @@ impl From<Error> for MainResult {
}
}
fn init_tracing() {
use std::io::stdout;
use tracing::Level;
use tracing_subscriber::{
filter::Targets,
fmt::{format::Format, Layer},
prelude::*,
registry::Registry,
};
// default is the Full format, there is no way to specify this, but it can be
// overridden via builder methods
let console_format = Format::default()
.with_ansi(true)
.with_target(true)
.with_level(true)
.json();
let console_layer = Layer::default()
.event_format(console_format)
.with_writer(stdout);
let console_level = Level::DEBUG;
let console_filter = Targets::new().with_target(env!("CARGO_PKG_NAME"), console_level);
let console_layer = if true {
console_layer.boxed()
} else {
console_layer.with_filter(console_filter).boxed()
};
let registry = Registry::default()
// just an example, you can actuall pass Options here for layers that might be
// set/unset at runtime
.with(Some(console_layer))
.with(None::<Layer<_>>);
tracing::subscriber::set_global_default(registry).unwrap();
}
#[tokio::main]
async fn main() -> MainResult {
init_tracing();
telemetry::init_tracing();
let args = Args::parse();
match args.command {
Command::Serve(serve_args) => {
@@ -140,6 +100,8 @@ async fn main() -> MainResult {
// build our application with a route
let app = routing::router(state);
let app = telemetry::init_request_tracing(app);
let addr = SocketAddr::from((
IpAddr::from_str(&serve_args.bind)
.map_err(|error| {

View File

@@ -9,6 +9,7 @@ pub struct Inventory {
}
impl Inventory {
#[tracing::instrument]
pub async fn load(ctx: &Context, pool: &sqlx::Pool<sqlx::Sqlite>) -> Result<Self, Error> {
let user_id = ctx.user.id.to_string();
let mut categories = sqlx::query_as!(
@@ -64,6 +65,7 @@ impl TryFrom<DbCategoryRow> for Category {
}
impl Category {
#[tracing::instrument]
pub async fn _find(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -90,6 +92,7 @@ impl Category {
.transpose()
}
#[tracing::instrument]
pub async fn save(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -113,16 +116,19 @@ impl Category {
Ok(id)
}
#[tracing::instrument]
pub fn items(&self) -> &Vec<Item> {
self.items
.as_ref()
.expect("you need to call populate_items()")
}
#[tracing::instrument]
pub fn total_weight(&self) -> i64 {
self.items().iter().map(|item| item.weight).sum()
}
#[tracing::instrument]
pub async fn populate_items(
&mut self,
ctx: &Context,
@@ -157,6 +163,7 @@ impl Category {
}
}
#[derive(Debug)]
pub struct Product {
pub id: Uuid,
pub name: String,
@@ -164,6 +171,7 @@ pub struct Product {
pub comment: Option<String>,
}
#[derive(Debug)]
pub struct InventoryItem {
pub id: Uuid,
pub name: String,
@@ -218,6 +226,7 @@ impl TryFrom<DbInventoryItemRow> for InventoryItem {
}
impl InventoryItem {
#[tracing::instrument]
pub async fn find(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -257,6 +266,7 @@ impl InventoryItem {
.transpose()
}
#[tracing::instrument]
pub async fn name_exists(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -278,6 +288,7 @@ impl InventoryItem {
.is_some())
}
#[tracing::instrument]
pub async fn delete(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -299,6 +310,7 @@ impl InventoryItem {
Ok(results.rows_affected() != 0)
}
#[tracing::instrument]
pub async fn update(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -330,6 +342,7 @@ impl InventoryItem {
.await??)
}
#[tracing::instrument]
pub async fn save(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -360,6 +373,7 @@ impl InventoryItem {
Ok(id)
}
#[tracing::instrument]
pub async fn get_category_max_weight(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -424,6 +438,7 @@ impl TryFrom<DbInventoryItemsRow> for Item {
}
impl Item {
#[tracing::instrument]
pub async fn _get_category_total_picked_weight(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,

View File

@@ -14,7 +14,7 @@ use serde_variant::to_variant_name;
use time;
use uuid::Uuid;
#[derive(sqlx::Type, PartialEq, PartialOrd, Deserialize)]
#[derive(sqlx::Type, PartialEq, PartialOrd, Deserialize, Debug)]
pub enum TripState {
Init,
Planning,
@@ -118,6 +118,7 @@ pub struct TripCategory {
}
impl TripCategory {
#[tracing::instrument]
pub fn total_picked_weight(&self) -> i64 {
self.items
.as_ref()
@@ -128,6 +129,7 @@ impl TripCategory {
.sum()
}
#[tracing::instrument]
pub async fn find(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -288,6 +290,7 @@ impl TryFrom<DbTripsItemsRow> for TripItem {
}
impl TripItem {
#[tracing::instrument]
pub async fn find(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -327,6 +330,7 @@ impl TripItem {
.transpose()
}
#[tracing::instrument]
pub async fn set_state(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -391,6 +395,7 @@ impl TryFrom<DbTripRow> for Trip {
}
}
#[derive(Debug)]
pub struct Trip {
pub id: Uuid,
pub name: String,
@@ -426,6 +431,7 @@ pub(crate) struct DbTripWeightRow {
}
impl Trip {
#[tracing::instrument]
pub async fn all(ctx: &Context, pool: &sqlx::Pool<sqlx::Sqlite>) -> Result<Vec<Trip>, Error> {
let user_id = ctx.user.id.to_string();
sqlx::query_as!(
@@ -452,6 +458,7 @@ impl Trip {
.collect::<Result<Vec<Trip>, Error>>()
}
#[tracing::instrument]
pub async fn find(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -482,6 +489,7 @@ impl Trip {
.transpose()
}
#[tracing::instrument]
pub async fn trip_type_remove(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -512,6 +520,7 @@ impl Trip {
Ok(results.rows_affected() != 0)
}
#[tracing::instrument]
pub async fn trip_type_add(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -545,6 +554,7 @@ impl Trip {
Ok(())
}
#[tracing::instrument]
pub async fn set_state(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -567,6 +577,7 @@ impl Trip {
Ok(result.rows_affected() != 0)
}
#[tracing::instrument]
pub async fn set_comment(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -589,6 +600,7 @@ impl Trip {
Ok(result.rows_affected() != 0)
}
#[tracing::instrument]
pub async fn set_attribute(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -616,6 +628,7 @@ impl Trip {
})
}
#[tracing::instrument]
pub async fn save(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -649,6 +662,7 @@ impl Trip {
Ok(id)
}
#[tracing::instrument]
pub async fn find_total_picked_weight(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -680,18 +694,21 @@ impl Trip {
Ok(weight)
}
#[tracing::instrument]
pub fn types(&self) -> &Vec<TripType> {
self.types
.as_ref()
.expect("you need to call load_trips_types()")
}
#[tracing::instrument]
pub fn categories(&self) -> &Vec<TripCategory> {
self.categories
.as_ref()
.expect("you need to call load_trips_types()")
}
#[tracing::instrument]
pub fn total_picked_weight(&self) -> i64 {
self.categories()
.iter()
@@ -707,6 +724,7 @@ impl Trip {
.sum::<i64>()
}
#[tracing::instrument]
pub async fn load_trips_types(
&mut self,
ctx: &Context,
@@ -758,6 +776,7 @@ impl Trip {
Ok(())
}
#[tracing::instrument]
pub async fn sync_trip_items_with_inventory(
&mut self,
ctx: &Context,
@@ -838,6 +857,7 @@ impl Trip {
Ok(())
}
#[tracing::instrument]
pub async fn load_categories(
&mut self,
ctx: &Context,
@@ -955,6 +975,7 @@ impl Trip {
}
}
#[derive(Debug)]
pub struct TripType {
pub id: Uuid,
pub name: String,
@@ -962,6 +983,7 @@ pub struct TripType {
}
impl TripsType {
#[tracing::instrument]
pub async fn all(ctx: &Context, pool: &sqlx::Pool<sqlx::Sqlite>) -> Result<Vec<Self>, Error> {
let user_id = ctx.user.id.to_string();
sqlx::query_as!(
@@ -981,6 +1003,7 @@ impl TripsType {
.collect::<Result<Vec<Self>, Error>>()
}
#[tracing::instrument]
pub async fn save(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -1004,6 +1027,7 @@ impl TripsType {
Ok(id)
}
#[tracing::instrument]
pub async fn set_name(
ctx: &Context,
pool: &sqlx::Pool<sqlx::Sqlite>,
@@ -1039,6 +1063,7 @@ pub enum TripTypeAttribute {
Name,
}
#[derive(Debug)]
pub struct TripsType {
pub id: Uuid,
pub name: String,

View File

@@ -8,6 +8,7 @@ pub struct User {
pub fullname: String,
}
#[derive(Debug)]
pub struct NewUser<'a> {
pub username: &'a str,
pub fullname: &'a str,
@@ -33,6 +34,7 @@ impl TryFrom<DbUserRow> for User {
}
impl User {
#[tracing::instrument]
pub async fn find_by_name(
pool: &sqlx::Pool<sqlx::Sqlite>,
name: &str,
@@ -49,6 +51,7 @@ impl User {
}
}
#[tracing::instrument]
pub async fn create(pool: &sqlx::Pool<sqlx::Sqlite>, user: NewUser<'_>) -> Result<Uuid, Error> {
let id = Uuid::new_v4();
let id_param = id.to_string();

View File

@@ -5,9 +5,6 @@ use axum::{
Router,
};
use tower_http::trace;
use tracing::Level;
use crate::{AppState, Error, RequestError, TopLevelPage};
use super::auth;
@@ -16,6 +13,7 @@ mod html;
mod routes;
use routes::*;
#[tracing::instrument]
fn get_referer(headers: &HeaderMap) -> Result<&str, Error> {
headers
.get("referer")
@@ -28,6 +26,7 @@ fn get_referer(headers: &HeaderMap) -> Result<&str, Error> {
})
}
#[tracing::instrument]
pub fn router(state: AppState) -> Router {
Router::new()
.route("/favicon.svg", get(icon))
@@ -126,9 +125,4 @@ pub fn router(state: AppState) -> Router {
})
})
.with_state(state)
.layer(
trace::TraceLayer::new_for_http()
.make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO))
.on_response(trace::DefaultOnResponse::new().level(Level::INFO)),
)
}

View File

@@ -15,12 +15,12 @@ use crate::{AppState, Context, Error, RequestError, TopLevelPage};
use super::{get_referer, html};
#[derive(Deserialize, Default)]
#[derive(Deserialize, Default, Debug)]
pub struct InventoryQuery {
edit_item: Option<Uuid>,
}
#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
pub struct NewItem {
#[serde(rename = "new-item-name")]
name: String,
@@ -32,13 +32,13 @@ pub struct NewItem {
category_id: Uuid,
}
#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
pub struct NewItemName {
#[serde(rename = "new-item-name")]
name: String,
}
#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
pub struct EditItem {
#[serde(rename = "edit-item-name")]
name: String,
@@ -46,7 +46,7 @@ pub struct EditItem {
weight: u32,
}
#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
pub struct NewTrip {
#[serde(rename = "new-trip-name")]
name: String,
@@ -62,19 +62,19 @@ pub struct TripQuery {
category: Option<Uuid>,
}
#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
pub struct CommentUpdate {
#[serde(rename = "new-comment")]
new_comment: String,
}
#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
pub struct TripUpdate {
#[serde(rename = "new-value")]
new_value: String,
}
#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
pub struct NewCategory {
#[serde(rename = "new-category-name")]
name: String,
@@ -85,18 +85,19 @@ pub struct TripTypeQuery {
edit: Option<Uuid>,
}
#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
pub struct NewTripType {
#[serde(rename = "new-trip-type-name")]
name: String,
}
#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
pub struct TripTypeUpdate {
#[serde(rename = "new-value")]
new_value: String,
}
#[tracing::instrument]
pub async fn root(Extension(current_user): Extension<models::user::User>) -> impl IntoResponse {
view::Root::build(
&Context::build(current_user),
@@ -105,6 +106,7 @@ pub async fn root(Extension(current_user): Extension<models::user::User>) -> imp
)
}
#[tracing::instrument]
pub async fn icon() -> impl IntoResponse {
(
[(header::CONTENT_TYPE, "image/svg+xml")],
@@ -112,6 +114,7 @@ pub async fn icon() -> impl IntoResponse {
)
}
#[tracing::instrument]
pub async fn debug(headers: HeaderMap) -> impl IntoResponse {
let mut out = String::new();
for (key, value) in headers.iter() {
@@ -120,6 +123,7 @@ pub async fn debug(headers: HeaderMap) -> impl IntoResponse {
out
}
#[tracing::instrument]
pub async fn inventory_active(
Extension(current_user): Extension<models::user::User>,
State(mut state): State<AppState>,
@@ -157,6 +161,7 @@ pub async fn inventory_active(
))
}
#[tracing::instrument]
pub async fn inventory_inactive(
Extension(current_user): Extension<models::user::User>,
State(mut state): State<AppState>,
@@ -179,6 +184,7 @@ pub async fn inventory_inactive(
))
}
#[tracing::instrument]
pub async fn inventory_item_validate_name(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -195,6 +201,7 @@ pub async fn inventory_item_validate_name(
))
}
#[tracing::instrument]
pub async fn inventory_item_create(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -244,6 +251,8 @@ pub async fn inventory_item_create(
.into_response())
}
}
#[tracing::instrument]
pub async fn inventory_item_delete(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -262,6 +271,7 @@ pub async fn inventory_item_delete(
}
}
#[tracing::instrument]
pub async fn inventory_item_edit(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -287,6 +297,7 @@ pub async fn inventory_item_edit(
Ok(Redirect::to(&format!("/inventory/category/{id}/")))
}
#[tracing::instrument]
pub async fn inventory_item_cancel(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -305,6 +316,7 @@ pub async fn inventory_item_cancel(
)))
}
#[tracing::instrument]
pub async fn trip_create(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -329,6 +341,7 @@ pub async fn trip_create(
Ok(Redirect::to(&format!("/trips/{new_id}/")))
}
#[tracing::instrument]
pub async fn trips(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -343,6 +356,7 @@ pub async fn trips(
))
}
#[tracing::instrument]
pub async fn trip(
Extension(current_user): Extension<models::user::User>,
State(mut state): State<AppState>,
@@ -390,6 +404,7 @@ pub async fn trip(
))
}
#[tracing::instrument]
pub async fn trip_type_remove(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -408,6 +423,7 @@ pub async fn trip_type_remove(
}
}
#[tracing::instrument]
pub async fn trip_type_add(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -419,6 +435,7 @@ pub async fn trip_type_add(
Ok(Redirect::to(&format!("/trips/{trip_id}/")))
}
#[tracing::instrument]
pub async fn trip_comment_set(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -443,6 +460,7 @@ pub async fn trip_comment_set(
}
}
#[tracing::instrument]
pub async fn trip_edit_attribute(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -467,6 +485,7 @@ pub async fn trip_edit_attribute(
Ok(Redirect::to(&format!("/trips/{trip_id}/")))
}
#[tracing::instrument]
pub async fn trip_item_set_state(
ctx: &Context,
state: &AppState,
@@ -480,6 +499,7 @@ pub async fn trip_item_set_state(
Ok(())
}
#[tracing::instrument]
pub async fn trip_row(
ctx: &Context,
state: &AppState,
@@ -524,6 +544,7 @@ pub async fn trip_row(
Ok(html::concat(&item_row, &category_row))
}
#[tracing::instrument]
pub async fn trip_item_set_pick(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -545,6 +566,7 @@ pub async fn trip_item_set_pick(
.map(|_| -> Result<Redirect, Error> { Ok(Redirect::to(get_referer(&headers)?)) })?
}
#[tracing::instrument]
pub async fn trip_item_set_pick_htmx(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -568,6 +590,7 @@ pub async fn trip_item_set_pick_htmx(
Ok((headers, trip_row(&ctx, &state, trip_id, item_id).await?))
}
#[tracing::instrument]
pub async fn trip_item_set_unpick(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -589,6 +612,7 @@ pub async fn trip_item_set_unpick(
.map(|_| -> Result<Redirect, Error> { Ok(Redirect::to(get_referer(&headers)?)) })?
}
#[tracing::instrument]
pub async fn trip_item_set_unpick_htmx(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -612,6 +636,7 @@ pub async fn trip_item_set_unpick_htmx(
Ok((headers, trip_row(&ctx, &state, trip_id, item_id).await?))
}
#[tracing::instrument]
pub async fn trip_item_set_pack(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -633,6 +658,7 @@ pub async fn trip_item_set_pack(
.map(|_| -> Result<Redirect, Error> { Ok(Redirect::to(get_referer(&headers)?)) })?
}
#[tracing::instrument]
pub async fn trip_item_set_pack_htmx(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -656,6 +682,7 @@ pub async fn trip_item_set_pack_htmx(
Ok((headers, trip_row(&ctx, &state, trip_id, item_id).await?))
}
#[tracing::instrument]
pub async fn trip_item_set_unpack(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -677,6 +704,7 @@ pub async fn trip_item_set_unpack(
.map(|_| -> Result<Redirect, Error> { Ok(Redirect::to(get_referer(&headers)?)) })?
}
#[tracing::instrument]
pub async fn trip_item_set_unpack_htmx(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -700,6 +728,7 @@ pub async fn trip_item_set_unpack_htmx(
Ok((headers, trip_row(&ctx, &state, trip_id, item_id).await?))
}
#[tracing::instrument]
pub async fn trip_item_set_ready(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -721,6 +750,7 @@ pub async fn trip_item_set_ready(
.map(|_| -> Result<Redirect, Error> { Ok(Redirect::to(get_referer(&headers)?)) })?
}
#[tracing::instrument]
pub async fn trip_item_set_ready_htmx(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -744,6 +774,7 @@ pub async fn trip_item_set_ready_htmx(
Ok((headers, trip_row(&ctx, &state, trip_id, item_id).await?))
}
#[tracing::instrument]
pub async fn trip_item_set_unready(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -765,6 +796,7 @@ pub async fn trip_item_set_unready(
.map(|_| -> Result<Redirect, Error> { Ok(Redirect::to(get_referer(&headers)?)) })?
}
#[tracing::instrument]
pub async fn trip_item_set_unready_htmx(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -788,6 +820,7 @@ pub async fn trip_item_set_unready_htmx(
Ok((headers, trip_row(&ctx, &state, trip_id, item_id).await?))
}
#[tracing::instrument]
pub async fn trip_total_weight_htmx(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -802,6 +835,7 @@ pub async fn trip_total_weight_htmx(
))
}
#[tracing::instrument]
pub async fn inventory_category_create(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -820,6 +854,7 @@ pub async fn inventory_category_create(
Ok(Redirect::to("/inventory/"))
}
#[tracing::instrument]
pub async fn trip_state_set(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -842,6 +877,8 @@ pub async fn trip_state_set(
Ok(Redirect::to(&format!("/trips/{trip_id}/")).into_response())
}
}
#[tracing::instrument]
pub async fn trips_types(
Extension(current_user): Extension<models::user::User>,
State(mut state): State<AppState>,
@@ -859,6 +896,8 @@ pub async fn trips_types(
Some(&TopLevelPage::Trips),
))
}
#[tracing::instrument]
pub async fn trip_type_create(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -876,6 +915,8 @@ pub async fn trip_type_create(
Ok(Redirect::to("/trips/types/"))
}
#[tracing::instrument]
pub async fn trips_types_edit_name(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -906,6 +947,7 @@ pub async fn trips_types_edit_name(
}
}
#[tracing::instrument]
pub async fn inventory_item(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -925,6 +967,7 @@ pub async fn inventory_item(
))
}
#[tracing::instrument]
pub async fn trip_category_select(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -959,6 +1002,7 @@ pub async fn trip_category_select(
))
}
#[tracing::instrument]
pub async fn inventory_category_select(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -995,6 +1039,7 @@ pub async fn inventory_category_select(
))
}
#[tracing::instrument]
pub async fn trip_packagelist(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -1016,6 +1061,7 @@ pub async fn trip_packagelist(
))
}
#[tracing::instrument]
pub async fn trip_item_packagelist_set_pack_htmx(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -1043,6 +1089,7 @@ pub async fn trip_item_packagelist_set_pack_htmx(
))
}
#[tracing::instrument]
pub async fn trip_item_packagelist_set_unpack_htmx(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -1072,6 +1119,7 @@ pub async fn trip_item_packagelist_set_unpack_htmx(
))
}
#[tracing::instrument]
pub async fn trip_item_packagelist_set_ready_htmx(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,
@@ -1099,6 +1147,7 @@ pub async fn trip_item_packagelist_set_ready_htmx(
))
}
#[tracing::instrument]
pub async fn trip_item_packagelist_set_unready_htmx(
Extension(current_user): Extension<models::user::User>,
State(state): State<AppState>,

View File

@@ -8,6 +8,7 @@ use std::str::FromStr as _;
use crate::StartError;
#[tracing::instrument]
pub async fn init_database_pool(url: &str) -> Result<Pool<Sqlite>, StartError> {
Ok(SqlitePoolOptions::new()
.max_connections(5)
@@ -20,6 +21,7 @@ pub async fn init_database_pool(url: &str) -> Result<Pool<Sqlite>, StartError> {
.await?)
}
#[tracing::instrument]
pub async fn migrate(url: &str) -> Result<(), StartError> {
let pool = SqlitePoolOptions::new()
.max_connections(5)

157
rust/src/telemetry.rs Normal file
View File

@@ -0,0 +1,157 @@
use std::fmt;
use std::io;
use std::time::Duration;
use axum::Router;
use http::Request;
use tower_http::{classify::ServerErrorsFailureClass, trace::TraceLayer};
use tracing::{Level, Span};
use tracing_subscriber::{
filter::{LevelFilter, Targets},
fmt::{format::Format, Layer},
layer::SubscriberExt,
prelude::*,
registry::Registry,
};
use uuid::Uuid;
use opentelemetry::global;
pub fn otel_init(f: impl FnOnce() -> ()) {
f()
}
pub fn init_tracing() {
// default is the Full format, there is no way to specify this, but it can be
// overridden via builder methods
let stdout_format = Format::default()
.pretty()
.with_ansi(true)
.with_target(true)
.with_level(true)
.with_file(false);
let stdout_layer = Layer::default()
.event_format(stdout_format)
.with_writer(io::stdout);
let stdout_filter = Targets::new()
.with_default(LevelFilter::OFF)
.with_targets(vec![
(env!("CARGO_PKG_NAME"), Level::DEBUG),
// this is for axum requests
("request", Level::DEBUG),
// required for tokio-console as by the docs
// ("tokio", Level::TRACE),
// ("runtime", Level::TRACE),
]);
let stdout_layer = stdout_layer.with_filter(stdout_filter);
let console_layer = console_subscriber::Builder::default().spawn();
global::set_text_map_propagator(opentelemetry_jaeger::Propagator::new());
// Sets up the machinery needed to export data to Jaeger
// There are other OTel crates that provide pipelines for the vendors
// mentioned earlier.
let tracer = opentelemetry_jaeger::new_agent_pipeline()
.with_service_name(env!("CARGO_PKG_NAME"))
.install_simple()
.unwrap();
let opentelemetry_filter = Targets::new()
.with_default(LevelFilter::OFF)
.with_targets(vec![
(env!("CARGO_PKG_NAME"), Level::DEBUG),
// this is for axum requests
("request", Level::DEBUG),
// required for tokio-console as by the docs
// ("tokio", Level::TRACE),
// ("runtime", Level::TRACE),
]);
let opentelemetry = tracing_opentelemetry::layer()
.with_tracer(tracer)
.with_filter(opentelemetry_filter);
let registry = Registry::default()
.with(console_layer)
.with(opentelemetry)
// just an example, you can actuall pass Options here for layers that might be
// set/unset at runtime
.with(Some(stdout_layer))
.with(None::<Layer<_>>);
tracing::subscriber::set_global_default(registry).unwrap();
}
struct Latency(Duration);
impl fmt::Display for Latency {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0.as_micros())
}
}
pub fn init_request_tracing(router: Router) -> Router {
router.layer(
TraceLayer::new_for_http()
.make_span_with(|_request: &Request<_>| {
let request_id = Uuid::new_v4();
tracing::debug_span!(
target: "request",
"request",
%request_id,
)
})
.on_request(|request: &Request<_>, _span: &Span| {
let request_headers = request.headers();
let http_version = request.version();
tracing::debug!(
target: "request",
method = request.method().as_str(),
path = request.uri().path(),
?http_version,
?request_headers,
"request received",
);
})
.on_response(
|response: &axum::response::Response, latency: Duration, _span: &Span| {
let response_headers = response.headers();
let latency = Latency(latency);
tracing::debug!(
target: "request",
%latency,
status = response.status().as_str(),
?response_headers,
"finished processing request",
);
},
)
.on_failure(
|error: ServerErrorsFailureClass, latency: Duration, _span: &Span| {
let latency = Latency(latency);
match error {
ServerErrorsFailureClass::StatusCode(code) => {
tracing::error!(
target: "request",
%latency,
"request failed with error response {}",
code,
);
}
ServerErrorsFailureClass::Error(message) => {
tracing::error!(
target: "request",
%latency,
"request failed: {}",
message,
);
}
}
},
),
)
}

View File

@@ -3,6 +3,7 @@ use maud::{html, Markup};
pub struct Home;
impl Home {
#[tracing::instrument]
pub fn build() -> Markup {
html!(
div

View File

@@ -7,6 +7,7 @@ use uuid::Uuid;
pub struct Inventory;
impl Inventory {
#[tracing::instrument]
pub fn build(
active_category: Option<&models::inventory::Category>,
categories: &Vec<models::inventory::Category>,
@@ -36,6 +37,7 @@ impl Inventory {
pub struct InventoryCategoryList;
impl InventoryCategoryList {
#[tracing::instrument]
pub fn build(
active_category: Option<&models::inventory::Category>,
categories: &Vec<models::inventory::Category>,
@@ -142,6 +144,7 @@ impl InventoryCategoryList {
pub struct InventoryItemList;
impl InventoryItemList {
#[tracing::instrument]
pub fn build(edit_item_id: Option<Uuid>, items: &Vec<models::inventory::Item>) -> Markup {
let biggest_item_weight: i64 = items.iter().map(|item| item.weight).max().unwrap_or(1);
html!(
@@ -317,6 +320,7 @@ impl InventoryItemList {
pub struct InventoryNewItemFormName;
impl InventoryNewItemFormName {
#[tracing::instrument]
pub fn build(value: Option<&str>, error: bool) -> Markup {
html!(
div
@@ -364,6 +368,7 @@ impl InventoryNewItemFormName {
pub struct InventoryNewItemFormWeight;
impl InventoryNewItemFormWeight {
#[tracing::instrument]
pub fn build() -> Markup {
html!(
div
@@ -406,6 +411,7 @@ impl InventoryNewItemFormWeight {
pub struct InventoryNewItemFormCategory;
impl InventoryNewItemFormCategory {
#[tracing::instrument]
pub fn build(
active_category: Option<&models::inventory::Category>,
categories: &Vec<models::inventory::Category>,
@@ -446,6 +452,7 @@ impl InventoryNewItemFormCategory {
pub struct InventoryNewItemForm;
impl InventoryNewItemForm {
#[tracing::instrument]
pub fn build(
active_category: Option<&models::inventory::Category>,
categories: &Vec<models::inventory::Category>,
@@ -492,6 +499,7 @@ impl InventoryNewItemForm {
pub struct InventoryNewCategoryForm;
impl InventoryNewCategoryForm {
#[tracing::instrument]
pub fn build() -> Markup {
html!(
form
@@ -546,6 +554,7 @@ impl InventoryNewCategoryForm {
pub struct InventoryItem;
impl InventoryItem {
#[tracing::instrument]
pub fn build(_state: &ClientState, item: &models::inventory::InventoryItem) -> Markup {
html!(
div ."p-8" {

View File

@@ -11,6 +11,7 @@ pub struct Root;
use crate::TopLevelPage;
impl Root {
#[tracing::instrument]
pub fn build(context: &Context, body: &Markup, active_page: Option<&TopLevelPage>) -> Markup {
let menu_item = |item: TopLevelPage, active_page: Option<&TopLevelPage>| {
let active = active_page.map_or(false, |page| *page == item);
@@ -116,6 +117,7 @@ impl Root {
pub struct ErrorPage;
impl ErrorPage {
#[tracing::instrument]
pub fn build(message: &str) -> Markup {
html!(
(DOCTYPE)

View File

@@ -12,6 +12,7 @@ pub mod packagelist;
pub mod types;
impl TripManager {
#[tracing::instrument]
pub fn build(trips: Vec<models::trips::Trip>) -> Markup {
html!(
div
@@ -28,6 +29,7 @@ impl TripManager {
}
}
#[derive(Debug)]
pub enum InputType {
Text,
Number,
@@ -35,6 +37,7 @@ pub enum InputType {
}
impl From<InputType> for &'static str {
#[tracing::instrument]
fn from(value: InputType) -> &'static str {
match value {
InputType::Text => "text",
@@ -58,6 +61,7 @@ fn trip_state_icon(state: &models::trips::TripState) -> &'static str {
pub struct TripTable;
impl TripTable {
#[tracing::instrument]
pub fn build(trips: Vec<models::trips::Trip>) -> Markup {
html!(
table
@@ -101,6 +105,7 @@ impl TripTable {
pub struct TripTableRow;
impl TripTableRow {
#[tracing::instrument(skip(value))]
pub fn build(trip_id: Uuid, value: impl maud::Render) -> Markup {
html!(
td ."border" ."p-0" ."m-0" {
@@ -120,6 +125,7 @@ impl TripTableRow {
pub struct NewTrip;
impl NewTrip {
#[tracing::instrument]
pub fn build() -> Markup {
html!(
form
@@ -215,6 +221,7 @@ impl NewTrip {
pub struct Trip;
impl Trip {
#[tracing::instrument]
pub fn build(
trip: &models::trips::Trip,
trip_edit_attribute: Option<&models::trips::TripAttribute>,
@@ -342,9 +349,10 @@ impl Trip {
pub struct TripInfoRow;
impl TripInfoRow {
#[tracing::instrument]
pub fn build(
name: &str,
value: Option<impl std::fmt::Display>,
value: Option<impl std::fmt::Display + std::fmt::Debug>,
attribute_key: &models::trips::TripAttribute,
edit_attribute: Option<&models::trips::TripAttribute>,
input_type: InputType,
@@ -465,6 +473,7 @@ impl TripInfoRow {
pub struct TripInfoTotalWeightRow;
impl TripInfoTotalWeightRow {
#[tracing::instrument]
pub fn build(trip_id: Uuid, value: i64) -> Markup {
html!(
span
@@ -482,6 +491,7 @@ impl TripInfoTotalWeightRow {
pub struct TripInfoStateRow;
impl TripInfoStateRow {
#[tracing::instrument]
pub fn build(trip_state: &models::trips::TripState) -> Markup {
let prev_state = trip_state.prev();
let next_state = trip_state.next();
@@ -586,6 +596,7 @@ impl TripInfoStateRow {
pub struct TripInfo;
impl TripInfo {
#[tracing::instrument]
pub fn build(
trip_edit_attribute: Option<&models::trips::TripAttribute>,
trip: &models::trips::Trip,
@@ -755,6 +766,7 @@ impl TripInfo {
pub struct TripComment;
impl TripComment {
#[tracing::instrument]
pub fn build(trip: &models::trips::Trip) -> Markup {
html!(
div
@@ -809,6 +821,7 @@ impl TripComment {
pub struct TripItems;
impl TripItems {
#[tracing::instrument]
pub fn build(
active_category: Option<&models::trips::TripCategory>,
trip: &models::trips::Trip,
@@ -836,6 +849,7 @@ impl TripItems {
pub struct TripCategoryListRow;
impl TripCategoryListRow {
#[tracing::instrument]
pub fn build(
trip_id: Uuid,
category: &models::trips::TripCategory,
@@ -944,6 +958,7 @@ impl TripCategoryListRow {
pub struct TripCategoryList;
impl TripCategoryList {
#[tracing::instrument]
pub fn build(
active_category: Option<&models::trips::TripCategory>,
trip: &models::trips::Trip,
@@ -1000,6 +1015,7 @@ impl TripCategoryList {
pub struct TripItemList;
impl TripItemList {
#[tracing::instrument]
pub fn build(trip_id: Uuid, items: &Vec<models::trips::TripItem>) -> Markup {
let biggest_item_weight: i64 = items.iter().map(|item| item.item.weight).max().unwrap_or(1);
@@ -1039,6 +1055,7 @@ impl TripItemList {
pub struct TripItemListRow;
impl TripItemListRow {
#[tracing::instrument]
pub fn build(
trip_id: Uuid,
item: &models::trips::TripItem,

View File

@@ -6,6 +6,7 @@ use crate::models;
pub struct TripPackageListRowReady;
impl TripPackageListRowReady {
#[tracing::instrument]
pub fn build(trip_id: Uuid, item: &models::trips::TripItem) -> Markup {
html!(
li
@@ -82,6 +83,7 @@ impl TripPackageListRowReady {
pub struct TripPackageListRowUnready;
impl TripPackageListRowUnready {
#[tracing::instrument]
pub fn build(trip_id: Uuid, item: &models::trips::TripItem) -> Markup {
html!(
li
@@ -158,6 +160,7 @@ impl TripPackageListRowUnready {
pub struct TripPackageListCategoryBlockReady;
impl TripPackageListCategoryBlockReady {
#[tracing::instrument]
pub fn build(trip: &models::trips::Trip, category: &models::trips::TripCategory) -> Markup {
let empty = !category
.items
@@ -214,6 +217,7 @@ impl TripPackageListCategoryBlockReady {
pub struct TripPackageListCategoryBlockUnready;
impl TripPackageListCategoryBlockUnready {
#[tracing::instrument]
pub fn build(trip: &models::trips::Trip, category: &models::trips::TripCategory) -> Markup {
let empty = !category
.items
@@ -269,6 +273,7 @@ impl TripPackageListCategoryBlockUnready {
pub struct TripPackageList;
impl TripPackageList {
#[tracing::instrument]
pub fn build(trip: &models::trips::Trip) -> Markup {
// let all_packed = trip.categories().iter().all(|category| {
// category

View File

@@ -5,6 +5,7 @@ use maud::{html, Markup};
pub struct TypeList;
impl TypeList {
#[tracing::instrument]
pub fn build(state: &ClientState, trip_types: Vec<models::trips::TripsType>) -> Markup {
html!(
div ."p-8" ."flex" ."flex-col" ."gap-8" {