1
0
Fork 0

Compare commits

...

2 commits

Author SHA1 Message Date
Adrian Hedqvist 20f4ae0658 cargo fmt 2023-04-02 15:27:06 +02:00
Adrian Hedqvist 5dd01a5d4c telemetry & metrics stuff 2023-04-02 15:26:20 +02:00
8 changed files with 311 additions and 68 deletions

218
Cargo.lock generated
View file

@ -74,7 +74,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.11",
"syn 2.0.13",
]
[[package]]
@ -375,6 +375,25 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
dependencies = [
"cfg-if",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
@ -409,7 +428,7 @@ dependencies = [
"proc-macro2",
"quote",
"scratch",
"syn 2.0.11",
"syn 2.0.13",
]
[[package]]
@ -426,7 +445,7 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.11",
"syn 2.0.13",
]
[[package]]
@ -464,6 +483,19 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "dashmap"
version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
dependencies = [
"cfg-if",
"hashbrown 0.12.3",
"lock_api",
"once_cell",
"parking_lot_core",
]
[[package]]
name = "deunicode"
version = "0.4.3"
@ -538,9 +570,9 @@ dependencies = [
[[package]]
name = "futures"
version = "0.3.27"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549"
checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
dependencies = [
"futures-channel",
"futures-core",
@ -552,9 +584,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.27"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac"
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
dependencies = [
"futures-core",
"futures-sink",
@ -562,35 +594,58 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.27"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd"
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
[[package]]
name = "futures-executor"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.27"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91"
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
[[package]]
name = "futures-macro"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.13",
]
[[package]]
name = "futures-sink"
version = "0.3.27"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2"
checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
[[package]]
name = "futures-task"
version = "0.3.27"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879"
checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
[[package]]
name = "futures-util"
version = "0.3.27"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab"
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
dependencies = [
"futures-core",
"futures-macro",
"futures-sink",
"futures-task",
"pin-project-lite",
@ -968,6 +1023,15 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "matchers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
"regex-automata",
]
[[package]]
name = "matchit"
version = "0.7.0"
@ -1071,6 +1135,52 @@ version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "opentelemetry"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69d6c3d7288a106c0a363e4b0e8d308058d56902adefb16f4936f417ffef086e"
dependencies = [
"opentelemetry_api",
"opentelemetry_sdk",
]
[[package]]
name = "opentelemetry_api"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c24f96e21e7acc813c7a8394ee94978929db2bcc46cf6b5014fc612bf7760c22"
dependencies = [
"fnv",
"futures-channel",
"futures-util",
"indexmap",
"js-sys",
"once_cell",
"pin-project-lite",
"thiserror",
]
[[package]]
name = "opentelemetry_sdk"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ca41c4933371b61c2a2f214bf16931499af4ec90543604ec828f7a625c09113"
dependencies = [
"async-trait",
"crossbeam-channel",
"dashmap",
"fnv",
"futures-channel",
"futures-executor",
"futures-util",
"once_cell",
"opentelemetry_api",
"percent-encoding",
"rand",
"thiserror",
]
[[package]]
name = "overload"
version = "0.1.1"
@ -1123,9 +1233,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "pest"
version = "2.5.6"
version = "2.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cbd939b234e95d72bc393d51788aec68aeeb5d51e748ca08ff3aad58cb722f7"
checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122"
dependencies = [
"thiserror",
"ucd-trie",
@ -1133,9 +1243,9 @@ dependencies = [
[[package]]
name = "pest_derive"
version = "2.5.6"
version = "2.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a81186863f3d0a27340815be8f2078dd8050b14cd71913db9fbda795e5f707d7"
checksum = "be99c4c1d2fc2769b1d00239431d711d08f6efedcecb8b6e30707160aee99c15"
dependencies = [
"pest",
"pest_generator",
@ -1143,22 +1253,22 @@ dependencies = [
[[package]]
name = "pest_generator"
version = "2.5.6"
version = "2.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75a1ef20bf3193c15ac345acb32e26b3dc3223aff4d77ae4fc5359567683796b"
checksum = "e56094789873daa36164de2e822b3888c6ae4b4f9da555a1103587658c805b1e"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn 1.0.109",
"syn 2.0.13",
]
[[package]]
name = "pest_meta"
version = "2.5.6"
version = "2.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e3b284b1f13a20dc5ebc90aff59a51b8d7137c221131b52a7260c08cbc1cc80"
checksum = "6733073c7cff3d8459fda0e42f13a047870242aed8b509fe98000928975f359e"
dependencies = [
"once_cell",
"pest",
@ -1250,9 +1360,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.54"
version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534"
checksum = "1d0dd4be24fcdcfeaa12a432d588dc59bbad6cad3510c67e74a2b6b2fc950564"
dependencies = [
"unicode-ident",
]
@ -1364,6 +1474,15 @@ dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.29"
@ -1440,7 +1559,7 @@ checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.11",
"syn 2.0.13",
]
[[package]]
@ -1572,9 +1691,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.11"
version = "2.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21e3787bb71465627110e7d87ed4faaa36c1f61042ee67badb9e2ef173accc40"
checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
dependencies = [
"proc-macro2",
"quote",
@ -1636,7 +1755,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.11",
"syn 2.0.13",
]
[[package]]
@ -1686,7 +1805,7 @@ checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.11",
"syn 2.0.13",
]
[[package]]
@ -1855,18 +1974,49 @@ dependencies = [
"tracing-core",
]
[[package]]
name = "tracing-opentelemetry"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21ebb87a95ea13271332df069020513ab70bdb5637ca42d6e492dc3bbbad48de"
dependencies = [
"once_cell",
"opentelemetry",
"tracing",
"tracing-core",
"tracing-log",
"tracing-subscriber",
]
[[package]]
name = "tracing-serde"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1"
dependencies = [
"serde",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
"regex",
"serde",
"serde_json",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
"tracing-serde",
]
[[package]]
@ -2085,6 +2235,7 @@ dependencies = [
"glob",
"hyper",
"lazy_static",
"opentelemetry",
"prometheus",
"pulldown-cmark",
"regex",
@ -2097,6 +2248,7 @@ dependencies = [
"tower",
"tower-http",
"tracing",
"tracing-opentelemetry",
"tracing-subscriber",
]

View file

@ -13,6 +13,7 @@ color-eyre = "0.6.1"
glob = "0.3.0"
hyper = { version = "0.14.19", features = ["full"] }
lazy_static = "1.4.0"
opentelemetry = { version = "0.18.0", features = ["metrics"] }
prometheus = { version = "0.13.3", features = ["process"] }
pulldown-cmark = "0.9.2"
regex = "1.7.2"
@ -25,4 +26,5 @@ toml = "0.7.3"
tower = { version = "0.4.12", features = ["full"] }
tower-http = { version = "0.4.0", features = ["full"] }
tracing = "0.1.35"
tracing-subscriber = { version = "0.3.11", features = ["fmt"] }
tracing-opentelemetry = "0.18.0"
tracing-subscriber = { version = "0.3.11", features = ["fmt", "env-filter", "json", "tracing-log"] }

View file

@ -31,13 +31,17 @@ lazy_static! {
pub fn routes(state: &Arc<AppState>) -> Router<Arc<AppState>> {
Router::new()
.route("/", get(index))
.nest("/posts", posts::router())
.nest("/tags", tags::router())
.merge(posts::router())
.merge(tags::router())
.merge(posts::alias_router(state.posts.values()))
.layer(axum::middleware::from_fn(metrics_middleware))
.route("/healthcheck", get(healthcheck))
.route("/metrics", get(metrics))
.nest_service("/static", tower_http::services::ServeDir::new("./static"))
.layer(axum::middleware::from_fn(metrics_middleware))
.route_service(
"/posts/:slug/*path",
tower_http::services::ServeDir::new("./"),
)
.route_service("/static/*path", tower_http::services::ServeDir::new("./"))
}
#[instrument(skip(state))]
@ -127,8 +131,6 @@ mod tests {
posts,
});
super::routes(&state)
.with_state(state)
.into_make_service();
super::routes(&state).with_state(state).into_make_service();
}
}

View file

@ -16,10 +16,11 @@ use crate::{
pub fn router() -> Router<Arc<AppState>> {
Router::new()
.route("/", get(index))
.route("/:slug/", get(view))
.route("/:slug/index.md", get(super::not_found))
.fallback_service(tower_http::services::ServeDir::new("./posts"))
.route("/posts", get(|| async { Redirect::permanent("/") }))
.route("/posts/", get(index))
.route("/posts/:slug", get(redirect))
.route("/posts/:slug/", get(view))
.route("/posts/:slug/index.md", get(super::not_found))
}
pub fn alias_router<'a>(posts: impl IntoIterator<Item = &'a Post>) -> Router<Arc<AppState>> {
@ -79,6 +80,18 @@ pub async fn view(
Ok(Html(res))
}
#[instrument(skip(state))]
pub async fn redirect(
Path(slug): Path<String>,
State(state): State<Arc<AppState>>,
) -> Result<Redirect, WebsiteError> {
if state.posts.contains_key(&slug) {
Ok(Redirect::permanent(&format!("/posts/{slug}/")))
} else {
Err(WebsiteError::NotFound)
}
}
#[cfg(test)]
mod tests {
use chrono::DateTime;

View file

@ -1,15 +1,22 @@
use std::sync::Arc;
use axum::{Router, response::Html, routing::get, extract::{State, Path}};
use axum::{
extract::{Path, State},
response::{Html, Redirect},
routing::get,
Router,
};
use serde_derive::Serialize;
use tracing::instrument;
use crate::{AppState, WebsiteError, post::Post};
use crate::{post::Post, AppState, WebsiteError};
pub fn router() -> Router<Arc<AppState>> {
Router::new()
.route("/", get(index))
.route("/:tag/", get(view))
.route("/tags", get(|| async { Redirect::permanent("/") }))
.route("/tags/", get(index))
.route("/tags/:tag", get(redirect))
.route("/tags/:tag/", get(view))
}
#[derive(Serialize, Debug)]
@ -19,7 +26,6 @@ struct PageContext<'a> {
#[instrument(skip(state))]
pub async fn index(State(state): State<Arc<AppState>>) -> Result<Html<String>, WebsiteError> {
let tags: Vec<_> = state.tags.values().collect();
let ctx = PageContext { title: "Tags" };
@ -33,8 +39,15 @@ pub async fn index(State(state): State<Arc<AppState>>) -> Result<Html<String>, W
}
#[instrument(skip(state))]
pub async fn view(Path(tag): Path<String>, State(state): State<Arc<AppState>>) -> Result<Html<String>, WebsiteError> {
let mut posts: Vec<&Post> = state.posts.values().filter(|p| p.is_published() && p.tags.contains(&tag)).collect();
pub async fn view(
Path(tag): Path<String>,
State(state): State<Arc<AppState>>,
) -> Result<Html<String>, WebsiteError> {
let mut posts: Vec<&Post> = state
.posts
.values()
.filter(|p| p.is_published() && p.tags.contains(&tag))
.collect();
posts.sort_by_key(|p| &p.date);
posts.reverse();
@ -50,4 +63,16 @@ pub async fn view(Path(tag): Path<String>, State(state): State<Arc<AppState>>) -
let res = state.tera.render("tag.html", &c)?;
Ok(Html(res))
}
}
#[instrument(skip(state))]
pub async fn redirect(
Path(slug): Path<String>,
State(state): State<Arc<AppState>>,
) -> Result<Redirect, WebsiteError> {
if state.tags.contains_key(&slug) {
Ok(Redirect::permanent(&format!("/tags/{slug}/")))
} else {
Err(WebsiteError::NotFound)
}
}

View file

@ -1,13 +1,16 @@
use std::{collections::HashMap, sync::Arc};
use std::{collections::HashMap, sync::Arc, time::Duration};
use axum::extract::MatchedPath;
use color_eyre::eyre::{Error, Result};
use hyper::{Body, Request, Response};
use post::Post;
use tag::Tag;
use tera::Tera;
use tower_http::{compression::CompressionLayer, trace::TraceLayer, cors::CorsLayer};
use tracing::log::*;
use tower_http::{compression::CompressionLayer, cors::CorsLayer};
use tracing::{info_span, log::*, Span};
use tracing_subscriber::{prelude::*, EnvFilter};
mod handlers;
mod post;
@ -22,7 +25,8 @@ pub struct AppState {
#[tokio::main]
async fn main() -> Result<()> {
color_eyre::install()?;
tracing_subscriber::fmt::init();
init_tracing();
info!("Starting server...");
let tera = Tera::new("templates/**/*")?;
@ -32,8 +36,12 @@ async fn main() -> Result<()> {
let app = handlers::routes(&state)
.layer(CorsLayer::permissive())
.layer(TraceLayer::new_for_http())
.layer(CompressionLayer::new())
.layer(
tower_http::trace::TraceLayer::new_for_http()
.make_span_with(make_span)
.on_response(on_response),
)
.with_state(state);
info!("Now listening at http://localhost:8180");
@ -45,6 +53,45 @@ async fn main() -> Result<()> {
Ok(())
}
fn init_tracing() {
let filter = EnvFilter::builder()
.with_default_directive("into".parse().unwrap())
.from_env_lossy()
.add_directive("otel=debug".parse().unwrap());
tracing_subscriber::registry()
.with(filter)
.with(tracing_subscriber::fmt::layer())
.init();
}
fn make_span(request: &Request<Body>) -> Span {
let uri = request.uri();
let route = request
.extensions()
.get::<MatchedPath>()
.map(|mp| mp.as_str())
.unwrap_or_default();
let method = request.method().as_str();
let target = uri.path_and_query().map(|p| p.as_str()).unwrap_or_default();
let name = format!("{method} {route}");
use tracing::field::Empty;
info_span!(
"request",
otel.name = %name,
http.route = %route,
http.method = %method,
http.target = %target,
http.status_code = Empty
)
}
fn on_response<B>(response: &Response<B>, _latency: Duration, span: &Span) {
span.record("http.status_code", response.status().as_str());
}
#[derive(Debug)]
pub enum WebsiteError {
NotFound,

View file

@ -112,7 +112,7 @@ pub async fn load_post(slug: &str) -> color_eyre::eyre::Result<Post> {
))
}
#[instrument]
#[instrument(skip(src))]
fn parse_frontmatter(
src: String,
) -> color_eyre::eyre::Result<(Option<TomlFrontMatter>, Option<String>)> {

View file

@ -11,20 +11,22 @@ pub struct Tag {
pub posts: Vec<String>,
}
pub fn get_tags<'a>(posts: impl IntoIterator<Item=&'a Post>) -> HashMap<String,Tag> {
let mut tags: HashMap<String,Tag> = HashMap::new();
pub fn get_tags<'a>(posts: impl IntoIterator<Item = &'a Post>) -> HashMap<String, Tag> {
let mut tags: HashMap<String, Tag> = HashMap::new();
for post in posts.into_iter() {
for key in &post.tags {
if let Some(tag) = tags.get_mut(key) {
tag.posts.push(post.slug.clone());
}
else {
tags.insert(key.clone(), Tag {
slug: key.clone(),
absolute_path: format!("/tags/{key}/"),
posts: vec![post.slug.clone()]
});
} else {
tags.insert(
key.clone(),
Tag {
slug: key.clone(),
absolute_path: format!("/tags/{key}/"),
posts: vec![post.slug.clone()],
},
);
}
}
}